[REF] web: adapt code after jQuery update
Part of task 1896658 Co-authored-by: Aaron Bohy <aab@odoo.com> Co-authored-by: Christophe Matthieu <chm@odoo.com> Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com> Co-authored-by: David Monjoie <dmo@odoo.com> Co-authored-by: Martin Geubelle <mge@odoo.com> Co-authored-by: svs-odoo <svs@odoo.com> Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
This commit is contained in:
committed by
Aaron Bohy
co-authored by
Aaron Bohy
Christophe Matthieu
Mathieu Duckerts-Antoine
David Monjoie
Martin Geubelle
svs-odoo
parent
eeed894fd5
commit
ab56e637b7
@@ -83,18 +83,19 @@ method.
|
|||||||
|
|
||||||
As required by :js:class:`~openerp.web.Widget`'s contract, if
|
As required by :js:class:`~openerp.web.Widget`'s contract, if
|
||||||
:js:func:`~openerp.web.Widget.start()` executes any asynchronous
|
:js:func:`~openerp.web.Widget.start()` executes any asynchronous
|
||||||
code it should return a ``$.Deferred`` so callers know when it's
|
code it should return a ``Promise`` so callers know when it's
|
||||||
ready for interaction.
|
ready for interaction.
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
start: function () {
|
start: function () {
|
||||||
return $.when(
|
return Promise.all([
|
||||||
this._super(),
|
this._super(),
|
||||||
// Simply read the res.widget object this action should display
|
// Simply read the res.widget object this action should display
|
||||||
new instance.web.Model('res.widget').call(
|
new instance.web.Model('res.widget').call(
|
||||||
'read', [[this.widget_id], ['title']])
|
'read', [[this.widget_id], ['title']])
|
||||||
.then(this.proxy('on_widget_loaded'));
|
.then(this.proxy('on_widget_loaded')
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
The client action can then behave exactly as it wishes to within its
|
The client action can then behave exactly as it wishes to within its
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ var Query = Class.extend({
|
|||||||
* Fetches the first record matching the query, or null
|
* Fetches the first record matching the query, or null
|
||||||
*
|
*
|
||||||
* @param {Object} [options] additional options for the rpc() method
|
* @param {Object} [options] additional options for the rpc() method
|
||||||
* @returns {jQuery.Deferred<Object|null>}
|
* @returns {Promise<Object|null>}
|
||||||
*/
|
*/
|
||||||
first: function (options) {
|
first: function (options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -115,7 +115,7 @@ var Query = Class.extend({
|
|||||||
* Fetches all records matching the query
|
* Fetches all records matching the query
|
||||||
*
|
*
|
||||||
* @param {Object} [options] additional options for the rpc() method
|
* @param {Object} [options] additional options for the rpc() method
|
||||||
* @returns {jQuery.Deferred<Array<>>}
|
* @returns {Promise<Array<>>}
|
||||||
*/
|
*/
|
||||||
all: function (options) {
|
all: function (options) {
|
||||||
return this._execute(options);
|
return this._execute(options);
|
||||||
@@ -123,10 +123,10 @@ var Query = Class.extend({
|
|||||||
/**
|
/**
|
||||||
* Fetches the number of records matching the query in the database
|
* Fetches the number of records matching the query in the database
|
||||||
*
|
*
|
||||||
* @returns {jQuery.Deferred<Number>}
|
* @returns {Promise<Number>}
|
||||||
*/
|
*/
|
||||||
count: function () {
|
count: function () {
|
||||||
if (this._count !== undefined) { return $.when(this._count); }
|
if (this._count !== undefined) { return Promise.resolve(this._count); }
|
||||||
return this._model.call(
|
return this._model.call(
|
||||||
'search_count', [this._filter], {
|
'search_count', [this._filter], {
|
||||||
context: this._model.context(this._context)});
|
context: this._model.context(this._context)});
|
||||||
@@ -367,11 +367,12 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* @param {Array} ids identifiers of the records to read
|
* @param {Array} ids identifiers of the records to read
|
||||||
* @param {Array} [fields] fields to read and return, by default all fields are returned
|
* @param {Array} [fields] fields to read and return, by default all fields are returned
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
read_ids: function (ids, fields, options) {
|
read_ids: function (ids, fields, options) {
|
||||||
if (_.isEmpty(ids))
|
if (_.isEmpty(ids)) {
|
||||||
return $.Deferred().resolve([]);
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var method = 'read';
|
var method = 'read';
|
||||||
@@ -405,7 +406,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* @params {Object} [options]
|
* @params {Object} [options]
|
||||||
* @param {Number} [options.offset=0] The index from which selected records should be returned
|
* @param {Number} [options.offset=0] The index from which selected records should be returned
|
||||||
* @param {Number} [options.limit=null] The maximum number of records to return
|
* @param {Number} [options.limit=null] The maximum number of records to return
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
read_slice: function (fields, options) {
|
read_slice: function (fields, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -414,21 +415,23 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
.limit(options.limit || false)
|
.limit(options.limit || false)
|
||||||
.offset(options.offset || 0)
|
.offset(options.offset || 0)
|
||||||
.all();
|
.all();
|
||||||
return this.orderer.add(query).done(function (records) {
|
var prom = this.orderer.add(query);
|
||||||
|
prom.then(function (records) {
|
||||||
self.ids = _(records).pluck('id');
|
self.ids = _(records).pluck('id');
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Reads the current dataset record (from its index)
|
* Reads the current dataset record (from its index)
|
||||||
*
|
*
|
||||||
* @params {Array} [fields] fields to read and return, by default all fields are returned
|
* @params {Array} [fields] fields to read and return, by default all fields are returned
|
||||||
* @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
|
* @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
read_index: function (fields, options) {
|
read_index: function (fields, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
return this.read_ids([this.ids[this.index]], fields, options).then(function (records) {
|
return this.read_ids([this.ids[this.index]], fields, options).then(function (records) {
|
||||||
if (_.isEmpty(records)) { return $.Deferred().reject().promise(); }
|
if (_.isEmpty(records)) { return Promise.reject(); }
|
||||||
return records[0];
|
return records[0];
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -437,7 +440,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
*
|
*
|
||||||
* @param {Array} [fields] fields to get default values for, by default all defaults are read
|
* @param {Array} [fields] fields to get default values for, by default all defaults are read
|
||||||
* @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
|
* @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
default_get: function (fields, options) {
|
default_get: function (fields, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
@@ -451,15 +454,17 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* @param {Object} options Dictionary that can contain the following keys:
|
* @param {Object} options Dictionary that can contain the following keys:
|
||||||
* - readonly_fields: Values from readonly fields that were updated by
|
* - readonly_fields: Values from readonly fields that were updated by
|
||||||
* on_changes. Only used by the BufferedDataSet to make the o2m work correctly.
|
* on_changes. Only used by the BufferedDataSet to make the o2m work correctly.
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
create: function (data, options) {
|
create: function (data, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this._model.call('create', [data], {
|
var prom = this._model.call('create', [data], {
|
||||||
context: this.get_context()
|
context: this.get_context()
|
||||||
}).done(function () {
|
});
|
||||||
|
prom.then(function () {
|
||||||
self.trigger('dataset_changed', data, options);
|
self.trigger('dataset_changed', data, options);
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Saves the provided data in an existing db record
|
* Saves the provided data in an existing db record
|
||||||
@@ -470,16 +475,18 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* - context: The context to use in the server-side call.
|
* - context: The context to use in the server-side call.
|
||||||
* - readonly_fields: Values from readonly fields that were updated by
|
* - readonly_fields: Values from readonly fields that were updated by
|
||||||
* on_changes. Only used by the BufferedDataSet to make the o2m work correctly.
|
* on_changes. Only used by the BufferedDataSet to make the o2m work correctly.
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
write: function (id, data, options) {
|
write: function (id, data, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var self = this;
|
var self = this;
|
||||||
return this._model.call('write', [[id], data], {
|
var prom = this._model.call('write', [[id], data], {
|
||||||
context: this.get_context(options.context)
|
context: this.get_context(options.context)
|
||||||
}).done(function () {
|
});
|
||||||
|
prom.then(function () {
|
||||||
self.trigger('dataset_changed', id, data, options);
|
self.trigger('dataset_changed', id, data, options);
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Deletes an existing record from the database
|
* Deletes an existing record from the database
|
||||||
@@ -488,11 +495,13 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
*/
|
*/
|
||||||
unlink: function (ids) {
|
unlink: function (ids) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this._model.call('unlink', [ids], {
|
var prom = this._model.call('unlink', [ids], {
|
||||||
context: this.get_context()
|
context: this.get_context()
|
||||||
}).done(function () {
|
});
|
||||||
|
prom.then(function () {
|
||||||
self.trigger('dataset_changed', ids);
|
self.trigger('dataset_changed', ids);
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Calls an arbitrary RPC method
|
* Calls an arbitrary RPC method
|
||||||
@@ -501,7 +510,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* @param {Array} [args] arguments to pass to the method
|
* @param {Array} [args] arguments to pass to the method
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
* @param {Function} error_callback
|
* @param {Function} error_callback
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
call: function (method, args) {
|
call: function (method, args) {
|
||||||
return this._model.call(method, args);
|
return this._model.call(method, args);
|
||||||
@@ -511,7 +520,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
*
|
*
|
||||||
* @param {String} method
|
* @param {String} method
|
||||||
* @param {Array} [args]
|
* @param {Array} [args]
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
call_button: function (method, args) {
|
call_button: function (method, args) {
|
||||||
return this._model.call_button(method, args);
|
return this._model.call_button(method, args);
|
||||||
@@ -520,7 +529,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* Fetches the "readable name" for records, based on intrinsic rules
|
* Fetches the "readable name" for records, based on intrinsic rules
|
||||||
*
|
*
|
||||||
* @param {Array} ids
|
* @param {Array} ids
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
name_get: function (ids) {
|
name_get: function (ids) {
|
||||||
return this._model.call('name_get', [ids], {context: this.get_context()});
|
return this._model.call('name_get', [ids], {context: this.get_context()});
|
||||||
@@ -532,7 +541,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* @param {String} [operator='ilike'] matching operator to use with the provided name value
|
* @param {String} [operator='ilike'] matching operator to use with the provided name value
|
||||||
* @param {Number} [limit=0] maximum number of matches to return
|
* @param {Number} [limit=0] maximum number of matches to return
|
||||||
* @param {Function} callback function to call with name_search result
|
* @param {Function} callback function to call with name_search result
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
name_search: function (name, domain, operator, limit) {
|
name_search: function (name, domain, operator, limit) {
|
||||||
return this._model.call('name_search', {
|
return this._model.call('name_search', {
|
||||||
@@ -603,7 +612,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
|
|||||||
* Resequence records.
|
* Resequence records.
|
||||||
*
|
*
|
||||||
* @param {Array} ids identifiers of the records to resequence
|
* @param {Array} ids identifiers of the records to resequence
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
resequence: function (ids, options) {
|
resequence: function (ids, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
@@ -643,7 +652,7 @@ var DataSetStatic = DataSet.extend({
|
|||||||
unlink: function (ids) {
|
unlink: function (ids) {
|
||||||
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
|
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
|
||||||
this.trigger('unlink', ids);
|
this.trigger('unlink', ids);
|
||||||
return $.Deferred().resolve({result: true});
|
return Promise.resolve({result: true});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -674,7 +683,7 @@ var DataSetSearch = DataSet.extend({
|
|||||||
* @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain
|
* @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain
|
||||||
* @param {Number} [options.offset=0] The index from which selected records should be returned
|
* @param {Number} [options.offset=0] The index from which selected records should be returned
|
||||||
* @param {Number} [options.limit=null] The maximum number of records to return
|
* @param {Number} [options.limit=null] The maximum number of records to return
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
read_slice: function (fields, options) {
|
read_slice: function (fields, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
@@ -686,11 +695,13 @@ var DataSetSearch = DataSet.extend({
|
|||||||
.limit(options.limit || false);
|
.limit(options.limit || false);
|
||||||
q = q.order_by.apply(q, this._sort);
|
q = q.order_by.apply(q, this._sort);
|
||||||
|
|
||||||
return this.orderer.add(q.all()).done(function (records) {
|
var prom = this.orderer.add(q.all());
|
||||||
|
prom.then(function (records) {
|
||||||
// FIXME: not sure about that one, *could* have discarded count
|
// FIXME: not sure about that one, *could* have discarded count
|
||||||
q.count().done(function (count) { self._length = count; });
|
q.count().then(function (count) { self._length = count; });
|
||||||
self.ids = _(records).pluck('id');
|
self.ids = _(records).pluck('id');
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
get_domain: function (other_domain) {
|
get_domain: function (other_domain) {
|
||||||
return this._model.domain(other_domain);
|
return this._model.domain(other_domain);
|
||||||
@@ -717,10 +728,12 @@ var DataSetSearch = DataSet.extend({
|
|||||||
},
|
},
|
||||||
unlink: function (ids, callback, error_callback) {
|
unlink: function (ids, callback, error_callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this._super(ids).done(function () {
|
var prom = this._super(ids);
|
||||||
|
prom.then(function () {
|
||||||
self.remove_ids( ids);
|
self.remove_ids( ids);
|
||||||
self.trigger("dataset_changed", ids, callback, error_callback);
|
self.trigger("dataset_changed", ids, callback, error_callback);
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
size: function () {
|
size: function () {
|
||||||
if (this._length !== null) {
|
if (this._length !== null) {
|
||||||
@@ -801,7 +814,7 @@ var Model = Class.extend({
|
|||||||
* @param {Array} [args] positipyEvalonal arguments
|
* @param {Array} [args] positipyEvalonal arguments
|
||||||
* @param {Object} [kwargs] keyword arguments
|
* @param {Object} [kwargs] keyword arguments
|
||||||
* @param {Object} [options] additional options for the rpc() method
|
* @param {Object} [options] additional options for the rpc() method
|
||||||
* @returns {jQuery.Deferred<>} call result
|
* @returns {Promise<>} call result
|
||||||
*/
|
*/
|
||||||
call: function (method, args, kwargs, options) {
|
call: function (method, args, kwargs, options) {
|
||||||
args = args || [];
|
args = args || [];
|
||||||
|
|||||||
@@ -22,20 +22,21 @@ var Apps = AbstractAction.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
get_client: function() {
|
get_client: function() {
|
||||||
// return the client via a deferred, resolved or rejected depending if
|
// return the client via a promise, resolved or rejected depending if
|
||||||
// the remote host is available or not.
|
// the remote host is available or not.
|
||||||
var check_client_available = function(client) {
|
var check_client_available = function(client) {
|
||||||
var d = $.Deferred();
|
|
||||||
var i = new Image();
|
var i = new Image();
|
||||||
i.onerror = function() {
|
var def = new Promise(function (resolve, reject) {
|
||||||
d.reject(client);
|
i.onerror = function() {
|
||||||
};
|
reject(client);
|
||||||
i.onload = function() {
|
};
|
||||||
d.resolve(client);
|
i.onload = function() {
|
||||||
};
|
resolve(client);
|
||||||
|
};
|
||||||
|
});
|
||||||
var ts = new Date().getTime();
|
var ts = new Date().getTime();
|
||||||
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif?%s', client.origin, ts);
|
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif?%s', client.origin, ts);
|
||||||
return d.promise();
|
return def;
|
||||||
};
|
};
|
||||||
if (apps_client) {
|
if (apps_client) {
|
||||||
return check_client_available(apps_client);
|
return check_client_available(apps_client);
|
||||||
@@ -105,58 +106,56 @@ var Apps = AbstractAction.extend({
|
|||||||
|
|
||||||
start: function() {
|
start: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def = $.Deferred();
|
return new Promise(function (resolve, reject) {
|
||||||
self.get_client().then(function(client) {
|
self.get_client().then(function (client) {
|
||||||
self.client = client;
|
self.client = client;
|
||||||
|
|
||||||
var qs = {db: client.dbname};
|
var qs = {db: client.dbname};
|
||||||
if (session.debug) {
|
if (session.debug) {
|
||||||
qs.debug = session.debug;
|
qs.debug = session.debug;
|
||||||
}
|
}
|
||||||
var u = $.param.querystring(client.origin + "/apps/embed/client", qs);
|
var u = $.param.querystring(client.origin + "/apps/embed/client", qs);
|
||||||
var css = {width: '100%', height: '750px'};
|
var css = {width: '100%', height: '750px'};
|
||||||
self.$ifr = $('<iframe>').attr('src', u);
|
self.$ifr = $('<iframe>').attr('src', u);
|
||||||
|
|
||||||
self.uniq = _.uniqueId('apps');
|
self.uniq = _.uniqueId('apps');
|
||||||
$(window).on("message." + self.uniq, self.proxy('_on_message'));
|
$(window).on("message." + self.uniq, self.proxy('_on_message'));
|
||||||
|
|
||||||
self.on('message:ready', self, function(m) {
|
self.on('message:ready', self, function(m) {
|
||||||
var w = this.$ifr[0].contentWindow;
|
var w = this.$ifr[0].contentWindow;
|
||||||
var act = {
|
var act = {
|
||||||
type: 'ir.actions.client',
|
type: 'ir.actions.client',
|
||||||
tag: this.remote_action_tag,
|
tag: this.remote_action_tag,
|
||||||
params: _.extend({}, this.params, {
|
params: _.extend({}, this.params, {
|
||||||
db: session.db,
|
db: session.db,
|
||||||
origin: session.origin,
|
origin: session.origin,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
w.postMessage({type:'action', action: act}, client.origin);
|
w.postMessage({type:'action', action: act}, client.origin);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.on('message:set_height', self, function(m) {
|
self.on('message:set_height', self, function(m) {
|
||||||
this.$ifr.height(m.height);
|
this.$ifr.height(m.height);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.on('message:blockUI', self, function() { framework.blockUI(); });
|
self.on('message:blockUI', self, function() { framework.blockUI(); });
|
||||||
self.on('message:unblockUI', self, function() { framework.unblockUI(); });
|
self.on('message:unblockUI', self, function() { framework.unblockUI(); });
|
||||||
self.on('message:warn', self, function(m) {self.do_warn(m.title, m.message, m.sticky); });
|
self.on('message:warn', self, function(m) {self.do_warn(m.title, m.message, m.sticky); });
|
||||||
|
|
||||||
self.$ifr.appendTo(self.$('.o_content')).css(css).addClass('apps-client');
|
self.$ifr.appendTo(self.$('.o_content')).css(css).addClass('apps-client');
|
||||||
|
|
||||||
def.resolve();
|
resolve();
|
||||||
}, function() {
|
}, function() {
|
||||||
self.do_warn(_t('Odoo Apps will be available soon'), _t('Showing locally available modules'), true);
|
self.do_warn(_t('Odoo Apps will be available soon'), _t('Showing locally available modules'), true);
|
||||||
return self._rpc({
|
return self._rpc({
|
||||||
route: '/web/action/load',
|
route: '/web/action/load',
|
||||||
params: {action_id: self.failback_action_id},
|
params: {action_id: self.failback_action_id},
|
||||||
}).then(function(action) {
|
}).then(function(action) {
|
||||||
return self.do_action(action);
|
return self.do_action(action);
|
||||||
}).always(function () {
|
}).then(reject, reject);
|
||||||
def.reject();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return def;
|
}
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var AppsUpdates = Apps.extend({
|
var AppsUpdates = Apps.extend({
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
* @name openerp
|
* @name openerp
|
||||||
* @namespace openerp
|
* @namespace openerp
|
||||||
*
|
*
|
||||||
* Each module can return a deferred. In that case, the module is marked as loaded
|
* Each module can return a promise. In that case, the module is marked as loaded
|
||||||
* only when the deferred is resolved, and its value is equal to the resolved value.
|
* only when the promise is resolved, and its value is equal to the resolved value.
|
||||||
* The module can be rejected (unloaded). This will be logged in the console as info.
|
* The module can be rejected (unloaded). This will be logged in the console as info.
|
||||||
*
|
*
|
||||||
* logs:
|
* logs:
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* Failed modules:
|
* Failed modules:
|
||||||
* A javascript error is detected
|
* A javascript error is detected
|
||||||
* Rejected modules:
|
* Rejected modules:
|
||||||
* The module returns a rejected deferred. It (and its dependent modules)
|
* The module returns a rejected promise. It (and its dependent modules)
|
||||||
* is not loaded.
|
* is not loaded.
|
||||||
* Rejected linked modules:
|
* Rejected linked modules:
|
||||||
* Modules who depend on a rejected module
|
* Modules who depend on a rejected module
|
||||||
@@ -27,14 +27,14 @@
|
|||||||
* Non loaded or failed module informations for debugging
|
* Non loaded or failed module informations for debugging
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var jobs = [];
|
var jobs = [];
|
||||||
var factories = Object.create(null);
|
var factories = Object.create(null);
|
||||||
var job_names = [];
|
var job_names = [];
|
||||||
var job_deps = [];
|
var job_deps = [];
|
||||||
var job_deferred = [];
|
var job_promise = [];
|
||||||
|
|
||||||
|
|
||||||
var services = Object.create({});
|
var services = Object.create({});
|
||||||
@@ -45,15 +45,20 @@
|
|||||||
var debug = ($.deparam($.param.querystring()).debug !== undefined);
|
var debug = ($.deparam($.param.querystring()).debug !== undefined);
|
||||||
|
|
||||||
var odoo = window.odoo = window.odoo || {};
|
var odoo = window.odoo = window.odoo || {};
|
||||||
|
var didLogInfoResolve;
|
||||||
|
var didLogInfoPromise = new Promise(function (resolve) {
|
||||||
|
didLogInfoResolve = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
_.extend(odoo, {
|
_.extend(odoo, {
|
||||||
testing: typeof QUnit === "object",
|
testing: typeof QUnit === "object",
|
||||||
debug: debug,
|
debug: debug,
|
||||||
remaining_jobs: jobs,
|
remaining_jobs: jobs,
|
||||||
|
|
||||||
__DEBUG__: {
|
__DEBUG__: {
|
||||||
didLogInfo: $.Deferred(),
|
didLogInfo: didLogInfoPromise,
|
||||||
get_dependencies: function (name, transitive) {
|
get_dependencies: function (name, transitive) {
|
||||||
var deps = name instanceof Array ? name: [name],
|
var deps = name instanceof Array ? name : [name],
|
||||||
changed;
|
changed;
|
||||||
do {
|
do {
|
||||||
changed = false;
|
changed = false;
|
||||||
@@ -67,26 +72,26 @@
|
|||||||
return deps;
|
return deps;
|
||||||
},
|
},
|
||||||
get_dependents: function (name) {
|
get_dependents: function (name) {
|
||||||
return _.pluck(_.where(job_deps, {from: name}), 'to');
|
return _.pluck(_.where(job_deps, { from: name }), 'to');
|
||||||
},
|
},
|
||||||
get_waited_jobs: function () {
|
get_waited_jobs: function () {
|
||||||
return _.uniq(_.map(jobs, function(job) {return job.name;}));
|
return _.uniq(_.map(jobs, function (job) { return job.name; }));
|
||||||
},
|
},
|
||||||
get_missing_jobs: function () {
|
get_missing_jobs: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var waited = this.get_waited_jobs();
|
var waited = this.get_waited_jobs();
|
||||||
var missing = [];
|
var missing = [];
|
||||||
_.each(waited, function(job) {
|
_.each(waited, function (job) {
|
||||||
_.each(self.get_dependencies(job), function(job) {
|
_.each(self.get_dependencies(job), function (job) {
|
||||||
if (!(job in self.services)) {
|
if (!(job in self.services)) {
|
||||||
missing.push(job);
|
missing.push(job);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return _.filter(_.difference(_.uniq(missing), waited), function (job) {return !job.error;});
|
return _.filter(_.difference(_.uniq(missing), waited), function (job) { return !job.error; });
|
||||||
},
|
},
|
||||||
get_failed_jobs: function () {
|
get_failed_jobs: function () {
|
||||||
return _.filter(jobs, function (job) {return !!job.error;});
|
return _.filter(jobs, function (job) { return !!job.error; });
|
||||||
},
|
},
|
||||||
factories: factories,
|
factories: factories,
|
||||||
services: services,
|
services: services,
|
||||||
@@ -109,10 +114,10 @@
|
|||||||
|
|
||||||
if (odoo.debug) {
|
if (odoo.debug) {
|
||||||
if (!(deps instanceof Array)) {
|
if (!(deps instanceof Array)) {
|
||||||
throw new Error ('Dependencies should be defined by an array', deps);
|
throw new Error('Dependencies should be defined by an array', deps);
|
||||||
}
|
}
|
||||||
if (typeof factory !== 'function') {
|
if (typeof factory !== 'function') {
|
||||||
throw new Error ('Factory should be defined by a function', factory);
|
throw new Error('Factory should be defined by a function', factory);
|
||||||
}
|
}
|
||||||
if (typeof name !== 'string') {
|
if (typeof name !== 'string') {
|
||||||
throw new Error("Invalid name definition (should be a string", name);
|
throw new Error("Invalid name definition (should be a string", name);
|
||||||
@@ -133,7 +138,7 @@
|
|||||||
|
|
||||||
job_names.push(name);
|
job_names.push(name);
|
||||||
_.each(deps, function (dep) {
|
_.each(deps, function (dep) {
|
||||||
job_deps.push({from:dep, to:name});
|
job_deps.push({ from: dep, to: name });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.process_jobs(jobs, services);
|
this.process_jobs(jobs, services);
|
||||||
@@ -149,7 +154,7 @@
|
|||||||
var job;
|
var job;
|
||||||
var jobdep;
|
var jobdep;
|
||||||
|
|
||||||
for (var k=0; k<jobs.length; k++) {
|
for (var k = 0; k < jobs.length; k++) {
|
||||||
debug_jobs[jobs[k].name] = job = {
|
debug_jobs[jobs[k].name] = job = {
|
||||||
dependencies: jobs[k].deps,
|
dependencies: jobs[k].deps,
|
||||||
dependents: odoo.__DEBUG__.get_dependents(jobs[k].name),
|
dependents: odoo.__DEBUG__.get_dependents(jobs[k].name),
|
||||||
@@ -162,10 +167,10 @@
|
|||||||
job.rejected = jobs[k].rejected;
|
job.rejected = jobs[k].rejected;
|
||||||
rejected.push(job.name);
|
rejected.push(job.name);
|
||||||
}
|
}
|
||||||
var deps = odoo.__DEBUG__.get_dependencies( job.name );
|
var deps = odoo.__DEBUG__.get_dependencies(job.name);
|
||||||
for (var i=0; i<deps.length; i++) {
|
for (var i = 0; i < deps.length; i++) {
|
||||||
if (job.name !== deps[i] && !(deps[i] in services)) {
|
if (job.name !== deps[i] && !(deps[i] in services)) {
|
||||||
jobdep = debug_jobs[deps[i]] || (deps[i] in factories && _.find(jobs, function (job) { return job.name === deps[i];}));
|
jobdep = debug_jobs[deps[i]] || (deps[i] in factories && _.find(jobs, function (job) { return job.name === deps[i]; }));
|
||||||
if (jobdep && jobdep.rejected) {
|
if (jobdep && jobdep.rejected) {
|
||||||
if (!job.rejected) {
|
if (!job.rejected) {
|
||||||
job.rejected = [];
|
job.rejected = [];
|
||||||
@@ -212,48 +217,49 @@
|
|||||||
missing: missing,
|
missing: missing,
|
||||||
failed: _.pluck(failed, 'name'),
|
failed: _.pluck(failed, 'name'),
|
||||||
};
|
};
|
||||||
odoo.__DEBUG__.didLogInfo.resolve();
|
didLogInfoResolve();
|
||||||
},
|
},
|
||||||
process_jobs: function (jobs, services) {
|
process_jobs: function (jobs, services) {
|
||||||
var job;
|
var job;
|
||||||
|
|
||||||
function process_job (job) {
|
function process_job(job) {
|
||||||
var require = make_require(job);
|
var require = make_require(job);
|
||||||
|
|
||||||
var job_exec;
|
var job_exec;
|
||||||
var def = $.Deferred();
|
var def = new Promise(function (resolve) {
|
||||||
try {
|
try {
|
||||||
job_exec = job.factory.call(null, require);
|
job_exec = job.factory.call(null, require);
|
||||||
jobs.splice(jobs.indexOf(job), 1);
|
jobs.splice(jobs.indexOf(job), 1);
|
||||||
job_deferred.push(def);
|
} catch (e) {
|
||||||
} catch (e) {
|
job.error = e;
|
||||||
job.error = e;
|
console.error('Error while loading ' + job.name);
|
||||||
console.error('Error while loading ' + job.name);
|
console.error(e.stack);
|
||||||
console.error(e.stack);
|
}
|
||||||
}
|
if (!job.error) {
|
||||||
if (!job.error) {
|
Promise.resolve(job_exec).then(
|
||||||
$.when(job_exec).then(
|
function (data) {
|
||||||
function (data) {
|
services[job.name] = data;
|
||||||
services[job.name] = data;
|
resolve();
|
||||||
def.resolve();
|
odoo.process_jobs(jobs, services);
|
||||||
odoo.process_jobs(jobs, services);
|
}).guardedCatch(function (e) {
|
||||||
}, function (e) {
|
job.rejected = e || true;
|
||||||
job.rejected = e || true;
|
jobs.push(job);
|
||||||
jobs.push(job);
|
resolve();
|
||||||
def.resolve();
|
}
|
||||||
}
|
);
|
||||||
);
|
}
|
||||||
}
|
});
|
||||||
|
job_promise.push(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_ready (job) {
|
function is_ready(job) {
|
||||||
return !job.error && !job.rejected && _.every(job.factory.deps, function (name) { return name in services; });
|
return !job.error && !job.rejected && _.every(job.factory.deps, function (name) { return name in services; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_require (job) {
|
function make_require(job) {
|
||||||
var deps = _.pick(services, job.deps);
|
var deps = _.pick(services, job.deps);
|
||||||
|
|
||||||
function require (name) {
|
function require(name) {
|
||||||
if (!(name in deps)) {
|
if (!(name in deps)) {
|
||||||
console.error('Undefined dependency: ', name);
|
console.error('Undefined dependency: ', name);
|
||||||
} else {
|
} else {
|
||||||
@@ -278,9 +284,9 @@
|
|||||||
// automatically log errors detected when loading modules
|
// automatically log errors detected when loading modules
|
||||||
var log_when_loaded = function () {
|
var log_when_loaded = function () {
|
||||||
_.delay(function () {
|
_.delay(function () {
|
||||||
var len = job_deferred.length;
|
var len = job_promise.length;
|
||||||
$.when.apply($, job_deferred).then(function () {
|
Promise.all(job_promise).then(function () {
|
||||||
if (len === job_deferred.length) {
|
if (len === job_promise.length) {
|
||||||
odoo.log();
|
odoo.log();
|
||||||
} else {
|
} else {
|
||||||
log_when_loaded();
|
log_when_loaded();
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ var AbstractAction = Widget.extend(ActionMixin, {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when(def).then(function () {
|
return Promise.resolve(def).then(function () {
|
||||||
var controlPanelView = new self.config.ControlPanelView(params);
|
var controlPanelView = new self.config.ControlPanelView(params);
|
||||||
return controlPanelView.getController(self).then(function (controlPanel) {
|
return controlPanelView.getController(self).then(function (controlPanel) {
|
||||||
self._controlPanel = controlPanel;
|
self._controlPanel = controlPanel;
|
||||||
@@ -112,7 +112,7 @@ var AbstractAction = Widget.extend(ActionMixin, {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
|||||||
if (event.data.on_success) {
|
if (event.data.on_success) {
|
||||||
event.data.on_success(result);
|
event.data.on_success(result);
|
||||||
}
|
}
|
||||||
}).fail(function (result) {
|
}).guardedCatch(function (result) {
|
||||||
if (event.data.on_fail) {
|
if (event.data.on_fail) {
|
||||||
event.data.on_fail(result);
|
event.data.on_fail(result);
|
||||||
}
|
}
|
||||||
@@ -116,17 +116,17 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
|||||||
.then(function () {
|
.then(function () {
|
||||||
self.$el.toggleClass('o_rtl', _t.database.parameters.direction === "rtl");
|
self.$el.toggleClass('o_rtl', _t.database.parameters.direction === "rtl");
|
||||||
self.bind_events();
|
self.bind_events();
|
||||||
return $.when(
|
return Promise.all([
|
||||||
self.set_action_manager(),
|
self.set_action_manager(),
|
||||||
self.set_loading()
|
self.set_loading()
|
||||||
);
|
]);
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
if (session.session_is_valid()) {
|
if (session.session_is_valid()) {
|
||||||
return self.show_application();
|
return self.show_application();
|
||||||
} else {
|
} else {
|
||||||
// database manager needs the webclient to keep going even
|
// database manager needs the webclient to keep going even
|
||||||
// though it has no valid session
|
// though it has no valid session
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
// Listen to 'scroll' event and propagate it on main bus
|
// Listen to 'scroll' event and propagate it on main bus
|
||||||
@@ -145,12 +145,13 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
|||||||
this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function () {
|
this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function () {
|
||||||
$(this).attr('data-toggle', 'tooltip').tooltip().trigger('mouseenter');
|
$(this).attr('data-toggle', 'tooltip').tooltip().trigger('mouseenter');
|
||||||
});
|
});
|
||||||
|
// TODO: this handler seems useless since 11.0, should be removed
|
||||||
this.$el.on('click', '.oe_dropdown_toggle', function (ev) {
|
this.$el.on('click', '.oe_dropdown_toggle', function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
var $toggle = $(this);
|
var $toggle = $(this);
|
||||||
var doc_width = $(document).width();
|
var doc_width = $(document).width();
|
||||||
var $menu = $toggle.siblings('.oe_dropdown_menu');
|
var $menu = $toggle.siblings('.oe_dropdown_menu');
|
||||||
$menu = $menu.size() >= 1 ? $menu : $toggle.find('.oe_dropdown_menu');
|
$menu = $menu.length >= 1 ? $menu : $toggle.find('.oe_dropdown_menu');
|
||||||
var state = $menu.is('.oe_opened');
|
var state = $menu.is('.oe_opened');
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// Do not alter propagation
|
// Do not alter propagation
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ var ActionManager = Widget.extend({
|
|||||||
* will be restored. It ensures that the current controller can be left (for
|
* will be restored. It ensures that the current controller can be left (for
|
||||||
* instance, that it has no unsaved changes).
|
* instance, that it has no unsaved changes).
|
||||||
*
|
*
|
||||||
* @returns {Deferred} resolved if the current controller can be left,
|
* @returns {Promise} resolved if the current controller can be left,
|
||||||
* rejected otherwise.
|
* rejected otherwise.
|
||||||
*/
|
*/
|
||||||
clearUncommittedChanges: function () {
|
clearUncommittedChanges: function () {
|
||||||
@@ -100,7 +100,7 @@ var ActionManager = Widget.extend({
|
|||||||
if (currentController) {
|
if (currentController) {
|
||||||
return currentController.widget.canBeRemoved();
|
return currentController.widget.canBeRemoved();
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* This is the entry point to execute Odoo actions, given as an ID in
|
* This is the entry point to execute Odoo actions, given as an ID in
|
||||||
@@ -123,7 +123,7 @@ var ActionManager = Widget.extend({
|
|||||||
* is useful when we come from a loadState())
|
* is useful when we come from a loadState())
|
||||||
* @param {boolean} [options.replace_last_action=false] set to true to
|
* @param {boolean} [options.replace_last_action=false] set to true to
|
||||||
* replace last part of the breadcrumbs with the action
|
* replace last part of the breadcrumbs with the action
|
||||||
* @return {$.Deferred<Object>} resolved with the action when the action is
|
* @return {Promise<Object>} resolved with the action when the action is
|
||||||
* loaded and appended to the DOM ; rejected if the action can't be
|
* loaded and appended to the DOM ; rejected if the action can't be
|
||||||
* executed (e.g. if doAction has been called to execute another action
|
* executed (e.g. if doAction has been called to execute another action
|
||||||
* before this one was complete).
|
* before this one was complete).
|
||||||
@@ -155,7 +155,7 @@ var ActionManager = Widget.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.dp.add($.when(def)).then(function () {
|
return this.dp.add(Promise.resolve(def)).then(function () {
|
||||||
// action.target 'main' is equivalent to 'current' except that it
|
// action.target 'main' is equivalent to 'current' except that it
|
||||||
// also clears the breadcrumbs
|
// also clears the breadcrumbs
|
||||||
options.clear_breadcrumbs = action.target === 'main' ||
|
options.clear_breadcrumbs = action.target === 'main' ||
|
||||||
@@ -211,12 +211,12 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {Object} state
|
* @param {Object} state
|
||||||
* @param {integer|string} [state.action] the action to execute (given its
|
* @param {integer|string} [state.action] the action to execute (given its
|
||||||
* id or tag for client actions)
|
* id or tag for client actions)
|
||||||
* @returns {Deferred} resolved when the UI has been updated
|
* @returns {Promise} resolved when the UI has been updated
|
||||||
*/
|
*/
|
||||||
loadState: function (state) {
|
loadState: function (state) {
|
||||||
var action;
|
var action;
|
||||||
if (!state.action) {
|
if (!state.action) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
if (_.isString(state.action) && core.action_registry.contains(state.action)) {
|
if (_.isString(state.action) && core.action_registry.contains(state.action)) {
|
||||||
action = {
|
action = {
|
||||||
@@ -299,7 +299,7 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {string} [action.target="current"] set to "new" to render the
|
* @param {string} [action.target="current"] set to "new" to render the
|
||||||
* controller in a dialog
|
* controller in a dialog
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the controller is started and appended
|
* @returns {Promise} resolved when the controller is started and appended
|
||||||
*/
|
*/
|
||||||
_executeAction: function (action, options) {
|
_executeAction: function (action, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -338,7 +338,7 @@ var ActionManager = Widget.extend({
|
|||||||
|
|
||||||
return action;
|
return action;
|
||||||
})
|
})
|
||||||
.fail(function () {
|
.guardedCatch(function () {
|
||||||
self._removeAction(action.jsID);
|
self._removeAction(action.jsID);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -349,7 +349,7 @@ var ActionManager = Widget.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} action
|
* @param {Object} action
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the controller is rendered inside a
|
* @returns {Promise} resolved when the controller is rendered inside a
|
||||||
* dialog appended to the DOM
|
* dialog appended to the DOM
|
||||||
*/
|
*/
|
||||||
_executeActionInDialog: function (action, options) {
|
_executeActionInDialog: function (action, options) {
|
||||||
@@ -401,7 +401,7 @@ var ActionManager = Widget.extend({
|
|||||||
|
|
||||||
return action;
|
return action;
|
||||||
});
|
});
|
||||||
}).fail(function () {
|
}).guardedCatch(function () {
|
||||||
self._removeAction(action.jsID);
|
self._removeAction(action.jsID);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -412,14 +412,14 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {Object} action the description of the action to execute
|
* @param {Object} action the description of the action to execute
|
||||||
* @param {string} action.tag the key of the action in the action_registry
|
* @param {string} action.tag the key of the action in the action_registry
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the client action has been executed
|
* @returns {Promise} resolved when the client action has been executed
|
||||||
*/
|
*/
|
||||||
_executeClientAction: function (action, options) {
|
_executeClientAction: function (action, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var ClientAction = core.action_registry.get(action.tag);
|
var ClientAction = core.action_registry.get(action.tag);
|
||||||
if (!ClientAction) {
|
if (!ClientAction) {
|
||||||
console.error("Could not find client action " + action.tag, action);
|
console.error("Could not find client action " + action.tag, action);
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
if (!(ClientAction.prototype instanceof AbstractAction)) {
|
if (!(ClientAction.prototype instanceof AbstractAction)) {
|
||||||
console.warn('The client action ' + action.tag + ' should be an instance of AbstractAction!');
|
console.warn('The client action ' + action.tag + ' should be an instance of AbstractAction!');
|
||||||
@@ -431,7 +431,7 @@ var ActionManager = Widget.extend({
|
|||||||
if (next) {
|
if (next) {
|
||||||
return this.doAction(next, options);
|
return this.doAction(next, options);
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
var controllerID = _.uniqueId('controller_');
|
var controllerID = _.uniqueId('controller_');
|
||||||
@@ -449,12 +449,14 @@ var ActionManager = Widget.extend({
|
|||||||
};
|
};
|
||||||
this.controllers[controllerID] = controller;
|
this.controllers[controllerID] = controller;
|
||||||
action.controllerID = controllerID;
|
action.controllerID = controllerID;
|
||||||
return this._executeAction(action, options).done(function () {
|
var prom = this._executeAction(action, options);
|
||||||
|
prom.then(function () {
|
||||||
// AAB: this should be done automatically in AbstractAction, so that
|
// AAB: this should be done automatically in AbstractAction, so that
|
||||||
// it can be overriden by actions that have specific stuff to push
|
// it can be overriden by actions that have specific stuff to push
|
||||||
// (e.g. Discuss, Views)
|
// (e.g. Discuss, Views)
|
||||||
self._pushState(controllerID, {});
|
self._pushState(controllerID, {});
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Executes actions of type 'ir.actions.act_window_close', i.e. closes the
|
* Executes actions of type 'ir.actions.act_window_close', i.e. closes the
|
||||||
@@ -469,7 +471,7 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {Object} [action.effect] effect to show up, e.g. rainbow man.
|
* @param {Object} [action.effect] effect to show up, e.g. rainbow man.
|
||||||
* @param {Object} [action.infos] infos on performing the close action.
|
* @param {Object} [action.infos] infos on performing the close action.
|
||||||
* Useful for providing some context for the `on_close` handler.
|
* Useful for providing some context for the `on_close` handler.
|
||||||
* @returns {Deferred} resolved immediately
|
* @returns {Promise} resolved immediately
|
||||||
*/
|
*/
|
||||||
_executeCloseAction: function (action, options) {
|
_executeCloseAction: function (action, options) {
|
||||||
var result;
|
var result;
|
||||||
@@ -484,7 +486,7 @@ var ActionManager = Widget.extend({
|
|||||||
this.trigger_up('show_effect', action.effect);
|
this.trigger_up('show_effect', action.effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.when(result);
|
return Promise.resolve(result);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Executes actions of type 'ir.actions.server'.
|
* Executes actions of type 'ir.actions.server'.
|
||||||
@@ -494,7 +496,7 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {integer} action.id the db ID of the action to execute
|
* @param {integer} action.id the db ID of the action to execute
|
||||||
* @param {Object} [action.context]
|
* @param {Object} [action.context]
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the action has been executed
|
* @returns {Promise} resolved when the action has been executed
|
||||||
*/
|
*/
|
||||||
_executeServerAction: function (action, options) {
|
_executeServerAction: function (action, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -520,7 +522,7 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {string} [action.target] set to 'self' to redirect in the current page,
|
* @param {string} [action.target] set to 'self' to redirect in the current page,
|
||||||
* redirects to a new page by default
|
* redirects to a new page by default
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the redirection is done (immediately
|
* @returns {Promise} resolved when the redirection is done (immediately
|
||||||
* when redirecting to a new page)
|
* when redirecting to a new page)
|
||||||
*/
|
*/
|
||||||
_executeURLAction: function (action, options) {
|
_executeURLAction: function (action, options) {
|
||||||
@@ -531,7 +533,7 @@ var ActionManager = Widget.extend({
|
|||||||
|
|
||||||
if (action.target === 'self') {
|
if (action.target === 'self') {
|
||||||
framework.redirect(url);
|
framework.redirect(url);
|
||||||
return $.Deferred();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
var w = window.open(url, '_blank');
|
var w = window.open(url, '_blank');
|
||||||
if (!w || w.closed || typeof w.closed === 'undefined') {
|
if (!w || w.closed || typeof w.closed === 'undefined') {
|
||||||
@@ -544,7 +546,7 @@ var ActionManager = Widget.extend({
|
|||||||
|
|
||||||
options.on_close();
|
options.on_close();
|
||||||
|
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Returns a description of the controllers in the given controller stack.
|
* Returns a description of the controllers in the given controller stack.
|
||||||
@@ -653,14 +655,14 @@ var ActionManager = Widget.extend({
|
|||||||
* @param {Object} action
|
* @param {Object} action
|
||||||
* @param {string} action.type
|
* @param {string} action.type
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @returns {Deferred} resolved when the action has been executed ; rejected
|
* @returns {Promise} resolved when the action has been executed ; rejected
|
||||||
* if the type of action isn't supported, or if the action can't be
|
* if the type of action isn't supported, or if the action can't be
|
||||||
* executed
|
* executed
|
||||||
*/
|
*/
|
||||||
_handleAction: function (action, options) {
|
_handleAction: function (action, options) {
|
||||||
if (!action.type) {
|
if (!action.type) {
|
||||||
console.error("No type for action", action);
|
console.error("No type for action", action);
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'ir.actions.act_url':
|
case 'ir.actions.act_url':
|
||||||
@@ -674,7 +676,7 @@ var ActionManager = Widget.extend({
|
|||||||
default:
|
default:
|
||||||
console.error("The ActionManager can't handle actions of type " +
|
console.error("The ActionManager can't handle actions of type " +
|
||||||
action.type, action);
|
action.type, action);
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -749,16 +751,17 @@ var ActionManager = Widget.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {integer|string} action's ID or xml ID
|
* @param {integer|string} action's ID or xml ID
|
||||||
* @param {Object} context
|
* @param {Object} context
|
||||||
* @returns {Deferred<Object>} resolved with the description of the action
|
* @returns {Promise<Object>} resolved with the description of the action
|
||||||
*/
|
*/
|
||||||
_loadAction: function (actionID, context) {
|
_loadAction: function (actionID, context) {
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
this.trigger_up('load_action', {
|
return new Promise(function (resolve, reject) {
|
||||||
actionID: actionID,
|
self.trigger_up('load_action', {
|
||||||
context: context,
|
actionID: actionID,
|
||||||
on_success: def.resolve.bind(def),
|
context: context,
|
||||||
|
on_success: resolve,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Preprocesses the action before it is handled by the ActionManager
|
* Preprocesses the action before it is handled by the ActionManager
|
||||||
@@ -815,20 +818,20 @@ var ActionManager = Widget.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} controllerID
|
* @param {string} controllerID
|
||||||
* @returns {Deferred} resolved when the controller has been restored
|
* @returns {Promise} resolved when the controller has been restored
|
||||||
*/
|
*/
|
||||||
_restoreController: function (controllerID) {
|
_restoreController: function (controllerID) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var controller = this.controllers[controllerID];
|
var controller = this.controllers[controllerID];
|
||||||
// AAB: AbstractAction should define a proper hook to execute code when
|
// AAB: AbstractAction should define a proper hook to execute code when
|
||||||
// it is restored (other than do_show), and it should return a deferred
|
// it is restored (other than do_show), and it should return a promise
|
||||||
var action = this.actions[controller.actionID];
|
var action = this.actions[controller.actionID];
|
||||||
var def;
|
var def;
|
||||||
if (action.on_reverse_breadcrumb) {
|
if (action.on_reverse_breadcrumb) {
|
||||||
def = action.on_reverse_breadcrumb();
|
def = action.on_reverse_breadcrumb();
|
||||||
}
|
}
|
||||||
return $.when(def).then(function () {
|
return Promise.resolve(def).then(function () {
|
||||||
return $.when(controller.widget.do_show()).then(function () {
|
return Promise.resolve(controller.widget.do_show()).then(function () {
|
||||||
var index = _.indexOf(self.controllerStack, controllerID);
|
var index = _.indexOf(self.controllerStack, controllerID);
|
||||||
self._pushController(controller, index);
|
self._pushController(controller, index);
|
||||||
});
|
});
|
||||||
@@ -844,7 +847,7 @@ var ActionManager = Widget.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} controller
|
* @param {Object} controller
|
||||||
* @returns {Deferred<Object>} resolved with the controller when it is ready
|
* @returns {Promise<Object>} resolved with the controller when it is ready
|
||||||
*/
|
*/
|
||||||
_startController: function (controller) {
|
_startController: function (controller) {
|
||||||
var fragment = document.createDocumentFragment();
|
var fragment = document.createDocumentFragment();
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ ActionManager.include({
|
|||||||
* @param {integer} [options.index=0] the controller's index in the stack
|
* @param {integer} [options.index=0] the controller's index in the stack
|
||||||
* @param {boolean} [options.lazy=false] set to true to differ the
|
* @param {boolean} [options.lazy=false] set to true to differ the
|
||||||
* initialization of the controller's widget
|
* initialization of the controller's widget
|
||||||
* @returns {Deferred<Object>} resolved with the created controller
|
* @returns {Promise<Object>} resolved with the created controller
|
||||||
*/
|
*/
|
||||||
_createViewController: function (action, viewType, viewOptions, options) {
|
_createViewController: function (action, viewType, viewOptions, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -153,7 +153,7 @@ ActionManager.include({
|
|||||||
// the requested view type isn't specified in the action (e.g.
|
// the requested view type isn't specified in the action (e.g.
|
||||||
// action with list view only, user clicks on a row in the list, it
|
// action with list view only, user clicks on a row in the list, it
|
||||||
// tries to switch to form view)
|
// tries to switch to form view)
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
@@ -186,26 +186,31 @@ ActionManager.include({
|
|||||||
// communication with trigger_up
|
// communication with trigger_up
|
||||||
controllerID: controllerID,
|
controllerID: controllerID,
|
||||||
});
|
});
|
||||||
|
var rejection;
|
||||||
var view = new viewDescr.Widget(viewDescr.fieldsView, viewOptions);
|
var view = new viewDescr.Widget(viewDescr.fieldsView, viewOptions);
|
||||||
var def = $.Deferred();
|
var def = new Promise(function (resolve, reject) {
|
||||||
action.controllers[viewType] = def;
|
rejection = reject;
|
||||||
view.getController(this).then(function (widget) {
|
view.getController(self).then(function (widget) {
|
||||||
if (def.state() === 'rejected') {
|
if (def.rejected) {
|
||||||
// the deferred has been rejected meanwhile, meaning that
|
// the promise has been rejected meanwhile, meaning that
|
||||||
// the action has been removed, so simply destroy the widget
|
// the action has been removed, so simply destroy the widget
|
||||||
widget.destroy();
|
widget.destroy();
|
||||||
} else {
|
} else {
|
||||||
controller.widget = widget;
|
controller.widget = widget;
|
||||||
def.resolve(controller);
|
resolve(controller);
|
||||||
}
|
}
|
||||||
}).fail(def.reject.bind(def));
|
}).guardedCatch(reject);
|
||||||
def.fail(function () {
|
});
|
||||||
|
// Need to define an reject property to call it into _destroyWindowAction
|
||||||
|
def.reject = rejection;
|
||||||
|
def.guardedCatch(function () {
|
||||||
|
def.rejected = true;
|
||||||
delete self.controllers[controllerID];
|
delete self.controllers[controllerID];
|
||||||
});
|
});
|
||||||
|
action.controllers[viewType] = def;
|
||||||
} else {
|
} else {
|
||||||
action.controllers[viewType] = $.Deferred().resolve(controller);
|
action.controllers[viewType] = Promise.resolve(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
return action.controllers[viewType];
|
return action.controllers[viewType];
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -217,18 +222,22 @@ ActionManager.include({
|
|||||||
*/
|
*/
|
||||||
_destroyWindowAction: function (action) {
|
_destroyWindowAction: function (action) {
|
||||||
var self = this;
|
var self = this;
|
||||||
_.each(action.controllers, function (controllerDef) {
|
for (var c in action.controllers) {
|
||||||
|
var controllerDef = action.controllers[c];
|
||||||
controllerDef.then(function (controller) {
|
controllerDef.then(function (controller) {
|
||||||
delete self.controllers[controller.jsID];
|
delete self.controllers[controller.jsID];
|
||||||
if (controller.widget) {
|
if (controller.widget) {
|
||||||
controller.widget.destroy();
|
controller.widget.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// reject the deferred if it is not yet resolved, so that the
|
// If controllerDef is not resolved yet, reject it so that the
|
||||||
// controller is correctly destroyed as soon as it is ready, and
|
// controller will be correctly destroyed as soon as it'll be ready,
|
||||||
// its reference is removed
|
// and its reference will be removed. Lazy-loaded controllers do
|
||||||
controllerDef.reject();
|
// not have a reject function on their promise
|
||||||
});
|
if (controllerDef.reject) {
|
||||||
|
controllerDef.reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Executes actions of type 'ir.actions.act_window'.
|
* Executes actions of type 'ir.actions.act_window'.
|
||||||
@@ -239,13 +248,13 @@ ActionManager.include({
|
|||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @param {integer} [options.resID] the current res ID
|
* @param {integer} [options.resID] the current res ID
|
||||||
* @param {string} [options.viewType] the view to open
|
* @param {string} [options.viewType] the view to open
|
||||||
* @returns {Deferred} resolved when the action is appended to the DOM
|
* @returns {Promise} resolved when the action is appended to the DOM
|
||||||
*/
|
*/
|
||||||
_executeWindowAction: function (action, options) {
|
_executeWindowAction: function (action, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.dp.add(this._loadViews(action)).then(function (fieldsViews) {
|
return this.dp.add(this._loadViews(action)).then(function (fieldsViews) {
|
||||||
var views = self._generateActionViews(action, fieldsViews);
|
var views = self._generateActionViews(action, fieldsViews);
|
||||||
action._views = action.views; // save the initial attribute
|
action._views = action.views; // save the initial attribute
|
||||||
action.views = views;
|
action.views = views;
|
||||||
action.controlPanelFieldsView = fieldsViews.search;
|
action.controlPanelFieldsView = fieldsViews.search;
|
||||||
action.controllers = {};
|
action.controllers = {};
|
||||||
@@ -285,7 +294,7 @@ ActionManager.include({
|
|||||||
self.controllerStack.push(lazyLoadedController.jsID);
|
self.controllerStack.push(lazyLoadedController.jsID);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return self.dp.add($.when(lazyViewDef))
|
return self.dp.add(Promise.resolve(lazyViewDef))
|
||||||
.then(function () {
|
.then(function () {
|
||||||
var viewOptions = {
|
var viewOptions = {
|
||||||
controllerState: options.controllerState,
|
controllerState: options.controllerState,
|
||||||
@@ -300,7 +309,7 @@ ActionManager.include({
|
|||||||
action.controllerID = controller.jsID;
|
action.controllerID = controller.jsID;
|
||||||
return self._executeAction(action, options);
|
return self._executeAction(action, options);
|
||||||
})
|
})
|
||||||
.fail(self._destroyWindowAction.bind(self, action));
|
.guardedCatch(self._destroyWindowAction.bind(self, action));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -389,7 +398,7 @@ ActionManager.include({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} action
|
* @param {Object} action
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadViews: function (action) {
|
_loadViews: function (action) {
|
||||||
var inDialog = action.target === 'new';
|
var inDialog = action.target === 'new';
|
||||||
@@ -443,11 +452,11 @@ ActionManager.include({
|
|||||||
return this.clearUncommittedChanges().then(function () {
|
return this.clearUncommittedChanges().then(function () {
|
||||||
// AAB: this will be done directly in AbstractAction's restore
|
// AAB: this will be done directly in AbstractAction's restore
|
||||||
// function
|
// function
|
||||||
var def;
|
var def = Promise.resolve();
|
||||||
if (action.on_reverse_breadcrumb) {
|
if (action.on_reverse_breadcrumb) {
|
||||||
def = action.on_reverse_breadcrumb();
|
def = action.on_reverse_breadcrumb();
|
||||||
}
|
}
|
||||||
return $.when(def).then(function () {
|
return Promise.resolve(def).then(function () {
|
||||||
return self._switchController(action, controller.viewType);
|
return self._switchController(action, controller.viewType);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -461,14 +470,14 @@ ActionManager.include({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} controller the controller to switch to
|
* @param {Object} controller the controller to switch to
|
||||||
* @param {Object} [viewOptions]
|
* @param {Object} [viewOptions]
|
||||||
* @return {Deferred} resolved when the new controller is in the DOM
|
* @return {Promise} resolved when the new controller is in the DOM
|
||||||
*/
|
*/
|
||||||
_switchController: function (action, viewType, viewOptions) {
|
_switchController: function (action, viewType, viewOptions) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var view = _.findWhere(action.views, {type: viewType});
|
var view = _.findWhere(action.views, {type: viewType});
|
||||||
if (!view) {
|
if (!view) {
|
||||||
// can't switch to an unknown view
|
// can't switch to an unknown view
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
var currentController = this.getCurrentController();
|
var currentController = this.getCurrentController();
|
||||||
var index;
|
var index;
|
||||||
@@ -519,28 +528,30 @@ ActionManager.include({
|
|||||||
};
|
};
|
||||||
|
|
||||||
var controllerDef = action.controllers[viewType];
|
var controllerDef = action.controllers[viewType];
|
||||||
if (!controllerDef || controllerDef.state() === 'rejected') {
|
if (controllerDef) {
|
||||||
// if the controllerDef is rejected, it probably means that the js
|
|
||||||
// code or the requests made to the server crashed. In that case,
|
|
||||||
// if we reuse the same deferred, then the switch to the view is
|
|
||||||
// definitely blocked. We want to use a new controller, even though
|
|
||||||
// it is very likely that it will recrash again. At least, it will
|
|
||||||
// give more feedback to the user, and it could happen that one
|
|
||||||
// record crashes, but not another.
|
|
||||||
controllerDef = newController();
|
|
||||||
} else {
|
|
||||||
controllerDef = controllerDef.then(function (controller) {
|
controllerDef = controllerDef.then(function (controller) {
|
||||||
if (!controller.widget) {
|
if (!controller.widget) {
|
||||||
// lazy loaded -> load it now (with same jsID)
|
// lazy loaded -> load it now (with same jsID)
|
||||||
return newController(controller.jsID);
|
return newController(controller.jsID);
|
||||||
} else {
|
} else {
|
||||||
return $.when(controller.widget.willRestore()).then(function () {
|
return Promise.resolve(controller.widget.willRestore()).then(function () {
|
||||||
return controller.widget.reload(viewOptions).then(function () {
|
return controller.widget.reload(viewOptions).then(function () {
|
||||||
return controller;
|
return controller;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, function () {
|
||||||
|
// if the controllerDef is rejected, it probably means that the js
|
||||||
|
// code or the requests made to the server crashed. In that case,
|
||||||
|
// if we reuse the same promise, then the switch to the view is
|
||||||
|
// definitely blocked. We want to use a new controller, even though
|
||||||
|
// it is very likely that it will recrash again. At least, it will
|
||||||
|
// give more feedback to the user, and it could happen that one
|
||||||
|
// record crashes, but not another.
|
||||||
|
return newController();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
controllerDef = newController();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.dp.add(controllerDef).then(function (controller) {
|
return this.dp.add(controllerDef).then(function (controller) {
|
||||||
@@ -577,11 +588,11 @@ ActionManager.include({
|
|||||||
var env = ev.data.env;
|
var env = ev.data.env;
|
||||||
var context = new Context(env.context, actionData.context || {});
|
var context = new Context(env.context, actionData.context || {});
|
||||||
var recordID = env.currentID || null; // pyUtils handles null value, not undefined
|
var recordID = env.currentID || null; // pyUtils handles null value, not undefined
|
||||||
var def = $.Deferred();
|
var def;
|
||||||
|
|
||||||
// determine the action to execute according to the actionData
|
// determine the action to execute according to the actionData
|
||||||
if (actionData.special) {
|
if (actionData.special) {
|
||||||
def = $.when({type: 'ir.actions.act_window_close', infos: 'special'});
|
def = Promise.resolve({type: 'ir.actions.act_window_close', infos: 'special'});
|
||||||
} else if (actionData.type === 'object') {
|
} else if (actionData.type === 'object') {
|
||||||
// call a Python Object method, which may return an action to execute
|
// call a Python Object method, which may return an action to execute
|
||||||
var args = recordID ? [[recordID]] : [env.resIDs];
|
var args = recordID ? [[recordID]] : [env.resIDs];
|
||||||
@@ -611,13 +622,15 @@ ActionManager.include({
|
|||||||
active_ids: env.resIDs,
|
active_ids: env.resIDs,
|
||||||
active_id: recordID,
|
active_id: recordID,
|
||||||
}));
|
}));
|
||||||
|
} else {
|
||||||
|
def = new Promise(function () {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the DropPrevious to prevent from executing the handler if another
|
// use the DropPrevious to prevent from executing the handler if another
|
||||||
// request (doAction, switchView...) has been done meanwhile ; execute
|
// request (doAction, switchView...) has been done meanwhile ; execute
|
||||||
// the fail handler if the 'call_button' or 'loadAction' failed but not
|
// the fail handler if the 'call_button' or 'loadAction' failed but not
|
||||||
// if the request failed due to the DropPrevious,
|
// if the request failed due to the DropPrevious,
|
||||||
def.fail(ev.data.on_fail);
|
def.guardedCatch(ev.data.on_fail);
|
||||||
this.dp.add(def).then(function (action) {
|
this.dp.add(def).then(function (action) {
|
||||||
// show effect if button have effect attribute
|
// show effect if button have effect attribute
|
||||||
// rainbowman can be displayed from two places: from attribute on a button or from python
|
// rainbowman can be displayed from two places: from attribute on a button or from python
|
||||||
|
|||||||
@@ -40,35 +40,36 @@ ActionManager.include({
|
|||||||
* report generation and download.
|
* report generation and download.
|
||||||
*
|
*
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @returns {Deferred} resolved when the report has been downloaded ;
|
* @returns {Promise} resolved when the report has been downloaded ;
|
||||||
* rejected if something went wrong during the report generation
|
* rejected if something went wrong during the report generation
|
||||||
*/
|
*/
|
||||||
_downloadReport: function (url) {
|
_downloadReport: function (url) {
|
||||||
|
var self = this;
|
||||||
framework.blockUI();
|
framework.blockUI();
|
||||||
var def = $.Deferred();
|
return new Promise(function (resolve, reject) {
|
||||||
var type = 'qweb-' + url.split('/')[2];
|
var type = 'qweb-' + url.split('/')[2];
|
||||||
var blocked = !session.get_file({
|
var blocked = !session.get_file({
|
||||||
url: '/report/download',
|
url: '/report/download',
|
||||||
data: {
|
data: {
|
||||||
data: JSON.stringify([url, type]),
|
data: JSON.stringify([url, type]),
|
||||||
},
|
},
|
||||||
success: def.resolve.bind(def),
|
success: resolve,
|
||||||
error: function () {
|
error: function () {
|
||||||
crash_manager.rpc_error.apply(crash_manager, arguments);
|
crash_manager.rpc_error.apply(crash_manager, arguments);
|
||||||
def.reject();
|
reject();
|
||||||
},
|
},
|
||||||
complete: framework.unblockUI,
|
complete: framework.unblockUI,
|
||||||
|
});
|
||||||
|
if (blocked) {
|
||||||
|
// AAB: this check should be done in get_file service directly,
|
||||||
|
// should not be the concern of the caller (and that way, get_file
|
||||||
|
// could return a promise)
|
||||||
|
var message = _t('A popup window with your report was blocked. You ' +
|
||||||
|
'may need to change your browser settings to allow ' +
|
||||||
|
'popup windows for this page.');
|
||||||
|
self.do_warn(_t('Warning'), message, true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (blocked) {
|
|
||||||
// AAB: this check should be done in get_file service directly,
|
|
||||||
// should not be the concern of the caller (and that way, get_file
|
|
||||||
// could return a deferred)
|
|
||||||
var message = _t('A popup window with your report was blocked. You ' +
|
|
||||||
'may need to change your browser settings to allow ' +
|
|
||||||
'popup windows for this page.');
|
|
||||||
this.do_warn(_t('Warning'), message, true);
|
|
||||||
}
|
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +78,7 @@ ActionManager.include({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} action the description of the action to execute
|
* @param {Object} action the description of the action to execute
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the action has been executed
|
* @returns {Promise} resolved when the action has been executed
|
||||||
*/
|
*/
|
||||||
_triggerDownload: function (action, options, type){
|
_triggerDownload: function (action, options, type){
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -97,7 +98,7 @@ ActionManager.include({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} action the description of the action to execute
|
* @param {Object} action the description of the action to execute
|
||||||
* @param {Object} options @see doAction for details
|
* @param {Object} options @see doAction for details
|
||||||
* @returns {Deferred} resolved when the action has been executed
|
* @returns {Promise} resolved when the action has been executed
|
||||||
*/
|
*/
|
||||||
_executeReportAction: function (action, options) {
|
_executeReportAction: function (action, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -125,7 +126,7 @@ ActionManager.include({
|
|||||||
} else {
|
} else {
|
||||||
console.error("The ActionManager can't handle reports of type " +
|
console.error("The ActionManager can't handle reports of type " +
|
||||||
action.report_type, action);
|
action.report_type, action);
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -135,7 +136,7 @@ ActionManager.include({
|
|||||||
*
|
*
|
||||||
* @param {Object} action
|
* @param {Object} action
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @returns {Deferred} resolved when the client action has been executed
|
* @returns {Promise} resolved when the client action has been executed
|
||||||
*/
|
*/
|
||||||
_executeReportClientAction: function (action, options) {
|
_executeReportClientAction: function (action, options) {
|
||||||
var urls = this._makeReportUrls(action);
|
var urls = this._makeReportUrls(action);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ var ActionMixin = {
|
|||||||
* Called by the action manager when action is restored (typically, when the
|
* Called by the action manager when action is restored (typically, when the
|
||||||
* user clicks on the action in the breadcrumb)
|
* user clicks on the action in the breadcrumb)
|
||||||
*
|
*
|
||||||
* @returns {Deferred|undefined}
|
* @returns {Promise|undefined}
|
||||||
*/
|
*/
|
||||||
willRestore: function () {},
|
willRestore: function () {},
|
||||||
|
|
||||||
@@ -89,14 +89,14 @@ var ActionMixin = {
|
|||||||
* example, if the user has edited a form, maybe we should ask him if we
|
* example, if the user has edited a form, maybe we should ask him if we
|
||||||
* can discard all his changes when we switch to another action. In that
|
* can discard all his changes when we switch to another action. In that
|
||||||
* case, the action manager will call this method. If the returned
|
* case, the action manager will call this method. If the returned
|
||||||
* deferred is succesfully resolved, then we can destroy the current action,
|
* promise is succesfully resolved, then we can destroy the current action,
|
||||||
* otherwise, we need to stop.
|
* otherwise, we need to stop.
|
||||||
*
|
*
|
||||||
* @returns {Deferred} resolved if the action can be removed, rejected
|
* @returns {Promise} resolved if the action can be removed, rejected
|
||||||
* otherwise
|
* otherwise
|
||||||
*/
|
*/
|
||||||
canBeRemoved: function () {
|
canBeRemoved: function () {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* This function is called when the current state of the action
|
* This function is called when the current state of the action
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ var Loading = Widget.extend({
|
|||||||
this._super(parent);
|
this._super(parent);
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
this.blocked_ui = false;
|
this.blocked_ui = false;
|
||||||
|
},
|
||||||
|
start: function() {
|
||||||
core.bus.on('rpc_request', this, this.request_call);
|
core.bus.on('rpc_request', this, this.request_call);
|
||||||
core.bus.on("rpc_response", this, this.response_call);
|
core.bus.on("rpc_response", this, this.response_call);
|
||||||
core.bus.on("rpc_response_failed", this, this.response_call);
|
core.bus.on("rpc_response_failed", this, this.response_call);
|
||||||
|
|||||||
@@ -64,20 +64,22 @@ var Menu = Widget.extend({
|
|||||||
|
|
||||||
// Apps Menu
|
// Apps Menu
|
||||||
this._appsMenu = new AppsMenu(self, this.menu_data);
|
this._appsMenu = new AppsMenu(self, this.menu_data);
|
||||||
this._appsMenu.appendTo(this.$menu_apps);
|
var appsMenuProm = this._appsMenu.appendTo(this.$menu_apps);
|
||||||
|
|
||||||
// Systray Menu
|
// Systray Menu
|
||||||
this.systray_menu = new SystrayMenu(this);
|
this.systray_menu = new SystrayMenu(this);
|
||||||
this.systray_menu.attachTo(this.$('.o_menu_systray'));
|
var systrayMenuProm = this.systray_menu.attachTo(this.$('.o_menu_systray')).then(function() {
|
||||||
|
dom.initAutoMoreMenu(self.$section_placeholder, {
|
||||||
dom.initAutoMoreMenu(this.$section_placeholder, {
|
|
||||||
maxWidth: function () {
|
maxWidth: function () {
|
||||||
return self.$el.width() - (self.$menu_apps.outerWidth(true) + self.$menu_brand_placeholder.outerWidth(true) + self.systray_menu.$el.outerWidth(true));
|
return self.$el.width() - (self.$menu_apps.outerWidth(true) + self.$menu_brand_placeholder.outerWidth(true) + self.systray_menu.$el.outerWidth(true));
|
||||||
},
|
},
|
||||||
sizeClass: 'SM',
|
sizeClass: 'SM',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return this._super.apply(this, arguments);
|
|
||||||
|
|
||||||
|
return Promise.all([this._super.apply(this, arguments), appsMenuProm, systrayMenuProm]);
|
||||||
},
|
},
|
||||||
change_menu_section: function (primary_menu_id) {
|
change_menu_section: function (primary_menu_id) {
|
||||||
if (!this.$menu_sections[primary_menu_id]) {
|
if (!this.$menu_sections[primary_menu_id]) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ var Pager = Widget.extend({
|
|||||||
* @param {boolean} [options.can_edit] editable feature of the pager
|
* @param {boolean} [options.can_edit] editable feature of the pager
|
||||||
* @param {boolean} [options.single_page_hidden] (not) to display the pager
|
* @param {boolean} [options.single_page_hidden] (not) to display the pager
|
||||||
* if only one page
|
* if only one page
|
||||||
* @param {function} [options.validate] callback returning a Deferred to
|
* @param {function} [options.validate] callback returning a Promise to
|
||||||
* validate changes
|
* validate changes
|
||||||
*/
|
*/
|
||||||
init: function (parent, size, current_min, limit, options) {
|
init: function (parent, size, current_min, limit, options) {
|
||||||
@@ -46,7 +46,7 @@ var Pager = Widget.extend({
|
|||||||
can_edit: true, // editable
|
can_edit: true, // editable
|
||||||
single_page_hidden: false, // displayed even if there is a single page
|
single_page_hidden: false, // displayed even if there is a single page
|
||||||
validate: function() {
|
validate: function() {
|
||||||
return $.Deferred().resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
withAccessKey: true, // can be disabled, for example, for x2m widgets
|
withAccessKey: true, // can be disabled, for example, for x2m widgets
|
||||||
});
|
});
|
||||||
@@ -55,7 +55,7 @@ var Pager = Widget.extend({
|
|||||||
/**
|
/**
|
||||||
* Renders the pager
|
* Renders the pager
|
||||||
*
|
*
|
||||||
* @returns {jQuery.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this.$value = this.$('.o_pager_value');
|
this.$value = this.$('.o_pager_value');
|
||||||
@@ -216,7 +216,6 @@ var Pager = Widget.extend({
|
|||||||
}
|
}
|
||||||
self.trigger('pager_changed', _.clone(self.state));
|
self.trigger('pager_changed', _.clone(self.state));
|
||||||
}
|
}
|
||||||
}).always(function() {
|
|
||||||
// Render the pager's new state (removes the input)
|
// Render the pager's new state (removes the input)
|
||||||
self._render();
|
self._render();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ var Sidebar = Widget.extend({
|
|||||||
action_id: item.action.id,
|
action_id: item.action.id,
|
||||||
context: context,
|
context: context,
|
||||||
},
|
},
|
||||||
}).done(function (result) {
|
}).then(function (result) {
|
||||||
result.context = new Context(
|
result.context = new Context(
|
||||||
result.context || {}, activeIdsContext)
|
result.context || {}, activeIdsContext)
|
||||||
.set_eval_context(context);
|
.set_eval_context(context);
|
||||||
|
|||||||
@@ -16,20 +16,19 @@ var SystrayMenu = Widget.extend({
|
|||||||
this._super(parent);
|
this._super(parent);
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.widgets = [];
|
this.widgets = [];
|
||||||
this.load = $.Deferred();
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
self._super.apply(this, arguments);
|
self._super.apply(this, arguments);
|
||||||
self._loadItems();
|
self._loadItems();
|
||||||
$.when.apply($, self.items).always(function () {
|
|
||||||
self.load.resolve();
|
return new Promise(function (resolve, reject) {
|
||||||
|
Promise.all(self.items).then(resolve).guardedCatch(resolve);
|
||||||
});
|
});
|
||||||
return self.load;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ var UserMenu = Widget.extend({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -36,7 +36,7 @@ var UserMenu = Widget.extend({
|
|||||||
var $avatar = self.$('.oe_topbar_avatar');
|
var $avatar = self.$('.oe_topbar_avatar');
|
||||||
if (!session.uid) {
|
if (!session.uid) {
|
||||||
$avatar.attr('src', $avatar.data('default-src'));
|
$avatar.attr('src', $avatar.data('default-src'));
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var topbar_name = session.name;
|
var topbar_name = session.name;
|
||||||
if (session.debug) {
|
if (session.debug) {
|
||||||
@@ -67,7 +67,7 @@ var UserMenu = Widget.extend({
|
|||||||
.then(function (url) {
|
.then(function (url) {
|
||||||
framework.redirect(url);
|
framework.redirect(url);
|
||||||
})
|
})
|
||||||
.fail(function (result, ev){
|
.guardedCatch(function (result, ev){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
framework.redirect('https://accounts.odoo.com/account');
|
framework.redirect('https://accounts.odoo.com/account');
|
||||||
});
|
});
|
||||||
@@ -100,7 +100,7 @@ var UserMenu = Widget.extend({
|
|||||||
model: "res.users",
|
model: "res.users",
|
||||||
method: "action_get"
|
method: "action_get"
|
||||||
})
|
})
|
||||||
.done(function (result) {
|
.then(function (result) {
|
||||||
result.res_id = session.uid;
|
result.res_id = session.uid;
|
||||||
self.do_action(result);
|
self.do_action(result);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ return AbstractWebClient.extend({
|
|||||||
|
|
||||||
instanciate_menu_widgets: function () {
|
instanciate_menu_widgets: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var defs = [];
|
var proms = [];
|
||||||
return this.load_menus().then(function (menuData) {
|
return this.load_menus().then(function (menuData) {
|
||||||
self.menu_data = menuData;
|
self.menu_data = menuData;
|
||||||
|
|
||||||
@@ -122,8 +122,8 @@ return AbstractWebClient.extend({
|
|||||||
self.menu.destroy();
|
self.menu.destroy();
|
||||||
}
|
}
|
||||||
self.menu = new Menu(self, menuData);
|
self.menu = new Menu(self, menuData);
|
||||||
defs.push(self.menu.prependTo(self.$el));
|
proms.push(self.menu.prependTo(self.$el));
|
||||||
return $.when.apply($, defs);
|
return Promise.all(proms);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ return AbstractWebClient.extend({
|
|||||||
on_hashchange: function (event) {
|
on_hashchange: function (event) {
|
||||||
if (this._ignore_hashchange) {
|
if (this._ignore_hashchange) {
|
||||||
this._ignore_hashchange = false;
|
this._ignore_hashchange = false;
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -181,48 +181,51 @@ return AbstractWebClient.extend({
|
|||||||
return this.menu_dm.add(data_manager.load_action(ev.data.action_id))
|
return this.menu_dm.add(data_manager.load_action(ev.data.action_id))
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
return self.action_mutex.exec(function () {
|
return self.action_mutex.exec(function () {
|
||||||
var completed = $.Deferred();
|
var completed = new Promise(function (resolve, reject) {
|
||||||
var options = _.extend({}, ev.data.options, {
|
var options = _.extend({}, ev.data.options, {
|
||||||
clear_breadcrumbs: true,
|
clear_breadcrumbs: true,
|
||||||
action_menu_id: ev.data.menu_id,
|
action_menu_id: ev.data.menu_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.resolve(self._openMenu(result, options))
|
||||||
|
.then(function() {
|
||||||
|
self._on_app_clicked_done(ev)
|
||||||
|
.then(resolve)
|
||||||
|
.guardedCatch(reject);
|
||||||
|
}).guardedCatch(function() {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
resolve();
|
||||||
|
}, 2000);
|
||||||
});
|
});
|
||||||
$.when(self._openMenu(result, options)).fail(function () {
|
|
||||||
completed.resolve();
|
|
||||||
}).done(function () {
|
|
||||||
self._on_app_clicked_done(ev)
|
|
||||||
.then(completed.resolve.bind(completed))
|
|
||||||
.fail(completed.reject.bind(completed));
|
|
||||||
});
|
|
||||||
setTimeout(function () {
|
|
||||||
completed.resolve();
|
|
||||||
}, 2000);
|
|
||||||
return completed;
|
return completed;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_on_app_clicked_done: function (ev) {
|
_on_app_clicked_done: function (ev) {
|
||||||
core.bus.trigger('change_menu_section', ev.data.menu_id);
|
core.bus.trigger('change_menu_section', ev.data.menu_id);
|
||||||
return $.Deferred().resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
on_menu_clicked: function (ev) {
|
on_menu_clicked: function (ev) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.menu_dm.add(data_manager.load_action(ev.data.action_id))
|
return this.menu_dm.add(data_manager.load_action(ev.data.action_id))
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
|
self.$el.removeClass('o_mobile_menu_opened');
|
||||||
|
|
||||||
return self.action_mutex.exec(function () {
|
return self.action_mutex.exec(function () {
|
||||||
var completed = $.Deferred();
|
var completed = new Promise(function (resolve, reject) {
|
||||||
$.when(self._openMenu(result, {
|
Promise.resolve(self._openMenu(result, {
|
||||||
clear_breadcrumbs: true,
|
clear_breadcrumbs: true,
|
||||||
})).always(function () {
|
})).then(resolve).guardedCatch(reject);
|
||||||
completed.resolve();
|
|
||||||
|
setTimeout(function () {
|
||||||
|
resolve();
|
||||||
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
completed.resolve();
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
return completed;
|
return completed;
|
||||||
});
|
});
|
||||||
}).always(function () {
|
}).guardedCatch(function () {
|
||||||
self.$el.removeClass('o_mobile_menu_opened');
|
self.$el.removeClass('o_mobile_menu_opened');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -233,7 +236,7 @@ return AbstractWebClient.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} action
|
* @param {Object} action
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_openMenu: function (action, options) {
|
_openMenu: function (action, options) {
|
||||||
return this.do_action(action, options);
|
return this.do_action(action, options);
|
||||||
|
|||||||
@@ -3,22 +3,19 @@ odoo.define('web.ajax', function (require) {
|
|||||||
|
|
||||||
var config = require('web.config');
|
var config = require('web.config');
|
||||||
var core = require('web.core');
|
var core = require('web.core');
|
||||||
var utils = require('web.utils');
|
|
||||||
var time = require('web.time');
|
var time = require('web.time');
|
||||||
var download = require('web.download');
|
var download = require('web.download');
|
||||||
var contentdisposition = require('web.contentdisposition');
|
var contentdisposition = require('web.contentdisposition');
|
||||||
|
|
||||||
var _t = core._t;
|
var _t = core._t;
|
||||||
|
|
||||||
function genericJsonRpc (fct_name, params, settings, fct) {
|
function _genericJsonRpc (fct_name, params, settings, fct) {
|
||||||
var shadow = settings.shadow || false;
|
var shadow = settings.shadow || false;
|
||||||
delete settings.shadow;
|
delete settings.shadow;
|
||||||
if (!shadow) {
|
if (!shadow) {
|
||||||
core.bus.trigger('rpc_request');
|
core.bus.trigger('rpc_request');
|
||||||
}
|
}
|
||||||
|
|
||||||
var deferred = $.Deferred();
|
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
jsonrpc: "2.0",
|
jsonrpc: "2.0",
|
||||||
method: fct_name,
|
method: fct_name,
|
||||||
@@ -26,7 +23,7 @@ function genericJsonRpc (fct_name, params, settings, fct) {
|
|||||||
id: Math.floor(Math.random() * 1000 * 1000 * 1000)
|
id: Math.floor(Math.random() * 1000 * 1000 * 1000)
|
||||||
};
|
};
|
||||||
var xhr = fct(data);
|
var xhr = fct(data);
|
||||||
var result = xhr.pipe(function(result) {
|
var result = xhr.then(function(result) {
|
||||||
core.bus.trigger('rpc:result', data, result);
|
core.bus.trigger('rpc:result', data, result);
|
||||||
if (result.error !== undefined) {
|
if (result.error !== undefined) {
|
||||||
if (result.error.data.arguments[0] !== "bus.Bus not available in test mode") {
|
if (result.error.data.arguments[0] !== "bus.Bus not available in test mode") {
|
||||||
@@ -36,65 +33,86 @@ function genericJsonRpc (fct_name, params, settings, fct) {
|
|||||||
console.error("Server application error", JSON.stringify(result.error));
|
console.error("Server application error", JSON.stringify(result.error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $.Deferred().reject("server", result.error);
|
return Promise.reject({type: "server", error: result.error});
|
||||||
} else {
|
} else {
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
}, function() {
|
}, function() {
|
||||||
//console.error("JsonRPC communication error", _.toArray(arguments));
|
//console.error("JsonRPC communication error", _.toArray(arguments));
|
||||||
var def = $.Deferred();
|
var reason = {
|
||||||
return def.reject.apply(def, ["communication"].concat(_.toArray(arguments)));
|
type: 'communication',
|
||||||
|
error: arguments[0],
|
||||||
|
textStatus: arguments[1],
|
||||||
|
errorThrown: arguments[2],
|
||||||
|
};
|
||||||
|
return Promise.reject(reason);
|
||||||
});
|
});
|
||||||
deferred.abort = function () {
|
|
||||||
deferred.reject({message: "XmlHttpRequestError abort"}, $.Event('abort'));
|
var rejection;
|
||||||
|
var promise = new Promise(function (resolve, reject) {
|
||||||
|
rejection = reject;
|
||||||
|
|
||||||
|
result.then(function (result) {
|
||||||
|
if (!shadow) {
|
||||||
|
core.bus.trigger('rpc_response');
|
||||||
|
}
|
||||||
|
resolve(result);
|
||||||
|
}, function (reason) {
|
||||||
|
var type = reason.type;
|
||||||
|
var error = reason.error;
|
||||||
|
var textStatus = reason.textStatus;
|
||||||
|
var errorThrown = reason.errorThrown;
|
||||||
|
if (type === "server") {
|
||||||
|
if (!shadow) {
|
||||||
|
core.bus.trigger('rpc_response');
|
||||||
|
}
|
||||||
|
if (error.code === 100) {
|
||||||
|
core.bus.trigger('invalidate_session');
|
||||||
|
}
|
||||||
|
reject({message: error, event: $.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]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
reject({message: nerror, event: $.Event()});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME: jsonp?
|
||||||
|
promise.abort = function () {
|
||||||
|
rejection({
|
||||||
|
message: "XmlHttpRequestError abort",
|
||||||
|
event: $.Event('abort')
|
||||||
|
});
|
||||||
if (xhr.abort) {
|
if (xhr.abort) {
|
||||||
xhr.abort();
|
xhr.abort();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
promise.guardedCatch(function (reason) { // Allow promise user to disable rpc_error call in case of failure
|
||||||
result.then(function (result) {
|
setTimeout(function () {
|
||||||
if (!shadow) {
|
// we want to execute this handler after all others (hence
|
||||||
core.bus.trigger('rpc_response');
|
// setTimeout) to let the other handlers prevent the event
|
||||||
}
|
if (!reason.event.isDefaultPrevented()) {
|
||||||
deferred.resolve(result);
|
core.bus.trigger('rpc_error', reason.message, reason.event);
|
||||||
}, function (type, error, textStatus, errorThrown) {
|
|
||||||
if (type === "server") {
|
|
||||||
if (!shadow) {
|
|
||||||
core.bus.trigger('rpc_response');
|
|
||||||
}
|
}
|
||||||
if (error.code === 100) {
|
}, 0);
|
||||||
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
|
return promise;
|
||||||
deferred.fail(function (error, event) {
|
};
|
||||||
if (!event.isDefaultPrevented()) {
|
|
||||||
core.bus.trigger('rpc_error', error, event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return deferred;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jsonRpc(url, fct_name, params, settings) {
|
function jsonRpc(url, fct_name, params, settings) {
|
||||||
settings = settings || {};
|
settings = settings || {};
|
||||||
return genericJsonRpc(fct_name, params, settings, function(data) {
|
return _genericJsonRpc(fct_name, params, settings, function(data) {
|
||||||
return $.ajax(url, _.extend({}, settings, {
|
return $.ajax(url, _.extend({}, settings, {
|
||||||
url: url,
|
url: url,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@@ -110,19 +128,6 @@ function rpc(url, params, settings) {
|
|||||||
return jsonRpc(url, 'call', params, settings);
|
return jsonRpc(url, 'call', params, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper
|
|
||||||
function realSetTimeout (fct, millis) {
|
|
||||||
var finished = new Date().getTime() + millis;
|
|
||||||
var wait = function() {
|
|
||||||
var current = new Date().getTime();
|
|
||||||
if (current < finished) {
|
|
||||||
setTimeout(wait, finished - current);
|
|
||||||
} else {
|
|
||||||
fct();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
setTimeout(wait, millis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load css asynchronously: fetch it from the url parameter and add a link tag
|
* Load css asynchronously: fetch it from the url parameter and add a link tag
|
||||||
@@ -131,26 +136,27 @@ function realSetTimeout (fct, millis) {
|
|||||||
* immediately.
|
* immediately.
|
||||||
*
|
*
|
||||||
* @param {String} url of the css to be fetched
|
* @param {String} url of the css to be fetched
|
||||||
* @returns {Deferred} resolved when the css has been loaded.
|
* @returns {Promise} resolved when the css has been loaded.
|
||||||
*/
|
*/
|
||||||
var loadCSS = (function () {
|
var loadCSS = (function () {
|
||||||
var urlDefs = Object.create(null);
|
var urlDefs = {};
|
||||||
|
|
||||||
return function loadCSS(url) {
|
return function loadCSS(url) {
|
||||||
if (url in urlDefs) {
|
if (url in urlDefs) {
|
||||||
// nothing to do here
|
// nothing to do here
|
||||||
} else if ($('link[href="' + url + '"]').length) {
|
} else if ($('link[href="' + url + '"]').length) {
|
||||||
// the link is already in the DOM, the deferred can be resolved
|
// the link is already in the DOM, the promise can be resolved
|
||||||
urlDefs[url] = $.when();
|
urlDefs[url] = Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
var $link = $('<link>', {
|
var $link = $('<link>', {
|
||||||
'href': url,
|
'href': url,
|
||||||
'rel': 'stylesheet',
|
'rel': 'stylesheet',
|
||||||
'type': 'text/css'
|
'type': 'text/css'
|
||||||
});
|
});
|
||||||
urlDefs[url] = $.Deferred();
|
urlDefs[url] = new Promise(function (resolve, reject) {
|
||||||
$link.on('load', function () {
|
$link.on('load', function () {
|
||||||
urlDefs[url].resolve();
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
$('head').append($link);
|
$('head').append($link);
|
||||||
}
|
}
|
||||||
@@ -159,44 +165,45 @@ var loadCSS = (function () {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
var loadJS = (function () {
|
var loadJS = (function () {
|
||||||
var urls = [];
|
var dependenciesPromise = {};
|
||||||
var defs = [];
|
|
||||||
|
|
||||||
var load = function loadJS(url) {
|
var load = function loadJS(url) {
|
||||||
// Check the DOM to see if a script with the specified url is already there
|
// Check the DOM to see if a script with the specified url is already there
|
||||||
var alreadyRequired = ($('script[src="' + url + '"]').length > 0);
|
var alreadyRequired = ($('script[src="' + url + '"]').length > 0);
|
||||||
|
|
||||||
// If loadJS was already called with the same URL, it will have a registered deferred indicating if
|
// If loadJS was already called with the same URL, it will have a registered promise indicating if
|
||||||
// the script has been fully loaded. If not, the deferred has to be initialized. This is initialized
|
// the script has been fully loaded. If not, the promise has to be initialized.
|
||||||
// as already resolved if the script was already there without the need of loadJS.
|
// This is initialized as already resolved if the script was already there without the need of loadJS.
|
||||||
var index = _.indexOf(urls, url);
|
if (url in dependenciesPromise) {
|
||||||
if (index < 0) {
|
return dependenciesPromise[url];
|
||||||
urls.push(url);
|
|
||||||
index = defs.push(alreadyRequired ? $.when() : $.Deferred()) - 1;
|
|
||||||
}
|
}
|
||||||
|
var scriptLoadedPromise = new Promise(function (resolve, reject) {
|
||||||
|
if (alreadyRequired) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
// Get the script associated promise and returns it after initializing the script if needed. The
|
||||||
|
// promise is marked to be resolved on script load and rejected on script error.
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = url;
|
||||||
|
script.onload = script.onreadystatechange = function() {
|
||||||
|
if ((script.readyState && script.readyState !== "loaded" && script.readyState !== "complete") || script.onload_done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
script.onload_done = true;
|
||||||
|
resolve(url);
|
||||||
|
};
|
||||||
|
script.onerror = function () {
|
||||||
|
console.error("Error loading file", script.src);
|
||||||
|
reject(url);
|
||||||
|
};
|
||||||
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
|
head.appendChild(script);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get the script associated deferred and returns it after initializing the script if needed. The
|
dependenciesPromise[url] = scriptLoadedPromise;
|
||||||
// deferred is marked to be resolved on script load and rejected on script error.
|
return scriptLoadedPromise;
|
||||||
var def = defs[index];
|
|
||||||
if (!alreadyRequired) {
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.type = 'text/javascript';
|
|
||||||
script.src = url;
|
|
||||||
script.onload = script.onreadystatechange = function() {
|
|
||||||
if ((script.readyState && script.readyState !== "loaded" && script.readyState !== "complete") || script.onload_done) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
script.onload_done = true;
|
|
||||||
def.resolve(url);
|
|
||||||
};
|
|
||||||
script.onerror = function () {
|
|
||||||
console.error("Error loading file", script.src);
|
|
||||||
def.reject(url);
|
|
||||||
};
|
|
||||||
var head = document.head || document.getElementsByTagName('head')[0];
|
|
||||||
head.appendChild(script);
|
|
||||||
}
|
|
||||||
return def;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return load;
|
return load;
|
||||||
@@ -308,22 +315,6 @@ function get_file(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function post (controller_url, data) {
|
function post (controller_url, data) {
|
||||||
|
|
||||||
var progressHandler = function (deferred) {
|
|
||||||
return function (state) {
|
|
||||||
if(state.lengthComputable) {
|
|
||||||
deferred.notify({
|
|
||||||
h_loaded: utils.human_size(state.loaded),
|
|
||||||
h_total : utils.human_size(state.total),
|
|
||||||
loaded : state.loaded,
|
|
||||||
total : state.total,
|
|
||||||
pcent : Math.round((state.loaded/state.total)*100)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var Def = $.Deferred();
|
|
||||||
var postData = new FormData();
|
var postData = new FormData();
|
||||||
|
|
||||||
$.each(data, function(i,val) {
|
$.each(data, function(i,val) {
|
||||||
@@ -333,25 +324,20 @@ function post (controller_url, data) {
|
|||||||
postData.append('csrf_token', core.csrf_token);
|
postData.append('csrf_token', core.csrf_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
return new Promise(function (resolve, reject) {
|
||||||
if(xhr.upload) xhr.upload.addEventListener('progress', progressHandler(Def), false);
|
$.ajax(controller_url, {
|
||||||
|
data: postData,
|
||||||
var ajaxDef = $.ajax(controller_url, {
|
processData: false,
|
||||||
xhr: function() {return xhr;},
|
contentType: false,
|
||||||
data: postData,
|
type: 'POST'
|
||||||
processData: false,
|
}).then(resolve).fail(reject);
|
||||||
contentType: false,
|
});
|
||||||
type: 'POST'
|
|
||||||
}).then(function (data) {Def.resolve(data);})
|
|
||||||
.fail(function (data) {Def.reject(data);});
|
|
||||||
|
|
||||||
return Def;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an XML file according to the given URL and adds its associated qweb
|
* Loads an XML file according to the given URL and adds its associated qweb
|
||||||
* templates to the given qweb engine. The function can also be used to get
|
* templates to the given qweb engine. The function can also be used to get
|
||||||
* the deferred which indicates when all the calls to the function are finished.
|
* the promise which indicates when all the calls to the function are finished.
|
||||||
*
|
*
|
||||||
* Note: "all the calls" = the calls that happened before the current no-args
|
* Note: "all the calls" = the calls that happened before the current no-args
|
||||||
* one + the calls that will happen after but when the previous ones are not
|
* one + the calls that will happen after but when the previous ones are not
|
||||||
@@ -359,8 +345,8 @@ function post (controller_url, data) {
|
|||||||
*
|
*
|
||||||
* @param {string} [url] - an URL where to find qweb templates
|
* @param {string} [url] - an URL where to find qweb templates
|
||||||
* @param {QWeb} [qweb] - the engine to which the templates need to be added
|
* @param {QWeb} [qweb] - the engine to which the templates need to be added
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
* If no argument is given to the function, the deferred's state
|
* If no argument is given to the function, the promise's state
|
||||||
* indicates if "all the calls" are finished (see main description).
|
* indicates if "all the calls" are finished (see main description).
|
||||||
* Otherwise, it indicates when the templates associated to the given
|
* Otherwise, it indicates when the templates associated to the given
|
||||||
* url have been loaded.
|
* url have been loaded.
|
||||||
@@ -369,63 +355,64 @@ var loadXML = (function () {
|
|||||||
// Some "static" variables associated to the loadXML function
|
// Some "static" variables associated to the loadXML function
|
||||||
var isLoading = false;
|
var isLoading = false;
|
||||||
var loadingsData = [];
|
var loadingsData = [];
|
||||||
var allLoadingsDef = $.when();
|
|
||||||
var seenURLs = [];
|
var seenURLs = [];
|
||||||
|
|
||||||
return function (url, qweb) {
|
return function (url, qweb) {
|
||||||
// If no argument, simply returns the deferred which indicates when
|
|
||||||
// "all the calls" are finished
|
|
||||||
if (!url || !qweb) {
|
|
||||||
return allLoadingsDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the given URL has already been seen, do nothing but returning the
|
|
||||||
// associated deferred
|
|
||||||
if (_.contains(seenURLs, url)) {
|
|
||||||
var oldLoadingData = _.findWhere(loadingsData, {url: url});
|
|
||||||
return oldLoadingData ? oldLoadingData.def : $.when();
|
|
||||||
}
|
|
||||||
seenURLs.push(url);
|
|
||||||
|
|
||||||
// Add the information about the new data to load: the url, the qweb
|
|
||||||
// engine and the associated deferred
|
|
||||||
var newLoadingData = {
|
|
||||||
url: url,
|
|
||||||
qweb: qweb,
|
|
||||||
def: $.Deferred(),
|
|
||||||
};
|
|
||||||
loadingsData.push(newLoadingData);
|
|
||||||
|
|
||||||
// If not already started, start the loading loop (reinitialize the
|
|
||||||
// "all the calls" deferred to an unresolved state)
|
|
||||||
if (!isLoading) {
|
|
||||||
allLoadingsDef = $.Deferred();
|
|
||||||
_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the deferred associated to the new given URL
|
|
||||||
return newLoadingData.def;
|
|
||||||
|
|
||||||
function _load() {
|
function _load() {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
if (loadingsData.length) {
|
if (loadingsData.length) {
|
||||||
// There is something to load, load it, resolve the associated
|
// There is something to load, load it, resolve the associated
|
||||||
// deferred then start loading the next one
|
// promise then start loading the next one
|
||||||
var loadingData = loadingsData[0];
|
var loadingData = loadingsData[0];
|
||||||
loadingData.qweb.add_template(loadingData.url, function () {
|
loadingData.qweb.add_template(loadingData.url, function () {
|
||||||
// Remove from array only now so that multiple calls to
|
// Remove from array only now so that multiple calls to
|
||||||
// loadXML with the same URL returns the right deferred
|
// loadXML with the same URL returns the right promise
|
||||||
loadingsData.shift();
|
loadingsData.shift();
|
||||||
loadingData.def.resolve();
|
loadingData.resolve();
|
||||||
_load();
|
_load();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// There is nothing to load anymore, so resolve the
|
// There is nothing to load anymore, so resolve the
|
||||||
// "all the calls" deferred
|
// "all the calls" promise
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
allLoadingsDef.resolve();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no argument, simply returns the promise which indicates when
|
||||||
|
// "all the calls" are finished
|
||||||
|
if (!url || !qweb) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the given URL has already been seen, do nothing but returning the
|
||||||
|
// associated promise
|
||||||
|
if (_.contains(seenURLs, url)) {
|
||||||
|
var oldLoadingData = _.findWhere(loadingsData, {url: url});
|
||||||
|
return oldLoadingData ? oldLoadingData.def : Promise.resolve();
|
||||||
|
}
|
||||||
|
seenURLs.push(url);
|
||||||
|
|
||||||
|
|
||||||
|
// Add the information about the new data to load: the url, the qweb
|
||||||
|
// engine and the associated promise
|
||||||
|
var newLoadingData = {
|
||||||
|
url: url,
|
||||||
|
qweb: qweb,
|
||||||
|
};
|
||||||
|
newLoadingData.def = new Promise(function (resolve, reject) {
|
||||||
|
newLoadingData.resolve = resolve;
|
||||||
|
newLoadingData.reject = reject;
|
||||||
|
});
|
||||||
|
loadingsData.push(newLoadingData);
|
||||||
|
|
||||||
|
// If not already started, start the loading loop (reinitialize the
|
||||||
|
// "all the calls" promise to an unresolved state)
|
||||||
|
if (!isLoading) {
|
||||||
|
_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the promise associated to the new given URL
|
||||||
|
return newLoadingData.def;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -444,7 +431,7 @@ var loadAsset = (function () {
|
|||||||
|
|
||||||
var load = function loadAsset(xmlId) {
|
var load = function loadAsset(xmlId) {
|
||||||
if (cache[xmlId]) {
|
if (cache[xmlId]) {
|
||||||
return $.when(cache[xmlId]);
|
return Promise.resolve(cache[xmlId]);
|
||||||
}
|
}
|
||||||
var params = {
|
var params = {
|
||||||
args: [xmlId, {
|
args: [xmlId, {
|
||||||
@@ -493,22 +480,20 @@ var loadAsset = (function () {
|
|||||||
* @param {Array<string>} [libs.assetLibs=[]] A list of xmlId. The loaded template
|
* @param {Array<string>} [libs.assetLibs=[]] A list of xmlId. The loaded template
|
||||||
* contains the script and link to be loaded
|
* contains the script and link to be loaded
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function loadLibs (libs) {
|
function loadLibs (libs) {
|
||||||
var defs = [];
|
var defs = [];
|
||||||
_.each(libs.jsLibs || [], function (urls) {
|
(libs.jsLibs || []).forEach(function (urls) {
|
||||||
defs.push($.when.apply($, defs).then(function () {
|
defs.push(Promise.all(defs).then(function () {
|
||||||
if (typeof(urls) === 'string') {
|
if (typeof(urls) === 'string') {
|
||||||
return ajax.loadJS(urls);
|
return ajax.loadJS(urls);
|
||||||
} else {
|
} else {
|
||||||
return $.when.apply($, _.map(urls, function (url) {
|
return Promise.all(urls.map(ajax.loadJS));
|
||||||
return ajax.loadJS(url);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
_.each(libs.cssLibs || [], function (url) {
|
(libs.cssLibs || []).forEach(function (url) {
|
||||||
defs.push(ajax.loadCSS(url));
|
defs.push(ajax.loadCSS(url));
|
||||||
});
|
});
|
||||||
_.each(libs.assetLibs || [], function (xmlId) {
|
_.each(libs.assetLibs || [], function (xmlId) {
|
||||||
@@ -516,7 +501,7 @@ function loadLibs (libs) {
|
|||||||
return loadLibs(asset);
|
return loadLibs(asset);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ajax = {
|
var ajax = {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ odoo.define('web.concurrency', function (require) {
|
|||||||
* everything concurrency related in Odoo.
|
* everything concurrency related in Odoo.
|
||||||
*
|
*
|
||||||
* The basic concurrency primitives in Odoo JS are the callback, and the
|
* The basic concurrency primitives in Odoo JS are the callback, and the
|
||||||
* promises. Promises (deferred) are more composable, so we usually use them
|
* promises. Promises (promise) are more composable, so we usually use them
|
||||||
* whenever possible. We use the jQuery implementation.
|
* whenever possible. We use the jQuery implementation.
|
||||||
*
|
*
|
||||||
* Those functions are really nothing special, but are simply the result of how
|
* Those functions are really nothing special, but are simply the result of how
|
||||||
@@ -19,54 +19,15 @@ var Class = require('web.Class');
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* The jquery implementation for $.when has a (most of the time) useful
|
* Returns a promise resolved after 'wait' milliseconds
|
||||||
* property: it is synchronous, if the deferred is resolved immediately.
|
|
||||||
*
|
|
||||||
* This means that when we execute $.when(def), then all registered
|
|
||||||
* callbacks will be executed before the next line is executed. This is
|
|
||||||
* useful quite often, but in some rare cases, we might want to force an
|
|
||||||
* async behavior. This is the purpose of this function, which simply adds a
|
|
||||||
* setTimeout before resolving the deferred.
|
|
||||||
*
|
|
||||||
* @returns {Deferred}
|
|
||||||
*/
|
|
||||||
asyncWhen: function () {
|
|
||||||
var async = false;
|
|
||||||
var def = $.Deferred();
|
|
||||||
$.when.apply($, arguments).done(function() {
|
|
||||||
var args = arguments;
|
|
||||||
var action = function() {
|
|
||||||
def.resolve.apply(def, args);
|
|
||||||
};
|
|
||||||
if (async) {
|
|
||||||
action();
|
|
||||||
} else {
|
|
||||||
setTimeout(action, 0);
|
|
||||||
}
|
|
||||||
}).fail(function() {
|
|
||||||
var args = arguments;
|
|
||||||
var action = function() {
|
|
||||||
def.reject.apply(def, args);
|
|
||||||
};
|
|
||||||
if (async) {
|
|
||||||
action();
|
|
||||||
} else {
|
|
||||||
setTimeout(action, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
async = true;
|
|
||||||
return def;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Returns a deferred resolved after 'wait' milliseconds
|
|
||||||
*
|
*
|
||||||
* @param {int} [wait=0] the delay in ms
|
* @param {int} [wait=0] the delay in ms
|
||||||
* @return {Deferred}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
delay: function (wait) {
|
delay: function (wait) {
|
||||||
var def = $.Deferred();
|
return new Promise(function (resolve) {
|
||||||
setTimeout(def.resolve, wait);
|
setTimeout(resolve, wait);
|
||||||
return def;
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* The DropMisordered abstraction is useful for situations where you have
|
* The DropMisordered abstraction is useful for situations where you have
|
||||||
@@ -93,27 +54,27 @@ return {
|
|||||||
this.failMisordered = failMisordered || false;
|
this.failMisordered = failMisordered || false;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Adds a deferred (usually an async request) to the sequencer
|
* Adds a promise (usually an async request) to the sequencer
|
||||||
*
|
*
|
||||||
* @param {Deferred} deferred to ensure add
|
* @param {Promise} promise to ensure add
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
add: function (deferred) {
|
add: function (promise) {
|
||||||
var res = $.Deferred();
|
var self = this;
|
||||||
|
var seq = this.lsn++;
|
||||||
var self = this, seq = this.lsn++;
|
var res = new Promise(function (resolve, reject) {
|
||||||
deferred.done(function () {
|
promise.then(function (result) {
|
||||||
if (seq > self.rsn) {
|
if (seq > self.rsn) {
|
||||||
self.rsn = seq;
|
self.rsn = seq;
|
||||||
res.resolve.apply(res, arguments);
|
resolve(result);
|
||||||
} else if (self.failMisordered) {
|
} else if (self.failMisordered) {
|
||||||
res.reject();
|
reject();
|
||||||
}
|
}
|
||||||
}).fail(function () {
|
}).guardedCatch(function (result) {
|
||||||
res.reject.apply(res, arguments);
|
reject(result);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
return res;
|
||||||
return res.promise();
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
@@ -152,17 +113,26 @@ return {
|
|||||||
*/
|
*/
|
||||||
DropPrevious: Class.extend({
|
DropPrevious: Class.extend({
|
||||||
/**
|
/**
|
||||||
* Registers a new deferred and rejects the previous one
|
* Registers a new promise and rejects the previous one
|
||||||
*
|
*
|
||||||
* @param {Deferred} deferred the new deferred
|
* @param {Promise} promise the new promise
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
add: function (deferred) {
|
add: function (promise) {
|
||||||
if (this.current_def) { this.current_def.reject(); }
|
if (this.currentDef) {
|
||||||
var res = $.Deferred();
|
this.currentDef.reject();
|
||||||
deferred.then(res.resolve, res.reject);
|
}
|
||||||
this.current_def = res;
|
var rejection;
|
||||||
return res.promise();
|
var res = new Promise(function (resolve, reject) {
|
||||||
|
rejection = reject;
|
||||||
|
promise.then(resolve).catch(function (reason) {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.currentDef = res;
|
||||||
|
this.currentDef.reject = rejection;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
@@ -171,7 +141,7 @@ return {
|
|||||||
* state and cause some corrupted state.
|
* state and cause some corrupted state.
|
||||||
*
|
*
|
||||||
* Imagine that we have a function to fetch some data _load(), which returns
|
* Imagine that we have a function to fetch some data _load(), which returns
|
||||||
* a deferred which resolves to something useful. Now, we have some code
|
* a promise which resolves to something useful. Now, we have some code
|
||||||
* looking like this::
|
* looking like this::
|
||||||
*
|
*
|
||||||
* return this._load().then(function (result) {
|
* return this._load().then(function (result) {
|
||||||
@@ -200,37 +170,51 @@ return {
|
|||||||
*/
|
*/
|
||||||
Mutex: Class.extend({
|
Mutex: Class.extend({
|
||||||
init: function () {
|
init: function () {
|
||||||
this.def = $.Deferred().resolve();
|
this.lock = Promise.resolve();
|
||||||
this.unlockedDef = undefined;
|
this.queueSize = 0;
|
||||||
|
this.unlockedProm = undefined;
|
||||||
|
this._unlock = undefined;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Add a computation to the queue, it will be executed as soon as the
|
* Add a computation to the queue, it will be executed as soon as the
|
||||||
* previous computations are completed.
|
* previous computations are completed.
|
||||||
*
|
*
|
||||||
* @param {function} action a function which may return a deferred
|
* @param {function} action a function which may return a Promise
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
exec: function (action) {
|
exec: function (action) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var current = this.def;
|
var currentLock = this.lock;
|
||||||
var next = this.def = $.Deferred();
|
var result;
|
||||||
this.unlockedDef = this.unlockedDef || $.Deferred();
|
this.queueSize++;
|
||||||
return current.then(function() {
|
this.unlockedProm = this.unlockedProm || new Promise(function (resolve) {
|
||||||
return $.when(action()).always(function () {
|
self._unlock = resolve;
|
||||||
next.resolve();
|
});
|
||||||
if (self.def.state() === 'resolved' && self.unlockedDef) {
|
this.lock = new Promise(function (unlockCurrent) {
|
||||||
self.unlockedDef.resolve();
|
currentLock.then(function () {
|
||||||
self.unlockedDef = undefined;
|
result = action();
|
||||||
}
|
var always = function (returnedResult) {
|
||||||
|
unlockCurrent();
|
||||||
|
self.queueSize--;
|
||||||
|
if (self.queueSize === 0) {
|
||||||
|
self.unlockedProm = undefined;
|
||||||
|
self._unlock();
|
||||||
|
}
|
||||||
|
return returnedResult;
|
||||||
|
};
|
||||||
|
Promise.resolve(result).then(always).guardedCatch(always);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return this.lock.then(function () {
|
||||||
|
return result;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @returns {Promise} resolved as soon as the Mutex is unlocked
|
* @returns {Promise} resolved as soon as the Mutex is unlocked
|
||||||
* (directly if it is currently idle)
|
* (directly if it is currently idle)
|
||||||
*/
|
*/
|
||||||
getUnlockedDef: function () {
|
getUnlockedDef: function () {
|
||||||
return $.when(this.unlockedDef);
|
return this.unlockedProm || Promise.resolve();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
@@ -253,65 +237,86 @@ return {
|
|||||||
*
|
*
|
||||||
* A MutexedDropPrevious has to be a class, and not a function, because we
|
* A MutexedDropPrevious has to be a class, and not a function, because we
|
||||||
* have to keep track of some internal state. The exec function takes as
|
* have to keep track of some internal state. The exec function takes as
|
||||||
* argument an action (and not a deferred as DropPrevious for example)
|
* argument an action (and not a promise as DropPrevious for example)
|
||||||
* because it's the MutexedDropPrevious role to trigger the RPC call that
|
* because it's the MutexedDropPrevious role to trigger the RPC call that
|
||||||
* returns a deferred when it's time.
|
* returns a promise when it's time.
|
||||||
*/
|
*/
|
||||||
MutexedDropPrevious: Class.extend({
|
MutexedDropPrevious: Class.extend({
|
||||||
init: function () {
|
init: function () {
|
||||||
this.currentDef = null;
|
|
||||||
this.locked = false;
|
this.locked = false;
|
||||||
|
this.currentProm = null;
|
||||||
this.pendingAction = null;
|
this.pendingAction = null;
|
||||||
this.pendingDef = null;
|
this.pendingProm = null;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {function} action a function which may return a deferred
|
* @param {function} action a function which may return a promise
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
exec: function (action) {
|
exec: function (action) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var resolution;
|
||||||
|
var rejection;
|
||||||
if (this.locked) {
|
if (this.locked) {
|
||||||
this.pendingAction = action;
|
this.pendingAction = action;
|
||||||
var oldPendingDef = this.pendingDef;
|
var oldPendingDef = this.pendingProm;
|
||||||
var pendingDef = this.pendingDef = $.Deferred();
|
|
||||||
if (oldPendingDef) {
|
this.pendingProm = new Promise(function (resolve, reject) {
|
||||||
oldPendingDef.reject();
|
resolution = resolve;
|
||||||
}
|
rejection = reject;
|
||||||
this.currentDef.reject();
|
if (oldPendingDef) {
|
||||||
return pendingDef.promise();
|
oldPendingDef.reject();
|
||||||
|
}
|
||||||
|
self.currentProm.reject();
|
||||||
|
});
|
||||||
|
this.pendingProm.resolve = resolution;
|
||||||
|
this.pendingProm.reject = rejection;
|
||||||
|
return this.pendingProm;
|
||||||
} else {
|
} else {
|
||||||
this.locked = true;
|
this.locked = true;
|
||||||
this.currentDef = $.Deferred();
|
this.currentProm = new Promise(function (resolve, reject) {
|
||||||
$.when(action())
|
resolution = resolve;
|
||||||
.then(this.currentDef.resolve.bind(this.currentDef))
|
rejection = reject;
|
||||||
.fail(this.currentDef.reject.bind(this.currentDef))
|
function unlock() {
|
||||||
.always(function () {
|
|
||||||
self.locked = false;
|
self.locked = false;
|
||||||
if (self.pendingAction) {
|
if (self.pendingAction) {
|
||||||
var action = self.pendingAction;
|
var action = self.pendingAction;
|
||||||
|
var prom = self.pendingProm;
|
||||||
self.pendingAction = null;
|
self.pendingAction = null;
|
||||||
|
self.pendingProm = null;
|
||||||
self.exec(action)
|
self.exec(action)
|
||||||
.then(self.pendingDef.resolve.bind(self.pendingDef))
|
.then(prom.resolve)
|
||||||
.fail(self.pendingDef.reject.bind(self.pendingDef));
|
.guardedCatch(prom.reject);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Promise.resolve(action())
|
||||||
|
.then(function (result) {
|
||||||
|
resolve(result);
|
||||||
|
unlock();
|
||||||
|
})
|
||||||
|
.guardedCatch(function (reason) {
|
||||||
|
reject(reason);
|
||||||
|
unlock();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return this.currentDef.promise();
|
this.currentProm.resolve = resolution;
|
||||||
|
this.currentProm.reject = rejection;
|
||||||
|
return this.currentProm;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
* Rejects a deferred as soon as a reference deferred is either resolved or
|
* Rejects a promise as soon as a reference promise is either resolved or
|
||||||
* rejected
|
* rejected
|
||||||
*
|
*
|
||||||
* @param {Deferred} [target_def] the deferred to potentially reject
|
* @param {Promise} [target_def] the promise to potentially reject
|
||||||
* @param {Deferred} [reference_def] the reference target
|
* @param {Promise} [reference_def] the reference target
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
rejectAfter: function (target_def, reference_def) {
|
rejectAfter: function (target_def, reference_def) {
|
||||||
var res = $.Deferred();
|
return new Promise(function (resolve, reject) {
|
||||||
target_def.then(res.resolve, res.reject);
|
target_def.then(resolve).guardedCatch(reject);
|
||||||
reference_def.always(res.reject);
|
reference_def.then(reject).guardedCatch(reject);
|
||||||
return res.promise();
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ var Dialog = Widget.extend({
|
|||||||
* (use this for non-editor frontend features)
|
* (use this for non-editor frontend features)
|
||||||
*/
|
*/
|
||||||
init: function (parent, options) {
|
init: function (parent, options) {
|
||||||
|
var self = this;
|
||||||
this._super(parent);
|
this._super(parent);
|
||||||
this._opened = $.Deferred();
|
this._opened = new Promise(function (resolve) {
|
||||||
|
self._openedResolver = resolve;
|
||||||
|
});
|
||||||
options = _.defaults(options || {}, {
|
options = _.defaults(options || {}, {
|
||||||
title: _t('Odoo'), subtitle: '',
|
title: _t('Odoo'), subtitle: '',
|
||||||
size: 'large',
|
size: 'large',
|
||||||
@@ -140,7 +142,7 @@ var Dialog = Widget.extend({
|
|||||||
def = buttonData.click.call(self, e);
|
def = buttonData.click.call(self, e);
|
||||||
}
|
}
|
||||||
if (buttonData.close) {
|
if (buttonData.close) {
|
||||||
$.when(def).always(self.close.bind(self));
|
Promise.resolve(def).then(self.close.bind(self)).guardedCatch(self.close.bind(self));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (self.technical) {
|
if (self.technical) {
|
||||||
@@ -185,7 +187,7 @@ var Dialog = Widget.extend({
|
|||||||
self.$modal.attr('open', true);
|
self.$modal.attr('open', true);
|
||||||
self.$modal.removeAttr("aria-hidden");
|
self.$modal.removeAttr("aria-hidden");
|
||||||
self.$modal.modal('show');
|
self.$modal.modal('show');
|
||||||
self._opened.resolve();
|
self._openedResolver();
|
||||||
if (options && options.shouldFocusButtons) {
|
if (options && options.shouldFocusButtons) {
|
||||||
self._onFocusControlButton();
|
self._onFocusControlButton();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
odoo.define('web.dom_ready', function (require) {
|
odoo.define('web.dom_ready', function (require) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var def = $.Deferred();
|
return new Promise(function (resolve, reject) {
|
||||||
$(def.resolve.bind(def));
|
$(resolve);
|
||||||
return def;
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
odoo.define('web.dom', function (require) {
|
odoo.define('web.dom', function (require) {
|
||||||
@@ -33,7 +32,7 @@ var _t = core._t;
|
|||||||
* that on_attach_callback() will be called on each w with arguments args
|
* that on_attach_callback() will be called on each w with arguments args
|
||||||
*/
|
*/
|
||||||
function _notify(content, callbacks) {
|
function _notify(content, callbacks) {
|
||||||
_.each(callbacks, function (c) {
|
callbacks.forEach(function (c) {
|
||||||
if (c.widget && c.widget.on_attach_callback) {
|
if (c.widget && c.widget.on_attach_callback) {
|
||||||
c.widget.on_attach_callback(c.callback_args);
|
c.widget.on_attach_callback(c.callback_args);
|
||||||
}
|
}
|
||||||
@@ -170,14 +169,14 @@ var dom = {
|
|||||||
* @return {jQuery} the detached elements
|
* @return {jQuery} the detached elements
|
||||||
*/
|
*/
|
||||||
detach: function (to_detach, options) {
|
detach: function (to_detach, options) {
|
||||||
_.each(to_detach, function (d) {
|
to_detach.forEach( function (d) {
|
||||||
if (d.widget.on_detach_callback) {
|
if (d.widget.on_detach_callback) {
|
||||||
d.widget.on_detach_callback(d.callback_args);
|
d.widget.on_detach_callback(d.callback_args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var $to_detach = options && options.$to_detach;
|
var $to_detach = options && options.$to_detach;
|
||||||
if (!$to_detach) {
|
if (!$to_detach) {
|
||||||
$to_detach = $(_.map(to_detach, function (d) {
|
$to_detach = $(to_detach.map(function (d) {
|
||||||
return d.widget.el;
|
return d.widget.el;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -229,7 +228,7 @@ var dom = {
|
|||||||
* @param {function|boolean} stopPropagation
|
* @param {function|boolean} stopPropagation
|
||||||
*/
|
*/
|
||||||
makeAsyncHandler: function (fct, preventDefault, stopPropagation) {
|
makeAsyncHandler: function (fct, preventDefault, stopPropagation) {
|
||||||
// Create a deferred indicating if a previous call to this handler is
|
// Create a promise indicating if a previous call to this handler is
|
||||||
// still pending.
|
// still pending.
|
||||||
var def = $.when();
|
var def = $.when();
|
||||||
|
|
||||||
@@ -284,11 +283,13 @@ var dom = {
|
|||||||
// a 'real' debounce creation useless. Also, during the debouncing
|
// a 'real' debounce creation useless. Also, during the debouncing
|
||||||
// part, the button is disabled without any visual effect.
|
// part, the button is disabled without any visual effect.
|
||||||
$button.addClass('o_debounce_disabled');
|
$button.addClass('o_debounce_disabled');
|
||||||
$.when(dom.DEBOUNCE && concurrency.delay(dom.DEBOUNCE)).then(function () {
|
Promise.resolve(dom.DEBOUNCE && concurrency.delay(dom.DEBOUNCE)).then(function () {
|
||||||
$button.addClass('disabled').prop('disabled', true);
|
$button.addClass('disabled').prop('disabled', true);
|
||||||
$button.removeClass('o_debounce_disabled');
|
$button.removeClass('o_debounce_disabled');
|
||||||
|
|
||||||
return $.when(result).always(function () {
|
return Promise.resolve(result).then(function () {
|
||||||
|
$button.removeClass('disabled').prop('disabled', false);
|
||||||
|
}).guardedCatch(function () {
|
||||||
$button.removeClass('disabled').prop('disabled', false);
|
$button.removeClass('disabled').prop('disabled', false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ function redirect (url, wait) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var wait_server = function() {
|
var wait_server = function() {
|
||||||
ajax.rpc("/web/webclient/version_info", {}).done(load).fail(function() {
|
ajax.rpc("/web/webclient/version_info", {}).then(load).guardedCatch(function () {
|
||||||
setTimeout(wait_server, 250);
|
setTimeout(wait_server, 250);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -172,7 +172,7 @@ core.action_registry.add("login", login);
|
|||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
redirect('/web/session/logout');
|
redirect('/web/session/logout');
|
||||||
return $.Deferred();
|
return new Promise();
|
||||||
}
|
}
|
||||||
core.action_registry.add("logout", logout);
|
core.action_registry.add("logout", logout);
|
||||||
|
|
||||||
|
|||||||
@@ -62,37 +62,36 @@ var ParentedMixin = {
|
|||||||
* Utility method to only execute asynchronous actions if the current
|
* Utility method to only execute asynchronous actions if the current
|
||||||
* object has not been destroyed.
|
* object has not been destroyed.
|
||||||
*
|
*
|
||||||
* @param {$.Deferred} promise The promise representing the asynchronous
|
* @param {Promise} promise The promise representing the asynchronous
|
||||||
* action.
|
* action.
|
||||||
* @param {bool} [reject=false] If true, the returned promise will be
|
* @param {bool} [shouldReject=false] If true, the returned promise will be
|
||||||
* rejected with no arguments if the current
|
* rejected with no arguments if the current
|
||||||
* object is destroyed. If false, the
|
* object is destroyed. If false, the
|
||||||
* returned promise will never be resolved
|
* returned promise will never be resolved
|
||||||
* or rejected.
|
* or rejected.
|
||||||
* @returns {$.Deferred} A promise that will mirror the given promise if
|
* @returns {Promise} A promise that will mirror the given promise if
|
||||||
* everything goes fine but will either be rejected
|
* everything goes fine but will either be rejected
|
||||||
* with no arguments or never resolved if the
|
* with no arguments or never resolved if the
|
||||||
* current object is destroyed.
|
* current object is destroyed.
|
||||||
*/
|
*/
|
||||||
alive: function (promise, reject) {
|
alive: function (promise, shouldReject) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return $.Deferred(function (def) {
|
|
||||||
promise.then(function () {
|
return new Promise(function (resolve, reject) {
|
||||||
|
promise.then(function (result) {
|
||||||
if (!self.isDestroyed()) {
|
if (!self.isDestroyed()) {
|
||||||
def.resolve.apply(def, arguments);
|
resolve(result);
|
||||||
|
} else if (shouldReject) {
|
||||||
|
reject();
|
||||||
}
|
}
|
||||||
}, function () {
|
}).guardedCatch(function (reason) {
|
||||||
if (!self.isDestroyed()) {
|
if (!self.isDestroyed()) {
|
||||||
def.reject.apply(def, arguments);
|
reject(reason);
|
||||||
|
} else if (shouldReject) {
|
||||||
|
reject();
|
||||||
}
|
}
|
||||||
}).always(function () {
|
|
||||||
if (reject) {
|
|
||||||
// noop if def already resolved or rejected
|
|
||||||
def.reject();
|
|
||||||
}
|
|
||||||
// otherwise leave promise in limbo
|
|
||||||
});
|
});
|
||||||
}).promise();
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Inform the object it should destroy itself, releasing any
|
* Inform the object it should destroy itself, releasing any
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ var Model = Class.extend(mixins.EventDispatcherMixin, ServicesMixin, {
|
|||||||
* @returns {Promise} The promise resolves to some kind of handle
|
* @returns {Promise} The promise resolves to some kind of handle
|
||||||
*/
|
*/
|
||||||
load: function () {
|
load: function () {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -119,9 +119,9 @@ var Controller = Widget.extend({
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
return $.when(
|
return Promise.all(
|
||||||
this._super.apply(this, arguments),
|
[this._super.apply(this, arguments),
|
||||||
this._startRenderer()
|
this._startRenderer()]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -177,7 +177,8 @@ var Factory = Class.extend({
|
|||||||
getController: function (parent) {
|
getController: function (parent) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var model = this.getModel(parent);
|
var model = this.getModel(parent);
|
||||||
return $.when(this._loadData(model), ajax.loadLibs(this)).then(function (state) {
|
return Promise.all([this._loadData(model), ajax.loadLibs(this)]).then(function (result) {
|
||||||
|
var state = result[0];
|
||||||
var renderer = self.getRenderer(parent, state);
|
var renderer = self.getRenderer(parent, state);
|
||||||
var Controller = self.Controller || self.config.Controller;
|
var Controller = self.Controller || self.config.Controller;
|
||||||
var controllerParams = _.extend({
|
var controllerParams = _.extend({
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ return {
|
|||||||
*
|
*
|
||||||
* @param {Object} params @see buildQuery for a description
|
* @param {Object} params @see buildQuery for a description
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @returns {Deferred<any>}
|
* @returns {Promise<any>}
|
||||||
*/
|
*/
|
||||||
query: function (params, options) {
|
query: function (params, options) {
|
||||||
var query = this.buildQuery(params);
|
var query = this.buildQuery(params);
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ var ServicesMixin = {
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Builds and executes RPC query. Returns a deferred's promise resolved with
|
* Builds and executes RPC query. Returns a promise resolved with
|
||||||
* the RPC result.
|
* the RPC result.
|
||||||
*
|
*
|
||||||
* @param {string} params either a route or a model
|
* @param {string} params either a route or a model
|
||||||
@@ -133,11 +133,17 @@ var ServicesMixin = {
|
|||||||
*/
|
*/
|
||||||
_rpc: function (params, options) {
|
_rpc: function (params, options) {
|
||||||
var query = rpc.buildQuery(params);
|
var query = rpc.buildQuery(params);
|
||||||
var def = this.call('ajax', 'rpc', query.route, query.params, options, this) || $.Deferred();
|
var prom = this.call('ajax', 'rpc', query.route, query.params, options, this);
|
||||||
var promise = def.promise();
|
if (!prom) {
|
||||||
var abort = (def.abort ? def.abort : def.reject) || function () {};
|
prom = new Promise(function () {});
|
||||||
promise.abort = abort.bind(def);
|
prom.abort = function () {};
|
||||||
return promise;
|
}
|
||||||
|
var abort = prom.abort ? prom.abort : prom.reject;
|
||||||
|
if (!abort) {
|
||||||
|
throw new Error("a rpc promise should always have a reject function");
|
||||||
|
}
|
||||||
|
prom.abort = abort.bind(prom);
|
||||||
|
return prom;
|
||||||
},
|
},
|
||||||
loadFieldView: function (modelName, context, view_id, view_type, options) {
|
loadFieldView: function (modelName, context, view_id, view_type, options) {
|
||||||
return this.loadViews(modelName, context, [[view_id, view_type]], options).then(function (result) {
|
return this.loadViews(modelName, context, [[view_id, view_type]], options).then(function (result) {
|
||||||
@@ -145,41 +151,45 @@ var ServicesMixin = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadViews: function (modelName, context, views, options) {
|
loadViews: function (modelName, context, views, options) {
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
this.trigger_up('load_views', {
|
return new Promise(function (resolve) {
|
||||||
modelName: modelName,
|
self.trigger_up('load_views', {
|
||||||
context: context,
|
modelName: modelName,
|
||||||
views: views,
|
context: context,
|
||||||
options: options,
|
views: views,
|
||||||
on_success: def.resolve.bind(def),
|
options: options,
|
||||||
|
on_success: resolve,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
loadFilters: function (modelName, actionId, context) {
|
loadFilters: function (modelName, actionId, context) {
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
this.trigger_up('load_filters', {
|
return new Promise(function (resolve, reject) {
|
||||||
modelName: modelName,
|
self.trigger_up('load_filters', {
|
||||||
actionId: actionId,
|
modelName: modelName,
|
||||||
context: context,
|
actionId: actionId,
|
||||||
on_success: def.resolve.bind(def),
|
context: context,
|
||||||
|
on_success: resolve,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
createFilter: function (filter) {
|
createFilter: function (filter) {
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
this.trigger_up('create_filter', {
|
return new Promise(function (resolve, reject) {
|
||||||
filter: filter,
|
self.trigger_up('create_filter', {
|
||||||
on_success: def.resolve.bind(def),
|
filter: filter,
|
||||||
|
on_success: resolve,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
deleteFilter: function (filterId) {
|
deleteFilter: function (filterId) {
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
this.trigger_up('delete_filter', {
|
return new Promise(function (resolve, reject) {
|
||||||
filterId: filterId,
|
self.trigger_up('delete_filter', {
|
||||||
on_success: def.resolve.bind(def),
|
filterId: filterId,
|
||||||
|
on_success: resolve,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
// Session stuff
|
// Session stuff
|
||||||
getSession: function () {
|
getSession: function () {
|
||||||
@@ -195,22 +205,22 @@ var ServicesMixin = {
|
|||||||
* Informs the action manager to do an action. This supposes that the action
|
* Informs the action manager to do an action. This supposes that the action
|
||||||
* manager can be found amongst the ancestors of the current widget.
|
* manager can be found amongst the ancestors of the current widget.
|
||||||
* If that's not the case this method will simply return an unresolved
|
* If that's not the case this method will simply return an unresolved
|
||||||
* deferred.
|
* promise.
|
||||||
*
|
*
|
||||||
* @param {any} action
|
* @param {any} action
|
||||||
* @param {any} options
|
* @param {any} options
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
do_action: function (action, options) {
|
do_action: function (action, options) {
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
this.trigger_up('do_action', {
|
self.trigger_up('do_action', {
|
||||||
action: action,
|
action: action,
|
||||||
options: options,
|
options: options,
|
||||||
on_success: function (result) { def.resolve(result); },
|
on_success: resolve,
|
||||||
on_fail: function (result) { def.reject(result); },
|
on_fail: reject,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
do_notify: function (title, message, sticky, className) {
|
do_notify: function (title, message, sticky, className) {
|
||||||
return this.call('notification', 'notify', {title: title, message: message, sticky: sticky, className: className});
|
return this.call('notification', 'notify', {title: title, message: message, sticky: sticky, className: className});
|
||||||
|
|||||||
@@ -84,27 +84,27 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
*/
|
*/
|
||||||
session_init: function () {
|
session_init: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def = this.session_reload();
|
var prom = this.session_reload();
|
||||||
|
|
||||||
if (this.is_frontend) {
|
if (this.is_frontend) {
|
||||||
return def.then(function () {
|
return prom.then(function () {
|
||||||
return self.load_translations();
|
return self.load_translations();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return def.then(function () {
|
return prom.then(function () {
|
||||||
var modules = self.module_list.join(',');
|
var modules = self.module_list.join(',');
|
||||||
var deferred = self.load_qweb(modules);
|
var promise = self.load_qweb(modules);
|
||||||
if (self.session_is_valid()) {
|
if (self.session_is_valid()) {
|
||||||
return deferred.then(function () { return self.load_modules(); });
|
return promise.then(function () { return self.load_modules(); });
|
||||||
}
|
}
|
||||||
return $.when(
|
return Promise.all([
|
||||||
deferred,
|
promise,
|
||||||
self.rpc('/web/webclient/bootstrap_translations', {mods: self.module_list})
|
self.rpc('/web/webclient/bootstrap_translations', {mods: self.module_list})
|
||||||
.then(function (trans) {
|
.then(function (trans) {
|
||||||
_t.database.set_bundle(trans);
|
_t.database.set_bundle(trans);
|
||||||
})
|
})
|
||||||
);
|
]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
session_is_valid: function () {
|
session_is_valid: function () {
|
||||||
@@ -119,7 +119,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
*/
|
*/
|
||||||
session_authenticate: function () {
|
session_authenticate: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
return $.when(this._session_authenticate.apply(this, arguments)).then(function () {
|
return Promise.resolve(this._session_authenticate.apply(this, arguments)).then(function () {
|
||||||
return self.load_modules();
|
return self.load_modules();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -131,7 +131,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
var params = {db: db, login: login, password: password};
|
var params = {db: db, login: login, password: password};
|
||||||
return this.rpc("/web/session/authenticate", params).then(function (result) {
|
return this.rpc("/web/session/authenticate", params).then(function (result) {
|
||||||
if (!result.uid) {
|
if (!result.uid) {
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
_.extend(self, result);
|
_.extend(self, result);
|
||||||
});
|
});
|
||||||
@@ -142,7 +142,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
},
|
},
|
||||||
user_has_group: function (group) {
|
user_has_group: function (group) {
|
||||||
if (!this.uid) {
|
if (!this.uid) {
|
||||||
return $.when(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
var def = this._groups_def[group];
|
var def = this._groups_def[group];
|
||||||
if (!def) {
|
if (!def) {
|
||||||
@@ -196,22 +196,24 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
var to_load = _.difference(modules, self.module_list).join(',');
|
var to_load = _.difference(modules, self.module_list).join(',');
|
||||||
this.module_list = all_modules;
|
this.module_list = all_modules;
|
||||||
|
|
||||||
var loaded = $.when(self.load_translations());
|
var loaded = Promise.resolve(self.load_translations());
|
||||||
var locale = "/web/webclient/locale/" + self.user_context.lang || 'en_US';
|
var locale = "/web/webclient/locale/" + self.user_context.lang || 'en_US';
|
||||||
var file_list = [ locale ];
|
var file_list = [ locale ];
|
||||||
if(to_load.length) {
|
if(to_load.length) {
|
||||||
loaded = $.when(
|
loaded = Promise.all([
|
||||||
loaded,
|
loaded,
|
||||||
self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)),
|
self.rpc('/web/webclient/csslist', {mods: to_load})
|
||||||
|
.then(self.load_css.bind(self)),
|
||||||
self.load_qweb(to_load),
|
self.load_qweb(to_load),
|
||||||
self.rpc('/web/webclient/jslist', {mods: to_load}).done(function (files) {
|
self.rpc('/web/webclient/jslist', {mods: to_load})
|
||||||
file_list = file_list.concat(files);
|
.then(function (files) {
|
||||||
})
|
file_list = file_list.concat(files);
|
||||||
);
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
return loaded.then(function () {
|
return loaded.then(function () {
|
||||||
return self.load_js(file_list);
|
return self.load_js(file_list);
|
||||||
}).done(function () {
|
}).then(function () {
|
||||||
self.on_modules_loaded();
|
self.on_modules_loaded();
|
||||||
self.trigger('module_loaded');
|
self.trigger('module_loaded');
|
||||||
});
|
});
|
||||||
@@ -227,24 +229,24 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
},
|
},
|
||||||
load_js: function (files) {
|
load_js: function (files) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var d = $.Deferred();
|
return new Promise(function (resolve, reject) {
|
||||||
if (files.length !== 0) {
|
if (files.length !== 0) {
|
||||||
var file = files.shift();
|
var file = files.shift();
|
||||||
var url = self.url(file, null);
|
var url = self.url(file, null);
|
||||||
ajax.loadJS(url).done(d.resolve);
|
ajax.loadJS(url).then(resolve);
|
||||||
} else {
|
} else {
|
||||||
d.resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
return d;
|
});
|
||||||
},
|
},
|
||||||
load_qweb: function (mods) {
|
load_qweb: function (mods) {
|
||||||
this.qweb_mutex.exec(function () {
|
var lock = this.qweb_mutex.exec(function () {
|
||||||
return $.get('/web/webclient/qweb?mods=' + mods).then(function (doc) {
|
return $.get('/web/webclient/qweb?mods=' + mods).then(function (doc) {
|
||||||
if (!doc) { return; }
|
if (!doc) { return; }
|
||||||
qweb.add_template(doc);
|
qweb.add_template(doc);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return this.qweb_mutex.def;
|
return lock;
|
||||||
},
|
},
|
||||||
on_modules_loaded: function () {
|
on_modules_loaded: function () {
|
||||||
var openerp = window.openerp;
|
var openerp = window.openerp;
|
||||||
@@ -276,12 +278,12 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
* (re)loads the content of a session: db name, username, user id, session
|
* (re)loads the content of a session: db name, username, user id, session
|
||||||
* context and status of the support contract
|
* context and status of the support contract
|
||||||
*
|
*
|
||||||
* @returns {$.Deferred} deferred indicating the session is done reloading
|
* @returns {Promise} promise indicating the session is done reloading
|
||||||
*/
|
*/
|
||||||
session_reload: function () {
|
session_reload: function () {
|
||||||
var result = _.extend({}, window.odoo.session_info);
|
var result = _.extend({}, window.odoo.session_info);
|
||||||
_.extend(this, result);
|
_.extend(this, result);
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Executes an RPC call, registering the provided callbacks.
|
* Executes an RPC call, registering the provided callbacks.
|
||||||
@@ -293,7 +295,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
* @param {String} url RPC endpoint
|
* @param {String} url RPC endpoint
|
||||||
* @param {Object} params call parameters
|
* @param {Object} params call parameters
|
||||||
* @param {Object} options additional options for rpc call
|
* @param {Object} options additional options for rpc call
|
||||||
* @returns {jQuery.Deferred} jquery-provided ajax deferred
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
rpc: function (url, params, options) {
|
rpc: function (url, params, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@@ -58,14 +58,14 @@ var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase#
|
|||||||
@param {Object} [lang] lang The language. If not specified it will default to the language
|
@param {Object} [lang] lang The language. If not specified it will default to the language
|
||||||
of the current user.
|
of the current user.
|
||||||
@param {string} [url='/web/webclient/translations']
|
@param {string} [url='/web/webclient/translations']
|
||||||
@returns {jQuery.Deferred}
|
@returns {Promise}
|
||||||
*/
|
*/
|
||||||
load_translations: function(session, modules, lang, url) {
|
load_translations: function(session, modules, lang, url) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return session.rpc(url || '/web/webclient/translations', {
|
return session.rpc(url || '/web/webclient/translations', {
|
||||||
"mods": modules || null,
|
"mods": modules || null,
|
||||||
"lang": lang || null
|
"lang": lang || null
|
||||||
}).done(function(trans) {
|
}).then(function (trans) {
|
||||||
self.set_bundle(trans);
|
self.set_bundle(trans);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ var ServicesMixin = require('web.ServicesMixin');
|
|||||||
* },
|
* },
|
||||||
* willStart: function () {
|
* willStart: function () {
|
||||||
* // async work that need to be done before the widget is ready
|
* // async work that need to be done before the widget is ready
|
||||||
* // this method should return a deferred
|
* // this method should return a promise
|
||||||
* },
|
* },
|
||||||
* start: function() {
|
* start: function() {
|
||||||
* // stuff you want to make after the rendering, `this.$el` holds a correct value
|
* // stuff you want to make after the rendering, `this.$el` holds a correct value
|
||||||
@@ -135,45 +135,47 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* Method called between @see init and @see start. Performs asynchronous
|
* Method called between @see init and @see start. Performs asynchronous
|
||||||
* calls required by the rendering and the start method.
|
* calls required by the rendering and the start method.
|
||||||
*
|
*
|
||||||
* This method should return a Deferred which is resolved when start can be
|
* This method should return a Promose which is resolved when start can be
|
||||||
* executed.
|
* executed.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
willStart: function () {
|
willStart: function () {
|
||||||
var defs = [];
|
var proms = [];
|
||||||
if (this.xmlDependencies) {
|
if (this.xmlDependencies) {
|
||||||
defs.push.apply(defs, _.map(this.xmlDependencies, function (xmlPath) {
|
proms.push.apply(proms, _.map(this.xmlDependencies, function (xmlPath) {
|
||||||
return ajax.loadXML(xmlPath, core.qweb);
|
return ajax.loadXML(xmlPath, core.qweb);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (this.jsLibs || this.cssLibs || this.assetLibs) {
|
if (this.jsLibs || this.cssLibs || this.assetLibs) {
|
||||||
defs.push(ajax.loadLibs(this));
|
proms.push(ajax.loadLibs(this));
|
||||||
}
|
}
|
||||||
return $.when.apply($, defs);
|
return Promise.all(proms);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Method called after rendering. Mostly used to bind actions, perform
|
* Method called after rendering. Mostly used to bind actions, perform
|
||||||
* asynchronous calls, etc...
|
* asynchronous calls, etc...
|
||||||
*
|
*
|
||||||
* By convention, this method should return an object that can be passed to
|
* By convention, this method should return an object that can be passed to
|
||||||
* $.when() to inform the caller when this widget has been initialized.
|
* Promise.resolve() to inform the caller when this widget has been initialized.
|
||||||
*
|
*
|
||||||
* Note that, for historic reasons, many widgets still do work in the start
|
* Note that, for historic reasons, many widgets still do work in the start
|
||||||
* method that would be more suited to the willStart method.
|
* method that would be more suited to the willStart method.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Destroys the current widget, also destroys all its children before
|
* Destroys the current widget, also destroys all its children before
|
||||||
* destroying itself.
|
* destroying itself.
|
||||||
*/
|
*/
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
_.invoke(this.getChildren(), 'destroy');
|
this.getChildren().forEach(function (child) {
|
||||||
if(this.$el) {
|
child.destroy();
|
||||||
|
});
|
||||||
|
if (this.$el) {
|
||||||
this.$el.remove();
|
this.$el.remove();
|
||||||
}
|
}
|
||||||
mixins.PropertiesMixin.destroy.call(this);
|
mixins.PropertiesMixin.destroy.call(this);
|
||||||
@@ -187,6 +189,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* Renders the current widget and appends it to the given jQuery object.
|
* Renders the current widget and appends it to the given jQuery object.
|
||||||
*
|
*
|
||||||
* @param {jQuery} target
|
* @param {jQuery} target
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
appendTo: function (target) {
|
appendTo: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -198,6 +201,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* Attach the current widget to a dom element
|
* Attach the current widget to a dom element
|
||||||
*
|
*
|
||||||
* @param {jQuery} target
|
* @param {jQuery} target
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
attachTo: function (target) {
|
attachTo: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -234,6 +238,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* object.
|
* object.
|
||||||
*
|
*
|
||||||
* @param {jQuery} target
|
* @param {jQuery} target
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
insertAfter: function (target) {
|
insertAfter: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -246,6 +251,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* object.
|
* object.
|
||||||
*
|
*
|
||||||
* @param {jQuery} target
|
* @param {jQuery} target
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
insertBefore: function (target) {
|
insertBefore: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -257,6 +263,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* Renders the current widget and prepends it to the given jQuery object.
|
* Renders the current widget and prepends it to the given jQuery object.
|
||||||
*
|
*
|
||||||
* @param {jQuery} target
|
* @param {jQuery} target
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
prependTo: function (target) {
|
prependTo: function (target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -282,6 +289,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* Renders the current widget and replaces the given jQuery object.
|
* Renders the current widget and replaces the given jQuery object.
|
||||||
*
|
*
|
||||||
* @param target A jQuery object or a Widget instance.
|
* @param target A jQuery object or a Widget instance.
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
replace: function (target) {
|
replace: function (target) {
|
||||||
return this._widgetRenderAndInsert(_.bind(function (t) {
|
return this._widgetRenderAndInsert(_.bind(function (t) {
|
||||||
@@ -415,7 +423,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
|||||||
* @private
|
* @private
|
||||||
* @param {function: jQuery -> any} insertion
|
* @param {function: jQuery -> any} insertion
|
||||||
* @param {jQuery} target
|
* @param {jQuery} target
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_widgetRenderAndInsert: function (insertion, target) {
|
_widgetRenderAndInsert: function (insertion, target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ var AbstractField = Widget.extend({
|
|||||||
* When a field widget is appended to the DOM, its start method is called,
|
* When a field widget is appended to the DOM, its start method is called,
|
||||||
* and will automatically call render. Most widgets should not override this.
|
* and will automatically call render. Most widgets should not override this.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -236,7 +236,7 @@ var AbstractField = Widget.extend({
|
|||||||
* if the value changed but was not notified.
|
* if the value changed but was not notified.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @returns {Deferred|undefined}
|
* @returns {Promise|undefined}
|
||||||
*/
|
*/
|
||||||
commitChanges: function () {},
|
commitChanges: function () {},
|
||||||
/**
|
/**
|
||||||
@@ -290,12 +290,12 @@ var AbstractField = Widget.extend({
|
|||||||
* is optional, and may be used by a widget to share information from the
|
* is optional, and may be used by a widget to share information from the
|
||||||
* moment a field change event is triggered to the moment a reset
|
* moment a field change event is triggered to the moment a reset
|
||||||
* operation is applied.
|
* operation is applied.
|
||||||
* @returns {Deferred} A Deferred, which resolves when the widget rendering
|
* @returns {Promise} A promise, which resolves when the widget rendering
|
||||||
* is complete
|
* is complete
|
||||||
*/
|
*/
|
||||||
reset: function (record, event) {
|
reset: function (record, event) {
|
||||||
this._reset(record, event);
|
this._reset(record, event);
|
||||||
return this._render() || $.when();
|
return this._render() || Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Remove the invalid class on a field
|
* Remove the invalid class on a field
|
||||||
@@ -396,7 +396,7 @@ var AbstractField = Widget.extend({
|
|||||||
* synchronous.
|
* synchronous.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred|undefined}
|
* @returns {Promise|undefined}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
if (this.attrs.decorations) {
|
if (this.attrs.decorations) {
|
||||||
@@ -413,7 +413,7 @@ var AbstractField = Widget.extend({
|
|||||||
* concrete widget.
|
* concrete widget.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred|undefined}
|
* @returns {Promise|undefined}
|
||||||
*/
|
*/
|
||||||
_renderEdit: function () {
|
_renderEdit: function () {
|
||||||
},
|
},
|
||||||
@@ -422,7 +422,7 @@ var AbstractField = Widget.extend({
|
|||||||
* the concrete widget.
|
* the concrete widget.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred|undefined}
|
* @returns {Promise|undefined}
|
||||||
*/
|
*/
|
||||||
_renderReadonly: function () {
|
_renderReadonly: function () {
|
||||||
},
|
},
|
||||||
@@ -457,13 +457,13 @@ var AbstractField = Widget.extend({
|
|||||||
* will not notify and not trigger the onchange, even though it was changed.
|
* will not notify and not trigger the onchange, even though it was changed.
|
||||||
* @param {boolean} [options.forceChange=false] if true, the change event will be
|
* @param {boolean} [options.forceChange=false] if true, the change event will be
|
||||||
* triggered even if the new value is the same as the old one
|
* triggered even if the new value is the same as the old one
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_setValue: function (value, options) {
|
_setValue: function (value, options) {
|
||||||
// we try to avoid doing useless work, if the value given has not
|
// we try to avoid doing useless work, if the value given has not
|
||||||
// changed. Note that we compare the unparsed values.
|
// changed. Note that we compare the unparsed values.
|
||||||
if (this.lastSetValue === value || (this.value === false && value === '')) {
|
if (this.lastSetValue === value || (this.value === false && value === '')) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
this.lastSetValue = value;
|
this.lastSetValue = value;
|
||||||
try {
|
try {
|
||||||
@@ -472,25 +472,26 @@ var AbstractField = Widget.extend({
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._isValid = false;
|
this._isValid = false;
|
||||||
this.trigger_up('set_dirty', {dataPointID: this.dataPointID});
|
this.trigger_up('set_dirty', {dataPointID: this.dataPointID});
|
||||||
return $.Deferred().reject();
|
return Promise.reject({message: "Value set is not valid"});
|
||||||
}
|
}
|
||||||
if (!(options && options.forceChange) && this._isSameValue(value)) {
|
if (!(options && options.forceChange) && this._isSameValue(value)) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var def = $.Deferred();
|
var self = this;
|
||||||
var changes = {};
|
return new Promise(function (resolve, reject) {
|
||||||
changes[this.name] = value;
|
var changes = {};
|
||||||
this.trigger_up('field_changed', {
|
changes[self.name] = value;
|
||||||
dataPointID: this.dataPointID,
|
self.trigger_up('field_changed', {
|
||||||
changes: changes,
|
dataPointID: self.dataPointID,
|
||||||
viewType: this.viewType,
|
changes: changes,
|
||||||
doNotSetDirty: options && options.doNotSetDirty,
|
viewType: self.viewType,
|
||||||
notifyChange: !options || options.notifyChange !== false,
|
doNotSetDirty: options && options.doNotSetDirty,
|
||||||
allowWarning: options && options.allowWarning,
|
notifyChange: !options || options.notifyChange !== false,
|
||||||
onSuccess: def.resolve.bind(def),
|
allowWarning: options && options.allowWarning,
|
||||||
onFailure: def.reject.bind(def),
|
onSuccess: resolve,
|
||||||
|
onFailure: reject,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ var InputField = DebouncedField.extend({
|
|||||||
// sure they are recomputed.
|
// sure they are recomputed.
|
||||||
this._applyDecorations();
|
this._applyDecorations();
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
return this._render();
|
return this._render();
|
||||||
}
|
}
|
||||||
@@ -486,8 +486,6 @@ var LinkButton = AbstractField.extend({
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var FieldDate = InputField.extend({
|
var FieldDate = InputField.extend({
|
||||||
className: "o_field_date",
|
className: "o_field_date",
|
||||||
tagName: "span",
|
tagName: "span",
|
||||||
@@ -513,7 +511,7 @@ var FieldDate = InputField.extend({
|
|||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def;
|
var prom;
|
||||||
if (this.mode === 'edit') {
|
if (this.mode === 'edit') {
|
||||||
this.datewidget = this._makeDatePicker();
|
this.datewidget = this._makeDatePicker();
|
||||||
this.datewidget.on('datetime_changed', this, function () {
|
this.datewidget.on('datetime_changed', this, function () {
|
||||||
@@ -522,13 +520,13 @@ var FieldDate = InputField.extend({
|
|||||||
this._setValue(value);
|
this._setValue(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
def = this.datewidget.appendTo('<div>').done(function () {
|
prom = this.datewidget.appendTo('<div>').then(function () {
|
||||||
self.datewidget.$el.addClass(self.$el.attr('class'));
|
self.datewidget.$el.addClass(self.$el.attr('class'));
|
||||||
self._prepareInput(self.datewidget.$input);
|
self._prepareInput(self.datewidget.$input);
|
||||||
self._replaceElement(self.datewidget.$el);
|
self._replaceElement(self.datewidget.$el);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when(def, this._super.apply(this, arguments));
|
return Promise.resolve(prom).then(this._super.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -728,7 +726,7 @@ var FieldMonetary = InputField.extend({
|
|||||||
this.$el.empty();
|
this.$el.empty();
|
||||||
|
|
||||||
// Prepare and add the input
|
// Prepare and add the input
|
||||||
this._prepareInput(this.$input).appendTo(this.$el);
|
var def = this._prepareInput(this.$input).appendTo(this.$el);
|
||||||
|
|
||||||
if (this.currency) {
|
if (this.currency) {
|
||||||
// Prepare and add the currency symbol
|
// Prepare and add the currency symbol
|
||||||
@@ -739,6 +737,7 @@ var FieldMonetary = InputField.extend({
|
|||||||
this.$el.prepend($currencySymbol);
|
this.$el.prepend($currencySymbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return def;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -1136,7 +1135,7 @@ var FieldText = InputField.extend(TranslatableFieldMixin, {
|
|||||||
*/
|
*/
|
||||||
reset: function () {
|
reset: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
return $.when(this._super.apply(this, arguments)).then(function () {
|
return Promise.resolve(this._super.apply(this, arguments)).then(function () {
|
||||||
if (self.mode === 'edit') {
|
if (self.mode === 'edit') {
|
||||||
self.$input.trigger('change');
|
self.$input.trigger('change');
|
||||||
}
|
}
|
||||||
@@ -2568,7 +2567,7 @@ var JournalDashboardGraph = AbstractField.extend({
|
|||||||
if (this._isInDOM) {
|
if (this._isInDOM) {
|
||||||
return this._renderInDOM();
|
return this._renderInDOM();
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Render the widget. This function assumes that it is attached to the DOM.
|
* Render the widget. This function assumes that it is attached to the DOM.
|
||||||
@@ -2743,13 +2742,13 @@ var FieldDomain = AbstractField.extend({
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @override _render from AbstractField
|
* @override _render from AbstractField
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
// If there is no model, only change the non-domain-selector content
|
// If there is no model, only change the non-domain-selector content
|
||||||
if (!this._domainModel) {
|
if (!this._domainModel) {
|
||||||
this._replaceContent();
|
this._replaceContent();
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert char value to array value
|
// Convert char value to array value
|
||||||
@@ -2901,7 +2900,7 @@ var AceEditor = DebouncedField.extend({
|
|||||||
/**
|
/**
|
||||||
* @override start from AbstractField (Widget)
|
* @override start from AbstractField (Widget)
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this._startAce(this.$('.ace-view-editor')[0]);
|
this._startAce(this.$('.ace-view-editor')[0]);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ var Registry = require('web.Registry');
|
|||||||
return new Registry();
|
return new Registry();
|
||||||
});
|
});
|
||||||
|
|
||||||
odoo.define('web._field_registry', function(require) {
|
odoo.define('web._field_registry', function (require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var AbstractField = require('web.AbstractField');
|
var AbstractField = require('web.AbstractField');
|
||||||
|
|||||||
@@ -171,11 +171,11 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
* is saved.
|
* is saved.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred} resolved as soon as there is no longer record being
|
* @returns {Promise} resolved as soon as there is no longer record being
|
||||||
* (quick) created
|
* (quick) created
|
||||||
*/
|
*/
|
||||||
commitChanges: function () {
|
commitChanges: function () {
|
||||||
return $.when(this.createDef);
|
return Promise.resolve(this.createDef);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -208,7 +208,7 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
this.isDirty = false;
|
this.isDirty = false;
|
||||||
}
|
}
|
||||||
if (this.isDirty) {
|
if (this.isDirty) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
return this._render();
|
return this._render();
|
||||||
}
|
}
|
||||||
@@ -257,9 +257,9 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
if (!source.validation || source.validation.call(self, req.term)) {
|
if (!source.validation || source.validation.call(self, req.term)) {
|
||||||
source.loading = true;
|
source.loading = true;
|
||||||
|
|
||||||
// Wrap the returned value of the source.method with $.when.
|
// Wrap the returned value of the source.method with a promise
|
||||||
// So event if the returned value is not async, it will work
|
// So event if the returned value is not async, it will work
|
||||||
$.when(source.method.call(self, req.term)).then(function (results) {
|
Promise.resolve(source.method.call(self, req.term)).then(function (results) {
|
||||||
source.results = results;
|
source.results = results;
|
||||||
source.loading = false;
|
source.loading = false;
|
||||||
resp(self._concatenateAutocompleteResults());
|
resp(self._concatenateAutocompleteResults());
|
||||||
@@ -366,49 +366,55 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {Deferred} resolved after the name_create or when the slowcreate
|
* @returns {Promise} resolved after the name_create or when the slowcreate
|
||||||
* modal is closed.
|
* modal is closed.
|
||||||
*/
|
*/
|
||||||
_quickCreate: function (name) {
|
_quickCreate: function (name) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def = $.Deferred();
|
var createDone;
|
||||||
this.createDef = this.createDef || $.Deferred();
|
|
||||||
// called when the record has been quick created, or when the dialog has
|
var def = new Promise(function (resolve, reject) {
|
||||||
// been closed (in the case of a 'slow' create), meaning that the job is
|
self.createDef = new Promise(function (innerResolve) {
|
||||||
// done
|
// called when the record has been quick created, or when the dialog has
|
||||||
var createDone = function () {
|
// been closed (in the case of a 'slow' create), meaning that the job is
|
||||||
def.resolve();
|
// done
|
||||||
self.createDef.resolve();
|
createDone = function () {
|
||||||
self.createDef = undefined;
|
innerResolve();
|
||||||
};
|
resolve();
|
||||||
// called if the quick create is disabled on this many2one, or if the
|
self.createDef = undefined;
|
||||||
// quick creation failed (probably because there are mandatory fields on
|
};
|
||||||
// the model)
|
|
||||||
var slowCreate = function () {
|
|
||||||
var dialog = self._searchCreatePopup("form", false, self._createContext(name));
|
|
||||||
dialog.on('closed', self, createDone);
|
|
||||||
};
|
|
||||||
if (this.nodeOptions.quick_create) {
|
|
||||||
var nameCreateDef = this._rpc({
|
|
||||||
model: this.field.relation,
|
|
||||||
method: 'name_create',
|
|
||||||
args: [name],
|
|
||||||
context: this.record.getContext(this.recordParams),
|
|
||||||
}).fail(function (error, ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
slowCreate();
|
|
||||||
});
|
});
|
||||||
this.dp.add(nameCreateDef)
|
|
||||||
.then(function (result) {
|
// called if the quick create is disabled on this many2one, or if the
|
||||||
if (self.mode === "edit") {
|
// quick creation failed (probably because there are mandatory fields on
|
||||||
self.reinitialize({id: result[0], display_name: result[1]});
|
// the model)
|
||||||
}
|
var slowCreate = function () {
|
||||||
createDone();
|
var dialog = self._searchCreatePopup("form", false, self._createContext(name));
|
||||||
})
|
dialog.on('closed', self, createDone);
|
||||||
.fail(def.reject.bind(def));
|
};
|
||||||
} else {
|
if (self.nodeOptions.quick_create) {
|
||||||
slowCreate();
|
var nameCreateDef = self._rpc({
|
||||||
}
|
model: self.field.relation,
|
||||||
|
method: 'name_create',
|
||||||
|
args: [name],
|
||||||
|
context: self.record.getContext(self.recordParams),
|
||||||
|
}).guardedCatch(function (reason) {
|
||||||
|
reason.event.preventDefault();
|
||||||
|
slowCreate();
|
||||||
|
});
|
||||||
|
self.dp.add(nameCreateDef)
|
||||||
|
.then(function (result) {
|
||||||
|
if (self.mode === "edit") {
|
||||||
|
self.reinitialize({id: result[0], display_name: result[1]});
|
||||||
|
}
|
||||||
|
createDone();
|
||||||
|
})
|
||||||
|
.guardedCatch(reject);
|
||||||
|
} else {
|
||||||
|
slowCreate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return def;
|
return def;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -456,35 +462,32 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} search_val
|
* @param {string} search_val
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_search: function (search_val) {
|
_search: function (search_val) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def = $.Deferred();
|
var def = new Promise(function (resolve, reject) {
|
||||||
this.orderer.add(def);
|
var context = self.record.getContext(self.recordParams);
|
||||||
|
var domain = self.record.getDomain(self.recordParams);
|
||||||
|
|
||||||
var context = this.record.getContext(this.recordParams);
|
// Add the additionalContext
|
||||||
var domain = this.record.getDomain(this.recordParams);
|
_.extend(context, self.additionalContext);
|
||||||
|
|
||||||
// Add the additionalContext
|
var blacklisted_ids = self._getSearchBlacklist();
|
||||||
_.extend(context, this.additionalContext);
|
if (blacklisted_ids.length > 0) {
|
||||||
|
domain.push(['id', 'not in', blacklisted_ids]);
|
||||||
|
}
|
||||||
|
|
||||||
var blacklisted_ids = this._getSearchBlacklist();
|
self._rpc({
|
||||||
if (blacklisted_ids.length > 0) {
|
model: self.field.relation,
|
||||||
domain.push(['id', 'not in', blacklisted_ids]);
|
method: "name_search",
|
||||||
}
|
kwargs: {
|
||||||
|
name: search_val,
|
||||||
this._rpc({
|
args: domain,
|
||||||
model: this.field.relation,
|
operator: "ilike",
|
||||||
method: "name_search",
|
limit: self.limit + 1,
|
||||||
kwargs: {
|
context: context,
|
||||||
name: search_val,
|
}}).then(function (result) {
|
||||||
args: domain,
|
|
||||||
operator: "ilike",
|
|
||||||
limit: this.limit + 1,
|
|
||||||
context: context,
|
|
||||||
}})
|
|
||||||
.then(function (result) {
|
|
||||||
// possible selections for the m2o
|
// possible selections for the m2o
|
||||||
var values = _.map(result, function (x) {
|
var values = _.map(result, function (x) {
|
||||||
x[1] = self._getDisplayName(x[1]);
|
x[1] = self._getDisplayName(x[1]);
|
||||||
@@ -516,7 +519,7 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$.when(prom).then(function (results) {
|
Promise.resolve(prom).then(function (results) {
|
||||||
var dynamicFilters;
|
var dynamicFilters;
|
||||||
if (results) {
|
if (results) {
|
||||||
var ids = _.map(results, function (x) {
|
var ids = _.map(results, function (x) {
|
||||||
@@ -563,9 +566,10 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
def.resolve(values);
|
resolve(values);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
this.orderer.add(def);
|
||||||
return def;
|
return def;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -670,8 +674,9 @@ var FieldMany2One = AbstractField.extend({
|
|||||||
readonly: !self.can_write,
|
readonly: !self.can_write,
|
||||||
on_saved: function (record, changed) {
|
on_saved: function (record, changed) {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
self._setValue(self.value.data, {forceChange: true});
|
self._setValue(self.value.data, {forceChange: true}).then(function() {
|
||||||
self.trigger_up('reload', {db_id: self.value.id});
|
self.trigger_up('reload', {db_id: self.value.id});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}).open();
|
}).open();
|
||||||
@@ -868,14 +873,14 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
* user if he wants to discard it if necessary.
|
* user if he wants to discard it if necessary.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
commitChanges: function () {
|
commitChanges: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var inEditionRecordID =
|
var inEditionRecordID =
|
||||||
this.renderer
|
this.renderer &&
|
||||||
&& this.renderer.viewType === "list"
|
this.renderer.viewType === "list" &&
|
||||||
&& this.renderer.getEditableRecordID();
|
this.renderer.getEditableRecordID();
|
||||||
if (inEditionRecordID) {
|
if (inEditionRecordID) {
|
||||||
return this.renderer.commitChanges(inEditionRecordID).then(function () {
|
return this.renderer.commitChanges(inEditionRecordID).then(function () {
|
||||||
return self._saveLine(inEditionRecordID);
|
return self._saveLine(inEditionRecordID);
|
||||||
@@ -894,7 +899,7 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {OdooEvent} [ev] an event that triggered the reset action
|
* @param {OdooEvent} [ev] an event that triggered the reset action
|
||||||
* @param {Boolean} [fieldChanged] if true, the widget field has changed
|
* @param {Boolean} [fieldChanged] if true, the widget field has changed
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reset: function (record, ev, fieldChanged) {
|
reset: function (record, ev, fieldChanged) {
|
||||||
// If 'fieldChanged' is false, it means that the reset was triggered by
|
// If 'fieldChanged' is false, it means that the reset was triggered by
|
||||||
@@ -904,7 +909,7 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
if (!fieldChanged) {
|
if (!fieldChanged) {
|
||||||
var newEval = this._evalColumnInvisibleFields();
|
var newEval = this._evalColumnInvisibleFields();
|
||||||
if (_.isEqual(this.currentColInvisibleFields, newEval)) {
|
if (_.isEqual(this.currentColInvisibleFields, newEval)) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
} else if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
|
} else if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
|
||||||
var command = ev.data.changes[this.name];
|
var command = ev.data.changes[this.name];
|
||||||
@@ -994,17 +999,20 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred|undefined}
|
* @returns {Promise|undefined}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
|
var self = this;
|
||||||
if (!this.view) {
|
if (!this.view) {
|
||||||
return this._super();
|
return this._super();
|
||||||
}
|
}
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
this.currentColInvisibleFields = this._evalColumnInvisibleFields();
|
this.currentColInvisibleFields = this._evalColumnInvisibleFields();
|
||||||
this.renderer.updateState(this.value, {'columnInvisibleFields': this.currentColInvisibleFields});
|
return this.renderer.updateState(this.value, {
|
||||||
this.pager.updateState({ size: this.value.count });
|
columnInvisibleFields: this.currentColInvisibleFields,
|
||||||
return $.when();
|
}).then(function () {
|
||||||
|
self.pager.updateState({ size: self.value.count });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
var arch = this.view.arch;
|
var arch = this.view.arch;
|
||||||
var viewType;
|
var viewType;
|
||||||
@@ -1050,11 +1058,11 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
* Prepends the control panel's $el to this widget's $el.
|
* Prepends the control panel's $el to this widget's $el.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderControlPanel: function () {
|
_renderControlPanel: function () {
|
||||||
if (!this.view) {
|
if (!this.view) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var self = this;
|
var self = this;
|
||||||
var defs = [];
|
var defs = [];
|
||||||
@@ -1072,7 +1080,7 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
validate: function () {
|
validate: function () {
|
||||||
var isList = self.view.arch.tag === 'tree';
|
var isList = self.view.arch.tag === 'tree';
|
||||||
// TODO: we should have some common method in the basic renderer...
|
// TODO: we should have some common method in the basic renderer...
|
||||||
return isList ? self.renderer.unselectRow() : $.when();
|
return isList ? self.renderer.unselectRow() : Promise.resolve();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.pager.on('pager_changed', this, function (new_state) {
|
this.pager.on('pager_changed', this, function (new_state) {
|
||||||
@@ -1089,7 +1097,7 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
this._renderButtons();
|
this._renderButtons();
|
||||||
defs.push(this.pager.appendTo($('<div>'))); // start the pager
|
defs.push(this.pager.appendTo($('<div>'))); // start the pager
|
||||||
defs.push(cpDef);
|
defs.push(cpDef);
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
self._controlPanel.updateContents({
|
self._controlPanel.updateContents({
|
||||||
cp_content: {
|
cp_content: {
|
||||||
$buttons: self.$buttons,
|
$buttons: self.$buttons,
|
||||||
@@ -1123,24 +1131,24 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} recordID
|
* @param {string} recordID
|
||||||
* @returns {Deferred} resolved if the line was properly saved or discarded.
|
* @returns {Promise} resolved if the line was properly saved or discarded.
|
||||||
* rejected if the line could not be saved and the user
|
* rejected if the line could not be saved and the user
|
||||||
* did not agree to discard.
|
* did not agree to discard.
|
||||||
*/
|
*/
|
||||||
_saveLine: function (recordID) {
|
_saveLine: function (recordID) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def = $.Deferred();
|
return new Promise(function (resolve, reject) {
|
||||||
var fieldNames = this.renderer.canBeSaved(recordID);
|
var fieldNames = self.renderer.canBeSaved(recordID);
|
||||||
if (fieldNames.length) {
|
if (fieldNames.length) {
|
||||||
this.trigger_up('discard_changes', {
|
self.trigger_up('discard_changes', {
|
||||||
recordID: recordID,
|
recordID: recordID,
|
||||||
onSuccess: def.resolve.bind(def),
|
onSuccess: resolve,
|
||||||
onFailure: def.reject.bind(def),
|
onFailure: reject,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.renderer.setRowMode(recordID, 'readonly').done(def.resolve.bind(def));
|
self.renderer.setRowMode(recordID, 'readonly').then(resolve);
|
||||||
}
|
}
|
||||||
return def.then(function () {
|
}).then(function () {
|
||||||
self.pager.updateState({ size: self.value.count });
|
self.pager.updateState({ size: self.value.count });
|
||||||
var newEval = self._evalColumnInvisibleFields();
|
var newEval = self._evalColumnInvisibleFields();
|
||||||
if (!_.isEqual(self.currentColInvisibleFields, newEval)) {
|
if (!_.isEqual(self.currentColInvisibleFields, newEval)) {
|
||||||
@@ -1233,7 +1241,7 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
this.trigger_up('edited_list', { id: this.value.id });
|
this.trigger_up('edited_list', { id: this.value.id });
|
||||||
var editedRecord = this.value.data[ev.data.index];
|
var editedRecord = this.value.data[ev.data.index];
|
||||||
this.renderer.setRowMode(editedRecord.id, 'edit')
|
this.renderer.setRowMode(editedRecord.id, 'edit')
|
||||||
.done(ev.data.onSuccess);
|
.then(ev.data.onSuccess);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Updates the given record with the changes.
|
* Updates the given record with the changes.
|
||||||
@@ -1260,11 +1268,11 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
operation: 'UPDATE',
|
operation: 'UPDATE',
|
||||||
id: ev.data.dataPointID,
|
id: ev.data.dataPointID,
|
||||||
data: changes,
|
data: changes,
|
||||||
}).done(function () {
|
}).then(function () {
|
||||||
if (ev.data.onSuccess) {
|
if (ev.data.onSuccess) {
|
||||||
ev.data.onSuccess();
|
ev.data.onSuccess();
|
||||||
}
|
}
|
||||||
}).fail(function () {
|
}).guardedCatch(function () {
|
||||||
if (ev.data.onFailure) {
|
if (ev.data.onFailure) {
|
||||||
ev.data.onFailure();
|
ev.data.onFailure();
|
||||||
}
|
}
|
||||||
@@ -1332,8 +1340,8 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
self.trigger_up('mutexify', {
|
self.trigger_up('mutexify', {
|
||||||
action: function () {
|
action: function () {
|
||||||
return self._saveLine(ev.data.recordID)
|
return self._saveLine(ev.data.recordID)
|
||||||
.done(ev.data.onSuccess)
|
.then(ev.data.onSuccess)
|
||||||
.fail(ev.data.onFailure);
|
.guardedCatch(ev.data.onFailure);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1361,18 +1369,20 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
notifyChange: false,
|
notifyChange: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$.when.apply($, defs).then(function () {
|
Promise.all(defs).then(function () {
|
||||||
|
function always() {
|
||||||
|
self.trigger_up('toggle_column_order', {
|
||||||
|
id: self.value.id,
|
||||||
|
name: event.data.handleField,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// trigger only once the onchange for parent record
|
// trigger only once the onchange for parent record
|
||||||
self._setValue({
|
self._setValue({
|
||||||
operation: 'UPDATE',
|
operation: 'UPDATE',
|
||||||
id: rowID,
|
id: rowID,
|
||||||
data: _.object([event.data.handleField], [event.data.offset + rowIDs.length]),
|
data: _.object([event.data.handleField], [event.data.offset + rowIDs.length]),
|
||||||
}).always(function () {
|
}).then(always).guardedCatch(always);
|
||||||
self.trigger_up('toggle_column_order', {
|
|
||||||
id: self.value.id,
|
|
||||||
name: event.data.handleField,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -1418,7 +1428,7 @@ var FieldOne2Many = FieldX2Many.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {OdooEvent} [ev] an event that triggered the reset action
|
* @param {OdooEvent} [ev] an event that triggered the reset action
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reset: function (record, ev) {
|
reset: function (record, ev) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -1527,13 +1537,16 @@ var FieldOne2Many = FieldX2Many.extend({
|
|||||||
context: data.context,
|
context: data.context,
|
||||||
}, {
|
}, {
|
||||||
allowWarning: data.allowWarning
|
allowWarning: data.allowWarning
|
||||||
}).always(function () {
|
}).then(function () {
|
||||||
self.creatingRecord = false;
|
self.creatingRecord = false;
|
||||||
}).done(function (){
|
}).then(function (){
|
||||||
if (data.onSuccess){
|
if (data.onSuccess){
|
||||||
data.onSuccess();
|
data.onSuccess();
|
||||||
}
|
}
|
||||||
});
|
}).guardedCatch(function() {
|
||||||
|
self.creatingRecord = false;
|
||||||
|
})
|
||||||
|
;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._openFormDialog({
|
this._openFormDialog({
|
||||||
@@ -1646,8 +1659,10 @@ var FieldMany2Many = FieldX2Many.extend({
|
|||||||
domain: this.record.getDomain(this.recordParams),
|
domain: this.record.getDomain(this.recordParams),
|
||||||
fields_view: this.attrs.views && this.attrs.views.form,
|
fields_view: this.attrs.views && this.attrs.views.form,
|
||||||
on_saved: function () {
|
on_saved: function () {
|
||||||
self._setValue({operation: 'TRIGGER_ONCHANGE'}, {forceChange: true});
|
self._setValue({operation: 'TRIGGER_ONCHANGE'}, {forceChange: true})
|
||||||
self.trigger_up('reload', {db_id: ev.data.id});
|
.then(function () {
|
||||||
|
self.trigger_up('reload', {db_id: ev.data.id});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
on_remove: function () {
|
on_remove: function () {
|
||||||
self._setValue({operation: 'FORGET', ids: [ev.data.id]});
|
self._setValue({operation: 'FORGET', ids: [ev.data.id]});
|
||||||
@@ -1918,10 +1933,12 @@ var FieldMany2ManyTags = AbstractField.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
reset: function (record, event) {
|
reset: function (record, event) {
|
||||||
this._super.apply(this, arguments);
|
var self = this;
|
||||||
if (event && event.target === this) {
|
return this._super.apply(this, arguments).then(function(){
|
||||||
this.activate();
|
if (event && event.target === self) {
|
||||||
}
|
self.activate();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ var AbstractFieldUpgrade = {
|
|||||||
* Redirects the user to the odoo-enterprise/uprade page
|
* Redirects the user to the odoo-enterprise/uprade page
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmUpgrade: function () {
|
_confirmUpgrade: function () {
|
||||||
return this._rpc({
|
return this._rpc({
|
||||||
|
|||||||
@@ -49,11 +49,11 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, {
|
|||||||
*/
|
*/
|
||||||
willStart: function () {
|
willStart: function () {
|
||||||
// TODO would be even greater to wait for localeDef only when necessary
|
// TODO would be even greater to wait for localeDef only when necessary
|
||||||
return $.when(
|
return Promise.all([
|
||||||
this._super.apply(this, arguments),
|
this._super.apply(this, arguments),
|
||||||
session.is_bound,
|
session.is_bound,
|
||||||
localeDef
|
localeDef
|
||||||
);
|
]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -82,7 +82,7 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, {
|
|||||||
$('input, textarea').placeholder();
|
$('input, textarea').placeholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -193,9 +193,9 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, {
|
|||||||
self.publicWidgets.push(widget);
|
self.publicWidgets.push(widget);
|
||||||
return widget.attachTo($(el));
|
return widget.attachTo($(el));
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Destroys all registered widget instances. Website would need this before
|
* Destroys all registered widget instances. Website would need this before
|
||||||
@@ -259,8 +259,8 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, {
|
|||||||
*/
|
*/
|
||||||
_onWidgetsStartRequest: function (ev) {
|
_onWidgetsStartRequest: function (ev) {
|
||||||
this._startWidgets(ev.data.$target, ev.data.options)
|
this._startWidgets(ev.data.$target, ev.data.options)
|
||||||
.done(ev.data.onSuccess)
|
.then(ev.data.onSuccess)
|
||||||
.fail(ev.data.onFailure);
|
.guardedCatch(ev.data.onFailure);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when the root is notified that the public widgets have to be
|
* Called when the root is notified that the public widgets have to be
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ var RootWidget = Widget.extend({
|
|||||||
defs.push(this._attachComponents());
|
defs.push(this._attachComponents());
|
||||||
this._listenToUpdates = true;
|
this._listenToUpdates = true;
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -71,7 +71,7 @@ var RootWidget = Widget.extend({
|
|||||||
self._widgets.push(w);
|
self._widgets.push(w);
|
||||||
return w.attachTo(element);
|
return w.attachTo(element);
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Instantiates the child widgets that need to be according to the linked
|
* Instantiates the child widgets that need to be according to the linked
|
||||||
@@ -88,7 +88,7 @@ var RootWidget = Widget.extend({
|
|||||||
var defs = _.map(childInfos, function (childInfo) {
|
var defs = _.map(childInfos, function (childInfo) {
|
||||||
return self._attachComponent(childInfo, $from);
|
return self._attachComponent(childInfo, $from);
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Returns the `RootWidgetRegistry` instance that is linked to this
|
* Returns the `RootWidgetRegistry` instance that is linked to this
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ var ReportAction = AbstractAction.extend({
|
|||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.iframe = this.$('iframe')[0];
|
this.iframe = this.$('iframe')[0];
|
||||||
return $.when(this._super.apply(this, arguments), session.is_bound).then(function () {
|
return Promise.all([this._super.apply(this, arguments), session.is_bound]).then(function () {
|
||||||
var web_base_url = session['web.base.url'];
|
var web_base_url = session['web.base.url'];
|
||||||
var trusted_host = utils.get_host_from_url(web_base_url);
|
var trusted_host = utils.get_host_from_url(web_base_url);
|
||||||
var trusted_protocol = utils.get_protocol_from_url(web_base_url);
|
var trusted_protocol = utils.get_protocol_from_url(web_base_url);
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ var session = require('web.session');
|
|||||||
|
|
||||||
var AjaxService = AbstractService.extend({
|
var AjaxService = AbstractService.extend({
|
||||||
rpc: function (route, args, options, target) {
|
rpc: function (route, args, options, target) {
|
||||||
var def = $.Deferred();
|
var rpcPromise;
|
||||||
var promise = def.promise();
|
var promise = new Promise(function (resolve, reject) {
|
||||||
var xhrDef = session.rpc(route, args, options);
|
rpcPromise = session.rpc(route, args, options);
|
||||||
promise.abort = xhrDef.abort.bind(xhrDef);
|
rpcPromise.then(function (result) {
|
||||||
xhrDef.then(function () {
|
if (!target.isDestroyed()) {
|
||||||
if (!target.isDestroyed()) {
|
resolve(result);
|
||||||
def.resolve.apply(def, arguments);
|
}
|
||||||
}
|
}).guardedCatch(function (reason) {
|
||||||
}).fail(function () {
|
if (!target.isDestroyed()) {
|
||||||
if (!target.isDestroyed()) {
|
reject(reason);
|
||||||
def.reject.apply(def, arguments);
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
promise.abort = rpcPromise.abort.bind(rpcPromise);
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ var translation = require('web.translation');
|
|||||||
*
|
*
|
||||||
* @type Boolean
|
* @type Boolean
|
||||||
*/
|
*/
|
||||||
var bus = new Bus ();
|
var bus = new Bus();
|
||||||
|
|
||||||
_.each('click,dblclick,keydown,keypress,keyup'.split(','), function(evtype) {
|
_.each('click,dblclick,keydown,keypress,keyup'.split(','), function (evtype) {
|
||||||
$('html').on(evtype, function(ev) {
|
$('html').on(evtype, function (ev) {
|
||||||
bus.trigger(evtype, ev);
|
bus.trigger(evtype, ev);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
_.each('resize,scroll'.split(','), function(evtype) {
|
_.each('resize,scroll'.split(','), function (evtype) {
|
||||||
$(window).on(evtype, function(ev) {
|
$(window).on(evtype, function (ev) {
|
||||||
bus.trigger(evtype, ev);
|
bus.trigger(evtype, ev);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -37,7 +37,7 @@ return {
|
|||||||
_lt: translation._lt,
|
_lt: translation._lt,
|
||||||
|
|
||||||
// registries
|
// registries
|
||||||
action_registry : new Registry(),
|
action_registry: new Registry(),
|
||||||
crash_registry: new Registry(),
|
crash_registry: new Registry(),
|
||||||
serviceRegistry: new Registry(),
|
serviceRegistry: new Registry(),
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ var CrashManager = core.Class.extend({
|
|||||||
ajax.jsonRpc('/web/webclient/version_info', 'call', {}, {shadow:true}).then(function () {
|
ajax.jsonRpc('/web/webclient/version_info', 'call', {}, {shadow:true}).then(function () {
|
||||||
core.bus.trigger('connection_restored');
|
core.bus.trigger('connection_restored');
|
||||||
self.isConnected = true;
|
self.isConnected = true;
|
||||||
}).fail(function () {
|
}).guardedCatch(function () {
|
||||||
// exponential backoff, with some jitter
|
// exponential backoff, with some jitter
|
||||||
delay = (delay * 1.5) + 500*Math.random();
|
delay = (delay * 1.5) + 500*Math.random();
|
||||||
setTimeout(checkConnection, delay);
|
setTimeout(checkConnection, delay);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ return core.Class.extend({
|
|||||||
*
|
*
|
||||||
* @param {int|string} [action_id] the action id or xmlid
|
* @param {int|string} [action_id] the action id or xmlid
|
||||||
* @param {Object} [additional_context] used to load the action
|
* @param {Object} [additional_context] used to load the action
|
||||||
* @return {Deferred} resolved with the action whose id or xmlid is action_id
|
* @return {Promise} resolved with the action whose id or xmlid is action_id
|
||||||
*/
|
*/
|
||||||
load_action: function (action_id, additional_context) {
|
load_action: function (action_id, additional_context) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -69,7 +69,7 @@ return core.Class.extend({
|
|||||||
* - options.load_filters: whether or not to load the filters,
|
* - options.load_filters: whether or not to load the filters,
|
||||||
* - options.action_id: the action_id (required to load filters),
|
* - options.action_id: the action_id (required to load filters),
|
||||||
* - options.toolbar: whether or not a toolbar will be displayed,
|
* - options.toolbar: whether or not a toolbar will be displayed,
|
||||||
* @return {Deferred} resolved with the requested views information
|
* @return {Promise} resolved with the requested views information
|
||||||
*/
|
*/
|
||||||
load_views: function (params, options) {
|
load_views: function (params, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -108,12 +108,12 @@ return core.Class.extend({
|
|||||||
var fvg = result.fields_views[view_descr[1]];
|
var fvg = result.fields_views[view_descr[1]];
|
||||||
fvg.viewFields = fvg.fields;
|
fvg.viewFields = fvg.fields;
|
||||||
fvg.fields = result.fields;
|
fvg.fields = result.fields;
|
||||||
self._cache.fields_views[fv_key] = $.when(fvg);
|
self._cache.fields_views[fv_key] = Promise.resolve(fvg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Insert filters, if any, into the filters cache
|
// Insert filters, if any, into the filters cache
|
||||||
if (result.filters) {
|
if (result.filters) {
|
||||||
self._cache.filters[filters_key] = $.when(result.filters);
|
self._cache.filters[filters_key] = Promise.resolve(result.filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.fields_views;
|
return result.fields_views;
|
||||||
@@ -130,7 +130,7 @@ return core.Class.extend({
|
|||||||
* @param {string} params.modelName
|
* @param {string} params.modelName
|
||||||
* @param {Object} params.context
|
* @param {Object} params.context
|
||||||
* @param {integer} params.actionId
|
* @param {integer} params.actionId
|
||||||
* @return {Deferred} resolved with the requested filters
|
* @return {Promise} resolved with the requested filters
|
||||||
*/
|
*/
|
||||||
load_filters: function (params) {
|
load_filters: function (params) {
|
||||||
var key = this._gen_key(params.modelName, params.actionId);
|
var key = this._gen_key(params.modelName, params.actionId);
|
||||||
@@ -143,7 +143,7 @@ return core.Class.extend({
|
|||||||
},
|
},
|
||||||
model: 'ir.filters',
|
model: 'ir.filters',
|
||||||
method: 'get_filters',
|
method: 'get_filters',
|
||||||
}).fail(this._invalidate.bind(this, this._cache.filters, key));
|
}).guardedCatch(this._invalidate.bind(this, this._cache.filters, key));
|
||||||
}
|
}
|
||||||
return this._cache.filters[key];
|
return this._cache.filters[key];
|
||||||
},
|
},
|
||||||
@@ -152,7 +152,7 @@ return core.Class.extend({
|
|||||||
* Calls 'create_or_replace' on 'ir_filters'.
|
* Calls 'create_or_replace' on 'ir_filters'.
|
||||||
*
|
*
|
||||||
* @param {Object} [filter] the filter description
|
* @param {Object} [filter] the filter description
|
||||||
* @return {Deferred} resolved with the id of the created or replaced filter
|
* @return {Promise} resolved with the id of the created or replaced filter
|
||||||
*/
|
*/
|
||||||
create_filter: function (filter) {
|
create_filter: function (filter) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -175,7 +175,7 @@ return core.Class.extend({
|
|||||||
* Calls 'unlink' on 'ir_filters'.
|
* Calls 'unlink' on 'ir_filters'.
|
||||||
*
|
*
|
||||||
* @param {integer} filterId Id of the filter to remove
|
* @param {integer} filterId Id of the filter to remove
|
||||||
* @return {Deferred}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
delete_filter: function (filterId) {
|
delete_filter: function (filterId) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ var ReportService = AbstractService.extend({
|
|||||||
* Checks the state of the installation of wkhtmltopdf on the server.
|
* Checks the state of the installation of wkhtmltopdf on the server.
|
||||||
* Implements an internal cache to do the request only once.
|
* Implements an internal cache to do the request only once.
|
||||||
*
|
*
|
||||||
* @returns {Deferred} resolved with the state of wkhtmltopdf on the server
|
* @returns {Promise} resolved with the state of wkhtmltopdf on the server
|
||||||
* (possible values are 'ok', 'broken', 'install', 'upgrade', 'workers').
|
* (possible values are 'ok', 'broken', 'install', 'upgrade', 'workers').
|
||||||
*/
|
*/
|
||||||
checkWkhtmltopdf: function () {
|
checkWkhtmltopdf: function () {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ var DebugManager = Widget.extend({
|
|||||||
this._has_features = false;
|
this._has_features = false;
|
||||||
// whether the current user is an administrator
|
// whether the current user is an administrator
|
||||||
this._is_admin = session.is_system;
|
this._is_admin = session.is_system;
|
||||||
return $.when(
|
return Promise.resolve(
|
||||||
this._rpc({
|
this._rpc({
|
||||||
model: 'res.users',
|
model: 'res.users',
|
||||||
method: 'check_access_rights',
|
method: 'check_access_rights',
|
||||||
@@ -160,7 +160,7 @@ var DebugManager = Widget.extend({
|
|||||||
.append(QWeb.render('WebClient.DebugManager.Global', {
|
.append(QWeb.render('WebClient.DebugManager.Global', {
|
||||||
manager: this,
|
manager: this,
|
||||||
}));
|
}));
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
select_view: function () {
|
select_view: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -281,7 +281,7 @@ DebugManager.include({
|
|||||||
model: 'ir.model',
|
model: 'ir.model',
|
||||||
method: 'search',
|
method: 'search',
|
||||||
args: [[['model', '=', model]]]
|
args: [[['model', '=', model]]]
|
||||||
}).done(function (ids) {
|
}).then(function (ids) {
|
||||||
self.do_action({
|
self.do_action({
|
||||||
res_model: 'ir.model.fields',
|
res_model: 'ir.model.fields',
|
||||||
name: _t('View Fields'),
|
name: _t('View Fields'),
|
||||||
@@ -324,7 +324,7 @@ DebugManager.include({
|
|||||||
DebugManager.include({
|
DebugManager.include({
|
||||||
start: function () {
|
start: function () {
|
||||||
this._can_edit_views = false;
|
this._can_edit_views = false;
|
||||||
return $.when(
|
return Promise.all([
|
||||||
this._super(),
|
this._super(),
|
||||||
this._rpc({
|
this._rpc({
|
||||||
model: 'ir.ui.view',
|
model: 'ir.ui.view',
|
||||||
@@ -334,6 +334,7 @@ DebugManager.include({
|
|||||||
.then(function (ar) {
|
.then(function (ar) {
|
||||||
this._can_edit_views = ar;
|
this._can_edit_views = ar;
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
|
]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
update: function (tag, descriptor, widget) {
|
update: function (tag, descriptor, widget) {
|
||||||
@@ -382,7 +383,7 @@ DebugManager.include({
|
|||||||
model: this._action.res_model,
|
model: this._action.res_model,
|
||||||
method: 'get_metadata',
|
method: 'get_metadata',
|
||||||
args: [selectedIDs],
|
args: [selectedIDs],
|
||||||
}).done(function(result) {
|
}).then(function(result) {
|
||||||
var metadata = result[0];
|
var metadata = result[0];
|
||||||
metadata.creator = field_utils.format.many2one(metadata.create_uid);
|
metadata.creator = field_utils.format.many2one(metadata.create_uid);
|
||||||
metadata.lastModifiedBy = field_utils.format.many2one(metadata.write_uid);
|
metadata.lastModifiedBy = field_utils.format.many2one(metadata.write_uid);
|
||||||
@@ -515,7 +516,7 @@ DebugManager.include({
|
|||||||
true,
|
true,
|
||||||
condition || false,
|
condition || false,
|
||||||
],
|
],
|
||||||
}).done(function () { d.close(); });
|
}).then(function () { d.close(); });
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,16 +65,16 @@
|
|||||||
}
|
}
|
||||||
console.log('Found ', $listOfAppMenuItems.length, 'apps to test');
|
console.log('Found ', $listOfAppMenuItems.length, 'apps to test');
|
||||||
|
|
||||||
var testDef = $.when();
|
var testPromise = Promise.resolve();
|
||||||
testDef = chainDeferred($listOfAppMenuItems, testDef, testApp);
|
testPromise = chainDeferred($listOfAppMenuItems, testPromise, testApp);
|
||||||
return testDef.then(function() {
|
return testPromise.then(function () {
|
||||||
console.log("Successfully tested ", testedApps.length, " apps");
|
console.log("Successfully tested ", testedApps.length, " apps");
|
||||||
console.log("Successfully tested ", testedMenus.length - testedApps.length, " menus");
|
console.log("Successfully tested ", testedMenus.length - testedApps.length, " menus");
|
||||||
console.log("test successful");
|
console.log("test successful");
|
||||||
}).always(function() {
|
console.log("Test took ", (performance.now() - startTime) / 1000, " seconds");
|
||||||
console.log("Test took ", (performance.now() - startTime)/1000, " seconds");
|
}).catch(function () {
|
||||||
}).fail(function () {
|
console.log("Test took ", (performance.now() - startTime) / 1000, " seconds");
|
||||||
console.error("test failed");
|
console.error("Error !");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,18 +86,18 @@
|
|||||||
* 3 - clicking on each menu
|
* 3 - clicking on each menu
|
||||||
* 3.1 - clicking on each view
|
* 3.1 - clicking on each view
|
||||||
* @param {DomElement} element: the App menu item
|
* @param {DomElement} element: the App menu item
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function testApp(element){
|
function testApp(element){
|
||||||
console.log("Testing app menu:", element.dataset.menuXmlid);
|
console.log("Testing app menu:", element.dataset.menuXmlid);
|
||||||
if (testedApps.indexOf(element.dataset.menuXmlid) >= 0) return $.Deferred().resolve(); // Another infinite loop protection
|
if (testedApps.indexOf(element.dataset.menuXmlid) >= 0) return Promise.resolve(); // Another infinite loop protection
|
||||||
testedApps.push(element.dataset.menuXmlid);
|
testedApps.push(element.dataset.menuXmlid);
|
||||||
return testMenuItem(element).then(function () {
|
return testMenuItem(element).then(function () {
|
||||||
var $subMenuItems;
|
var $subMenuItems;
|
||||||
$subMenuItems = $('.o_menu_entry_lvl_1, .o_menu_entry_lvl_2, .o_menu_entry_lvl_3, .o_menu_entry_lvl_4');
|
$subMenuItems = $('.o_menu_entry_lvl_1, .o_menu_entry_lvl_2, .o_menu_entry_lvl_3, .o_menu_entry_lvl_4');
|
||||||
var testMenuDef = $.when();
|
var testMenuPromise = Promise.resolve();
|
||||||
testMenuDef = chainDeferred($subMenuItems, testMenuDef, testMenuItem);
|
testMenuPromise = chainDeferred($subMenuItems, testMenuPromise, testMenuItem);
|
||||||
return testMenuDef;
|
return testMenuPromise;
|
||||||
}).then(function(){
|
}).then(function(){
|
||||||
// no effect in community
|
// no effect in community
|
||||||
var $homeMenu = $("nav.o_main_navbar > a.o_menu_toggle.fa-th");
|
var $homeMenu = $("nav.o_main_navbar > a.o_menu_toggle.fa-th");
|
||||||
@@ -112,10 +112,10 @@
|
|||||||
* 2 - Orchestrate the view switch
|
* 2 - Orchestrate the view switch
|
||||||
*
|
*
|
||||||
* @param {DomElement} element: the menu item
|
* @param {DomElement} element: the menu item
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function testMenuItem(element){
|
function testMenuItem(element){
|
||||||
if (testedMenus.indexOf(element.dataset.menuXmlid) >= 0) return $.Deferred().resolve(); // Avoid infinite loop
|
if (testedMenus.indexOf(element.dataset.menuXmlid) >= 0) return Promise.resolve(); // Avoid infinite loop
|
||||||
console.log("Testing menu", element.innerText.trim(), " ", element.dataset.menuXmlid);
|
console.log("Testing menu", element.innerText.trim(), " ", element.dataset.menuXmlid);
|
||||||
testedMenus.push(element.dataset.menuXmlid);
|
testedMenus.push(element.dataset.menuXmlid);
|
||||||
var startActionCount = clientActionCount;
|
var startActionCount = clientActionCount;
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
$modal.modal('hide');
|
$modal.modal('hide');
|
||||||
isModal = true;
|
isModal = true;
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
return startActionCount != clientActionCount;
|
return startActionCount != clientActionCount;
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
if (!isModal) {
|
if (!isModal) {
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
if (!isModal) {
|
if (!isModal) {
|
||||||
return testViews();
|
return testViews();
|
||||||
}
|
}
|
||||||
}).fail(function() {
|
}).catch(function () {
|
||||||
console.error("Error while testing", element);
|
console.error("Error while testing", element);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -151,21 +151,21 @@
|
|||||||
*/
|
*/
|
||||||
function testViews() {
|
function testViews() {
|
||||||
var $switches = $("nav.o_cp_switch_buttons > button:not(.active):visible");
|
var $switches = $("nav.o_cp_switch_buttons > button:not(.active):visible");
|
||||||
var testSwitchDef = $.when();
|
var testSwitchPromise = Promise.resolve();
|
||||||
// chainDeferred($switches, testSwitchDef, testViewSwitch # FIXME
|
// chainDeferred($switches, testSwitchPromise, testViewSwitch # FIXME
|
||||||
_.each($switches, function(switchButton) {
|
_.each($switches, function(switchButton) {
|
||||||
testSwitchDef = testSwitchDef.then(function () {
|
testSwitchPromise = testSwitchPromise.then(function () {
|
||||||
// get the view view-type data attribute
|
// get the view view-type data attribute
|
||||||
return testViewSwitch(switchButton.dataset.viewType);
|
return testViewSwitch(switchButton.dataset.viewType);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return testSwitchDef;
|
return testSwitchPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test a view button
|
* Test a view button
|
||||||
* @param {string} viewType: a string for the view type to test (list, kanban ...)
|
* @param {string} viewType: a string for the view type to test (list, kanban ...)
|
||||||
* @returns {Deferred} a deferred that wait for the view to be loaded
|
* @returns {Promise} a promise that wait for the view to be loaded
|
||||||
*/
|
*/
|
||||||
function testViewSwitch(viewType){
|
function testViewSwitch(viewType){
|
||||||
console.log("Testing view switch: ", viewType);
|
console.log("Testing view switch: ", viewType);
|
||||||
@@ -188,20 +188,20 @@
|
|||||||
* Click on each filter in the control pannel
|
* Click on each filter in the control pannel
|
||||||
*/
|
*/
|
||||||
function testFilters() {
|
function testFilters() {
|
||||||
var filterDef = $.when();
|
var filterProm = Promise.resolve();
|
||||||
// var $filters = $('div.o_control_panel div.btn-group.o_dropdown > ul.o_filters_menu > li:not(.o_add_custom_filter)');
|
// var $filters = $('div.o_control_panel div.btn-group.o_dropdown > ul.o_filters_menu > li:not(.o_add_custom_filter)');
|
||||||
var $filters = $('.o_filters_menu > .o_menu_item');
|
var $filters = $('.o_filters_menu > .o_menu_item');
|
||||||
console.log("Testing " + $filters.length + " filters");
|
console.log("Testing " + $filters.length + " filters");
|
||||||
var filter_ids = _.compact(_.map($filters, function(f) { return f.dataset.id}));
|
var filter_ids = _.compact(_.map($filters, function(f) { return f.dataset.id}));
|
||||||
_.each(filter_ids, function(filter_id){
|
_.each(filter_ids, function(filter_id){
|
||||||
filterDef = filterDef.then(function(){
|
filterProm = filterProm.then(function(){
|
||||||
var currentViewCount = viewUpdateCount;
|
var currentViewCount = viewUpdateCount;
|
||||||
var $filter = $('.o_menu_item[data-id="' + filter_id + '"] a');
|
var $filter = $('.o_menu_item[data-id="' + filter_id + '"] a');
|
||||||
// with some customized search views, the filter cannot be found
|
// with some customized search views, the filter cannot be found
|
||||||
if ($filter[0] === undefined) {
|
if ($filter[0] === undefined) {
|
||||||
console.warn('Filter with ID ', filter_id , 'cannot be found');
|
console.warn('Filter with ID ', filter_id , 'cannot be found');
|
||||||
return $.Deferred().resolve();
|
return Promise.resolve();
|
||||||
};
|
}
|
||||||
console.log('Clicking on filter "', $filter.text().trim(), '"');
|
console.log('Clicking on filter "', $filter.text().trim(), '"');
|
||||||
$filter[0].click();
|
$filter[0].click();
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@@ -220,52 +220,55 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return filterDef;
|
return filterProm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility functions
|
// utility functions
|
||||||
/**
|
/**
|
||||||
* Wait a certain amount of time for a condition to occur
|
* Wait a certain amount of time for a condition to occur
|
||||||
* @param stopCondition: a function that returns a boolean
|
* @param {function} stopCondition a function that returns a boolean
|
||||||
* @returns {Deferred} that is rejected if the timeout is exceeded
|
* @returns {Promise} that is rejected if the timeout is exceeded
|
||||||
*/
|
*/
|
||||||
function waitForCondition(stopCondition) {
|
function waitForCondition(stopCondition) {
|
||||||
var def = $.Deferred();
|
var prom = new Promise(function (resolve, reject) {
|
||||||
var interval = 250;
|
var interval = 250;
|
||||||
var timeLimit = 15000;
|
var timeLimit = 15000;
|
||||||
function checkCondition() {
|
|
||||||
if (stopCondition()) {
|
function checkCondition() {
|
||||||
def.resolve();
|
if (stopCondition()) {
|
||||||
} else {
|
resolve();
|
||||||
timeLimit -= interval;
|
|
||||||
if (timeLimit > 0) {
|
|
||||||
// recursive call until the resolve or the timeout
|
|
||||||
setTimeout(checkCondition, interval);
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Timeout exceeded", stopCondition);
|
timeLimit -= interval;
|
||||||
def.reject();
|
if (timeLimit > 0) {
|
||||||
|
// recursive call until the resolve or the timeout
|
||||||
|
setTimeout(checkCondition, interval);
|
||||||
|
} else {
|
||||||
|
console.error("Timeout exceeded", stopCondition);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
setTimeout(checkCondition, interval);
|
||||||
setTimeout(checkCondition, interval);
|
});
|
||||||
return def;
|
return prom;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* chain deferred actions
|
* Chain deferred actions.
|
||||||
* @param $elements: a list of jquery elements to be passed as arg to the function
|
*
|
||||||
* @param deferred: the deferred on which other deferreds will be chained
|
* @param {jQueryElement} $elements a list of jquery elements to be passed as arg to the function
|
||||||
* @param f: the function to be deferred
|
* @param {Promise} promise the promise on which other promises will be chained
|
||||||
* @returns : the chained deferred
|
* @param {function} f the function to be deferred
|
||||||
|
* @returns {Promise} the chained promise
|
||||||
*/
|
*/
|
||||||
function chainDeferred($elements, deferred, f) {
|
function chainDeferred($elements, promise, f) {
|
||||||
_.each($elements, function(el) {
|
_.each($elements, function(el) {
|
||||||
deferred = deferred.then(function () {
|
promise = promise.then(function () {
|
||||||
return f(el);
|
return f(el);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return deferred;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
/**
|
/**
|
||||||
* Simply renders and updates the url.
|
* Simply renders and updates the url.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -70,12 +70,16 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
this.$el.addClass('o_view_controller');
|
this.$el.addClass('o_view_controller');
|
||||||
|
|
||||||
return this._super.apply(this, arguments).then(function () {
|
return this._super.apply(this, arguments).then(function () {
|
||||||
|
var prom;
|
||||||
if (self._controlPanel) {
|
if (self._controlPanel) {
|
||||||
// render the ControlPanel elements (buttons, pager, sidebar...)
|
// render the ControlPanel elements (buttons, pager, sidebar...)
|
||||||
self.controlPanelElements = self._renderControlPanelElements();
|
prom = self._renderControlPanelElements().then(function (elements) {
|
||||||
self._controlPanel.$el.prependTo(self.$el);
|
self.controlPanelElements = elements;
|
||||||
|
self._controlPanel.$el.prependTo(self.$el);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
return Promise.resolve(prom);
|
||||||
|
}).then(function () {
|
||||||
return self._update(self.initialState);
|
return self._update(self.initialState);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -119,7 +123,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
*/
|
*/
|
||||||
canBeRemoved: function () {
|
canBeRemoved: function () {
|
||||||
// AAB: get rid of this option when on_hashchange mechanism is improved
|
// AAB: get rid of this option when on_hashchange mechanism is improved
|
||||||
return this.discardChanges(undefined, {readonlyIfRealDiscard: true});
|
return this.discardChanges(undefined, { readonlyIfRealDiscard: true });
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Discards the changes made on the record associated to the given ID, or
|
* Discards the changes made on the record associated to the given ID, or
|
||||||
@@ -128,15 +132,15 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
* this method on the active view to make sure it is ok to open the home
|
* this method on the active view to make sure it is ok to open the home
|
||||||
* screen (and lose all current state).
|
* screen (and lose all current state).
|
||||||
*
|
*
|
||||||
* Note that it returns a deferred, because the view could choose to ask the
|
* Note that it returns a Promise, because the view could choose to ask the
|
||||||
* user if he agrees to discard.
|
* user if he agrees to discard.
|
||||||
*
|
*
|
||||||
* @param {string} [recordID]
|
* @param {string} [recordID]
|
||||||
* if not given, we consider all the changes made by the controller
|
* if not given, we consider all the changes made by the controller
|
||||||
* @returns {Deferred} resolved if properly discarded, rejected otherwise
|
* @returns {Promise} resolved if properly discarded, rejected otherwise
|
||||||
*/
|
*/
|
||||||
discardChanges: function (recordID) {
|
discardChanges: function (recordID) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Export the state of the controller containing information that is shared
|
* Export the state of the controller containing information that is shared
|
||||||
@@ -155,7 +159,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
/**
|
/**
|
||||||
* Gives the focus to the renderer
|
* Gives the focus to the renderer
|
||||||
*/
|
*/
|
||||||
giveFocus: function() {
|
giveFocus: function () {
|
||||||
this.renderer.giveFocus();
|
this.renderer.giveFocus();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -174,7 +178,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
* Short helper method to reload the view
|
* Short helper method to reload the view
|
||||||
*
|
*
|
||||||
* @param {Object} [params] This object will simply be given to the update
|
* @param {Object} [params] This object will simply be given to the update
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reload: function (params) {
|
reload: function (params) {
|
||||||
params = params || {};
|
params = params || {};
|
||||||
@@ -186,7 +190,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
params = _.extend({}, params, searchQuery);
|
params = _.extend({}, params, searchQuery);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when(def).then(this.update.bind(this, params, {}));
|
return Promise.resolve(def).then(this.update.bind(this, params, {}));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* For views that require a pager, this method will be called to allow the
|
* For views that require a pager, this method will be called to allow the
|
||||||
@@ -195,16 +199,20 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
* your view does not want a pager, just let this method empty.
|
* your view does not want a pager, just let this method empty.
|
||||||
*
|
*
|
||||||
* @param {jQuery Node} $node
|
* @param {jQuery Node} $node
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
renderPager: function ($node) {
|
renderPager: function ($node) {
|
||||||
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Same as renderPager, but for the 'sidebar' zone (the zone with the menu
|
* Same as renderPager, but for the 'sidebar' zone (the zone with the menu
|
||||||
* dropdown in the control panel next to the buttons)
|
* dropdown in the control panel next to the buttons)
|
||||||
*
|
*
|
||||||
* @param {jQuery Node} $node
|
* @param {jQuery Node} $node
|
||||||
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
renderSidebar: function ($node) {
|
renderSidebar: function ($node) {
|
||||||
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* This is the main entry point for the controller. Changes from the search
|
* This is the main entry point for the controller. Changes from the search
|
||||||
@@ -219,12 +227,12 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {boolean} [options.reload=true] if true, the model will reload data
|
* @param {boolean} [options.reload=true] if true, the model will reload data
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
update: function (params, options) {
|
update: function (params, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var shouldReload = (options && 'reload' in options) ? options.reload : true;
|
var shouldReload = (options && 'reload' in options) ? options.reload : true;
|
||||||
var def = shouldReload ? this.model.reload(this.handle, params) : $.when();
|
var def = shouldReload ? this.model.reload(this.handle, params) : Promise.resolve();
|
||||||
// we check here that the updateIndex of the control panel hasn't changed
|
// we check here that the updateIndex of the control panel hasn't changed
|
||||||
// between the start of the update request and the moment the controller
|
// between the start of the update request and the moment the controller
|
||||||
// asks the control panel to update itself ; indeed, it could happen that
|
// asks the control panel to update itself ; indeed, it could happen that
|
||||||
@@ -285,17 +293,17 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
* return {'html': '<h1>hello, world</h1>'}
|
* return {'html': '<h1>hello, world</h1>'}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderBanner: function () {
|
_renderBanner: function () {
|
||||||
if (this.bannerRoute !== undefined) {
|
if (this.bannerRoute !== undefined) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.dp
|
return this.dp
|
||||||
.add(this._rpc({route: this.bannerRoute}))
|
.add(this._rpc({ route: this.bannerRoute }))
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (!response.html) {
|
if (!response.html) {
|
||||||
self.$el.removeClass('o_has_banner');
|
self.$el.removeClass('o_has_banner');
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
self.$el.addClass('o_has_banner');
|
self.$el.addClass('o_has_banner');
|
||||||
var $banner = $(response.html);
|
var $banner = $(response.html);
|
||||||
@@ -313,22 +321,24 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
defs.push(ajax.loadJS(js.src));
|
defs.push(ajax.loadJS(js.src));
|
||||||
js.remove();
|
js.remove();
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
$banner.prependTo(self.$('> .o_content'));
|
$banner.prependTo(self.$('> .o_content'));
|
||||||
self._$banner = $banner;
|
self._$banner = $banner;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders the control elements (buttons, pager and sidebar) of the current
|
* Renders the control elements (buttons, pager and sidebar) of the current
|
||||||
* view.
|
* view.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} an object containing the control panel jQuery elements
|
* @returns {Promise<Object>} resolved with an object containing the control
|
||||||
|
* panel jQuery elements
|
||||||
*/
|
*/
|
||||||
_renderControlPanelElements: function () {
|
_renderControlPanelElements: function () {
|
||||||
|
var self = this;
|
||||||
var elements = {
|
var elements = {
|
||||||
$buttons: $('<div>'),
|
$buttons: $('<div>'),
|
||||||
$sidebar: $('<div>'),
|
$sidebar: $('<div>'),
|
||||||
@@ -336,15 +346,18 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.renderButtons(elements.$buttons);
|
this.renderButtons(elements.$buttons);
|
||||||
this.renderSidebar(elements.$sidebar);
|
var sidebarProm = this.renderSidebar(elements.$sidebar);
|
||||||
this.renderPager(elements.$pager);
|
var pagerProm = this.renderPager(elements.$pager);
|
||||||
// remove the unnecessary outer div
|
|
||||||
elements = _.mapObject(elements, function($node) {
|
|
||||||
return $node && $node.contents();
|
|
||||||
});
|
|
||||||
elements.$switch_buttons = this._renderSwitchButtons();
|
|
||||||
|
|
||||||
return elements;
|
return Promise.all([sidebarProm, pagerProm]).then(function () {
|
||||||
|
// remove the unnecessary outer div
|
||||||
|
elements = _.mapObject(elements, function ($node) {
|
||||||
|
return $node && $node.contents();
|
||||||
|
});
|
||||||
|
elements.$switch_buttons = self._renderSwitchButtons();
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders the switch buttons and binds listeners on them.
|
* Renders the switch buttons and binds listeners on them.
|
||||||
@@ -354,7 +367,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
*/
|
*/
|
||||||
_renderSwitchButtons: function () {
|
_renderSwitchButtons: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var views = _.filter(this.actionViews, {multiRecord: this.isMultiRecord});
|
var views = _.filter(this.actionViews, { multiRecord: this.isMultiRecord });
|
||||||
|
|
||||||
if (views.length <= 1) {
|
if (views.length <= 1) {
|
||||||
return $();
|
return $();
|
||||||
@@ -373,7 +386,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
var $switchButtonsFiltered = config.device.isMobile ? $switchButtons.find('button') : $switchButtons.filter('button');
|
var $switchButtonsFiltered = config.device.isMobile ? $switchButtons.find('button') : $switchButtons.filter('button');
|
||||||
$switchButtonsFiltered.click(_.debounce(function (event) {
|
$switchButtonsFiltered.click(_.debounce(function (event) {
|
||||||
var viewType = $(event.target).data('view-type');
|
var viewType = $(event.target).data('view-type');
|
||||||
self.trigger_up('switch_view', {view_type: viewType});
|
self.trigger_up('switch_view', { view_type: viewType });
|
||||||
}, 200, true));
|
}, 200, true));
|
||||||
|
|
||||||
// set active view's icon as view switcher button's icon in mobile
|
// set active view's icon as view switcher button's icon in mobile
|
||||||
@@ -402,7 +415,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} state the state given by the model
|
* @param {Object} state the state given by the model
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_update: function () {
|
_update: function () {
|
||||||
// AAB: update the control panel -> this will be moved elsewhere at some point
|
// AAB: update the control panel -> this will be moved elsewhere at some point
|
||||||
@@ -524,7 +537,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
|
|||||||
*/
|
*/
|
||||||
_onOpenRecord: function (ev) {
|
_onOpenRecord: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
var record = this.model.get(ev.data.id, {raw: true});
|
var record = this.model.get(ev.data.id, { raw: true });
|
||||||
this.trigger_up('switch_view', {
|
this.trigger_up('switch_view', {
|
||||||
view_type: 'form',
|
view_type: 'form',
|
||||||
res_id: record.res_id,
|
res_id: record.res_id,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ var AbstractModel = mvc.Model.extend({
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reload: function (params) {
|
reload: function (params) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Processes date(time) and selection field values sent by the server.
|
* Processes date(time) and selection field values sent by the server.
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ return mvc.Renderer.extend({
|
|||||||
this.noContentHelp = params.noContentHelp;
|
this.noContentHelp = params.noContentHelp;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* The rendering can be asynchronous (but it is not encouraged). The start
|
* The rendering is asynchronous. The start
|
||||||
* method simply makes sure that we render the view.
|
* method simply makes sure that we render the view.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this.$el.addClass(this.arch.attrs.class);
|
this.$el.addClass(this.arch.attrs.class);
|
||||||
return $.when(this._render(), this._super());
|
return Promise.all([this._render(), this._super()]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called each time the renderer is attached into the DOM.
|
* Called each time the renderer is attached into the DOM.
|
||||||
@@ -86,11 +86,11 @@ return mvc.Renderer.extend({
|
|||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {boolean} [params.noRender=false]
|
* @param {boolean} [params.noRender=false]
|
||||||
* if true, the method only updates the state without rerendering
|
* if true, the method only updates the state without rerendering
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
updateState: function (state, params) {
|
updateState: function (state, params) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
return params.noRender ? $.when() : this._render();
|
return params.noRender ? Promise.resolve() : this._render();
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -102,10 +102,10 @@ return mvc.Renderer.extend({
|
|||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -194,11 +194,12 @@ var AbstractView = Factory.extend({
|
|||||||
def = this._createControlPanel(parent);
|
def = this._createControlPanel(parent);
|
||||||
}
|
}
|
||||||
var _super = this._super.bind(this);
|
var _super = this._super.bind(this);
|
||||||
return $.when(def).then(function (controlPanel) {
|
return Promise.resolve(def).then(function (controlPanel) {
|
||||||
// get the parent of the model if it already exists, as _super will
|
// get the parent of the model if it already exists, as _super will
|
||||||
// set the new controller as parent, which we don't want
|
// set the new controller as parent, which we don't want
|
||||||
var modelParent = self.model && self.model.getParent();
|
var modelParent = self.model && self.model.getParent();
|
||||||
return _super(parent).done(function (controller) {
|
var prom = _super(parent);
|
||||||
|
prom.then(function (controller) {
|
||||||
if (controlPanel) {
|
if (controlPanel) {
|
||||||
controlPanel.setParent(controller);
|
controlPanel.setParent(controller);
|
||||||
}
|
}
|
||||||
@@ -207,7 +208,7 @@ var AbstractView = Factory.extend({
|
|||||||
self.model.setParent(modelParent);
|
self.model.setParent(modelParent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return prom;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
// savingDef is used to ensure that we always wait for pending save
|
// savingDef is used to ensure that we always wait for pending save
|
||||||
// operations to complete before checking if there are changes to
|
// operations to complete before checking if there are changes to
|
||||||
// discard when discardChanges is called
|
// discard when discardChanges is called
|
||||||
this.savingDef = $.when();
|
this.savingDef = Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
// add classname to reflect the (absence of) access rights (used to
|
// add classname to reflect the (absence of) access rights (used to
|
||||||
@@ -66,24 +66,27 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @param {string} [recordID] - default to main recordID
|
* @param {string} [recordID] - default to main recordID
|
||||||
* @returns {Deferred<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
* resolved if can be discarded, a boolean value is given to tells
|
* resolved if can be discarded, a boolean value is given to tells
|
||||||
* if there is something to discard or not
|
* if there is something to discard or not
|
||||||
* rejected otherwise
|
* rejected otherwise
|
||||||
*/
|
*/
|
||||||
canBeDiscarded: function (recordID) {
|
canBeDiscarded: function (recordID) {
|
||||||
|
var self = this;
|
||||||
if (!this.isDirty(recordID)) {
|
if (!this.isDirty(recordID)) {
|
||||||
return $.when(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = _t("The record has been modified, your changes will be discarded. Do you want to proceed?");
|
var message = _t("The record has been modified, your changes will be discarded. Do you want to proceed?");
|
||||||
var def = $.Deferred();
|
var def;
|
||||||
var dialog = Dialog.confirm(this, message, {
|
def = new Promise(function (resolve, reject) {
|
||||||
title: _t("Warning"),
|
var dialog = Dialog.confirm(self, message, {
|
||||||
confirm_callback: def.resolve.bind(def, true),
|
title: _t("Warning"),
|
||||||
cancel_callback: def.reject.bind(def),
|
confirm_callback: resolve.bind(self, true),
|
||||||
|
cancel_callback: reject,
|
||||||
|
});
|
||||||
|
dialog.on('closed', def, reject);
|
||||||
});
|
});
|
||||||
dialog.on('closed', def, def.reject);
|
|
||||||
return def;
|
return def;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +117,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* @see _.discardChanges
|
* @see _.discardChanges
|
||||||
*/
|
*/
|
||||||
discardChanges: function (recordID, options) {
|
discardChanges: function (recordID, options) {
|
||||||
return $.when(this.mutex.getUnlockedDef(), this.savingDef)
|
return Promise.all([this.mutex.getUnlockedDef(), this.savingDef])
|
||||||
.then(this._discardChanges.bind(this, recordID || this.handle, options));
|
.then(this._discardChanges.bind(this, recordID || this.handle, options));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -144,6 +147,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
renderPager: function ($node, options) {
|
renderPager: function ($node, options) {
|
||||||
|
var self = this;
|
||||||
var data = this.model.get(this.handle, {raw: true});
|
var data = this.model.get(this.handle, {raw: true});
|
||||||
this.pager = new Pager(this, data.count, data.offset + 1, data.limit, options);
|
this.pager = new Pager(this, data.count, data.offset + 1, data.limit, options);
|
||||||
|
|
||||||
@@ -161,19 +165,21 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
})
|
})
|
||||||
.then(this.pager.enable.bind(this.pager));
|
.then(this.pager.enable.bind(this.pager));
|
||||||
});
|
});
|
||||||
this.pager.appendTo($node);
|
return this.pager.appendTo($node).then(function() {
|
||||||
this._updatePager(); // to force proper visibility
|
self._updatePager(); // to force proper visibility
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Saves the record whose ID is given if necessary (@see _saveRecord).
|
* Saves the record whose ID is given if necessary (@see _saveRecord).
|
||||||
*
|
*
|
||||||
* @param {string} [recordID] - default to main recordID
|
* @param {string} [recordID] - default to main recordID
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
* Resolved with the list of field names (whose value has been modified)
|
* Resolved with the list of field names (whose value has been modified)
|
||||||
* Rejected if the record can't be saved
|
* Rejected if the record can't be saved
|
||||||
*/
|
*/
|
||||||
saveRecord: function (recordID, options) {
|
saveRecord: function (recordID, options) {
|
||||||
|
var self = this;
|
||||||
// Some field widgets can't detect (all) their changes immediately or
|
// Some field widgets can't detect (all) their changes immediately or
|
||||||
// may have to validate them before notifying them, so we ask them to
|
// may have to validate them before notifying them, so we ask them to
|
||||||
// commit their current value before saving. This has to be done outside
|
// commit their current value before saving. This has to be done outside
|
||||||
@@ -187,23 +193,22 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
// user if some discarding has to be made). This operation must also be
|
// user if some discarding has to be made). This operation must also be
|
||||||
// mutex-protected as commitChanges function of x2m has to be aware of
|
// mutex-protected as commitChanges function of x2m has to be aware of
|
||||||
// all final changes made to a row.
|
// all final changes made to a row.
|
||||||
var self = this;
|
var unlockedMutex = this.mutex.getUnlockedDef()
|
||||||
var savingDef = $.Deferred();
|
|
||||||
this.savingDef = savingDef;
|
|
||||||
|
|
||||||
return this.mutex.getUnlockedDef()
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return self.renderer.commitChanges(recordID || self.handle);
|
return self.renderer.commitChanges(recordID || self.handle);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return self.mutex.exec(self._saveRecord.bind(self, recordID, options));
|
return self.mutex.exec(self._saveRecord.bind(self, recordID, options));
|
||||||
})
|
});
|
||||||
.then(savingDef.resolve.bind(savingDef))
|
this.savingDef = new Promise(function (resolve) {
|
||||||
.fail(savingDef.resolve.bind(savingDef));
|
unlockedMutex.then(resolve).guardedCatch(resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
return unlockedMutex;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
update: function (params, options) {
|
update: function (params, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -267,31 +272,32 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} attrs the attrs of the button clicked
|
* @param {Object} attrs the attrs of the button clicked
|
||||||
* @param {Object} [record] the current state of the view
|
* @param {Object} [record] the current state of the view
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_callButtonAction: function (attrs, record) {
|
_callButtonAction: function (attrs, record) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def = $.Deferred();
|
var def = new Promise(function (resolve, reject) {
|
||||||
var reload = function () {
|
var reload = function () {
|
||||||
return self.isDestroyed() ? $.when() : self.reload();
|
return self.isDestroyed() ? Promise.resolve() : self.reload();
|
||||||
};
|
};
|
||||||
record = record || this.model.get(this.handle);
|
record = record || self.model.get(self.handle);
|
||||||
|
|
||||||
this.trigger_up('execute_action', {
|
self.trigger_up('execute_action', {
|
||||||
action_data: _.extend({}, attrs, {
|
action_data: _.extend({}, attrs, {
|
||||||
context: record.getContext({additionalContext: attrs.context || {}}),
|
context: record.getContext({additionalContext: attrs.context || {}}),
|
||||||
}),
|
}),
|
||||||
env: {
|
env: {
|
||||||
context: record.getContext(),
|
context: record.getContext(),
|
||||||
currentID: record.data.id,
|
currentID: record.data.id,
|
||||||
model: record.model,
|
model: record.model,
|
||||||
resIDs: record.res_ids,
|
resIDs: record.res_ids,
|
||||||
},
|
},
|
||||||
on_success: def.resolve.bind(def),
|
on_success: resolve,
|
||||||
on_fail: function () {
|
on_fail: function () {
|
||||||
self.update({}, {reload: false}).always(def.reject.bind(def));
|
self.update({}, {reload: false}).then(reject).guardedCatch(reject);
|
||||||
},
|
},
|
||||||
on_closed: reload,
|
on_closed: reload,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return this.alive(def);
|
return this.alive(def);
|
||||||
},
|
},
|
||||||
@@ -305,7 +311,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* @param {string} id - the id of one of the view's records
|
* @param {string} id - the id of one of the view's records
|
||||||
* @param {string[]} fields - the changed fields
|
* @param {string[]} fields - the changed fields
|
||||||
* @param {OdooEvent} e - the event that triggered the change
|
* @param {OdooEvent} e - the event that triggered the change
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmChange: function (id, fields, e) {
|
_confirmChange: function (id, fields, e) {
|
||||||
if (e.name === 'discard_changes' && e.target.reset) {
|
if (e.name === 'discard_changes' && e.target.reset) {
|
||||||
@@ -364,7 +370,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* the new record to be in edit mode too, but the view manager calls
|
* the new record to be in edit mode too, but the view manager calls
|
||||||
* this function as the URL changes...) @todo get rid of this when
|
* this function as the URL changes...) @todo get rid of this when
|
||||||
* the webclient/action_manager's hashchange mechanism is improved.
|
* the webclient/action_manager's hashchange mechanism is improved.
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_discardChanges: function (recordID, options) {
|
_discardChanges: function (recordID, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -459,7 +465,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* @param {boolean} [options.savePoint=false]
|
* @param {boolean} [options.savePoint=false]
|
||||||
* if true, the record will only be 'locally' saved: its changes
|
* if true, the record will only be 'locally' saved: its changes
|
||||||
* will move from the _changes key to the data key
|
* will move from the _changes key to the data key
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
* Resolved with the list of field names (whose value has been modified)
|
* Resolved with the list of field names (whose value has been modified)
|
||||||
* Rejected if the record can't be saved
|
* Rejected if the record can't be saved
|
||||||
*/
|
*/
|
||||||
@@ -490,7 +496,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
}
|
}
|
||||||
return saveDef;
|
return saveDef;
|
||||||
} else {
|
} else {
|
||||||
return $.Deferred().reject(); // Cannot be saved
|
return Promise.reject("SaveRecord: this.canBeSave is false"); // Cannot be saved
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -501,7 +507,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
* @private
|
* @private
|
||||||
* @param {string} mode - 'readonly' or 'edit'
|
* @param {string} mode - 'readonly' or 'edit'
|
||||||
* @param {string} [recordID]
|
* @param {string} [recordID]
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_setMode: function (mode, recordID) {
|
_setMode: function (mode, recordID) {
|
||||||
if ((recordID || this.handle) === this.handle) {
|
if ((recordID || this.handle) === this.handle) {
|
||||||
@@ -511,7 +517,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
core.bus.trigger('DOM_updated');
|
core.bus.trigger('DOM_updated');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Helper method, to get the current environment variables from the model
|
* Helper method, to get the current environment variables from the model
|
||||||
@@ -564,14 +570,14 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
var recordID = ev.data.recordID;
|
var recordID = ev.data.recordID;
|
||||||
this._discardChanges(recordID)
|
this._discardChanges(recordID)
|
||||||
.done(function () {
|
.then(function () {
|
||||||
// TODO this will tell the renderer to rerender the widget that
|
// TODO this will tell the renderer to rerender the widget that
|
||||||
// asked for the discard but will unfortunately lose the click
|
// asked for the discard but will unfortunately lose the click
|
||||||
// made on another row if any
|
// made on another row if any
|
||||||
self._confirmChange(recordID, [ev.data.fieldName], ev)
|
self._confirmChange(recordID, [ev.data.fieldName], ev)
|
||||||
.always(ev.data.onSuccess);
|
.then(ev.data.onSuccess).guardedCatch(ev.data.onSuccess);
|
||||||
})
|
})
|
||||||
.fail(ev.data.onFailure);
|
.guardedCatch(ev.data.onFailure);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Forces to save directly the changes if the controller is in readonly,
|
* Forces to save directly the changes if the controller is in readonly,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -61,13 +61,13 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* before notifying them).
|
* before notifying them).
|
||||||
*
|
*
|
||||||
* @param {string} recordID
|
* @param {string} recordID
|
||||||
* @return {Deferred}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
commitChanges: function (recordID) {
|
commitChanges: function (recordID) {
|
||||||
var defs = _.map(this.allFieldWidgets[recordID], function (widget) {
|
var defs = _.map(this.allFieldWidgets[recordID], function (widget) {
|
||||||
return widget.commitChanges();
|
return widget.commitChanges();
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Updates the internal state of the renderer to the new state. By default,
|
* Updates the internal state of the renderer to the new state. By default,
|
||||||
@@ -86,7 +86,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @param {string[]} fields
|
* @param {string[]} fields
|
||||||
* @param {OdooEvent} ev
|
* @param {OdooEvent} ev
|
||||||
* @returns {Deferred<AbstractField[]>} resolved with the list of widgets
|
* @returns {Promise<AbstractField[]>} resolved with the list of widgets
|
||||||
* that have been reset
|
* that have been reset
|
||||||
*/
|
*/
|
||||||
confirmChange: function (state, id, fields, ev) {
|
confirmChange: function (state, id, fields, ev) {
|
||||||
@@ -118,7 +118,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
// state before evaluation
|
// state before evaluation
|
||||||
defs.push(this._updateAllModifiers(record));
|
defs.push(this._updateAllModifiers(record));
|
||||||
|
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
return resetWidgets;
|
return resetWidgets;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -295,7 +295,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {AbstractField} widget
|
* @param {AbstractField} widget
|
||||||
* @returns {boolean|Deferred<boolean>} @see AbstractField.isValid
|
* @returns {boolean|Promise<boolean>} @see AbstractField.isValid
|
||||||
*/
|
*/
|
||||||
_canWidgetBeSaved: function (widget) {
|
_canWidgetBeSaved: function (widget) {
|
||||||
var modifiers = this._getEvaluatedModifiers(widget.__node, widget.record);
|
var modifiers = this._getEvaluatedModifiers(widget.__node, widget.record);
|
||||||
@@ -386,6 +386,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* @param {Object} node the attrs coming from the arch
|
* @param {Object} node the attrs coming from the arch
|
||||||
*/
|
*/
|
||||||
_postProcessField: function (widget, node) {
|
_postProcessField: function (widget, node) {
|
||||||
|
this._handleAttributes(widget.$el, node);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Registers or updates the modifiers data associated to the given node.
|
* Registers or updates the modifiers data associated to the given node.
|
||||||
@@ -503,7 +504,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* Render the view
|
* Render the view
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
var oldAllFieldWidgets = this.allFieldWidgets;
|
var oldAllFieldWidgets = this.allFieldWidgets;
|
||||||
@@ -520,7 +521,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
/**
|
/**
|
||||||
* Instantiates the appropriate AbstractField specialization for the given
|
* Instantiates the appropriate AbstractField specialization for the given
|
||||||
* node and prepares its rendering and addition to the DOM. Indeed, the
|
* node and prepares its rendering and addition to the DOM. Indeed, the
|
||||||
* rendering of the widget will be started and the associated deferred will
|
* rendering of the widget will be started and the associated promise will
|
||||||
* be added to the 'defs' attribute. This is supposed to be created and
|
* be added to the 'defs' attribute. This is supposed to be created and
|
||||||
* deleted by the calling code if necessary.
|
* deleted by the calling code if necessary.
|
||||||
*
|
*
|
||||||
@@ -559,28 +560,31 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
|
|
||||||
widget.__node = node; // TODO get rid of this if possible one day
|
widget.__node = node; // TODO get rid of this if possible one day
|
||||||
|
|
||||||
// Prepare widget rendering and save the related deferred
|
// Prepare widget rendering and save the related promise
|
||||||
var def = widget._widgetRenderAndInsert(function () {});
|
var def = widget._widgetRenderAndInsert(function () {});
|
||||||
var async = def.state() === 'pending';
|
var $el = $('<div>');
|
||||||
var $el = async ? $('<div>') : widget.$el;
|
|
||||||
if (async) {
|
this.defs.push(def);
|
||||||
this.defs.push(def);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the modifiers registration by associating the widget and by
|
// Update the modifiers registration by associating the widget and by
|
||||||
// giving the modifiers options now (as the potential callback is
|
// giving the modifiers options now (as the potential callback is
|
||||||
// associated to new widget)
|
// associated to new widget)
|
||||||
var self = this;
|
var self = this;
|
||||||
def.then(function () {
|
def.then(function () {
|
||||||
if (async) {
|
// when the caller of renderFieldWidget uses something like
|
||||||
$el.replaceWith(widget.$el);
|
// this.renderFieldWidget(...).addClass(...), the class is added on
|
||||||
}
|
// the temporary div and not on the actual element that will be
|
||||||
|
// rendered. As we do not return a promise and some callers cannot
|
||||||
|
// wait for this.defs, we copy those attributes to the final element.
|
||||||
|
widget.$el.attr($el.getAttributes());
|
||||||
|
|
||||||
|
$el.replaceWith(widget.$el);
|
||||||
self._registerModifiers(node, record, widget, {
|
self._registerModifiers(node, record, widget, {
|
||||||
callback: function (element, modifiers, record) {
|
callback: function (element, modifiers, record) {
|
||||||
element.$el.toggleClass('o_field_empty', !!(
|
element.$el.toggleClass('o_field_empty', !!(
|
||||||
record.data.id
|
record.data.id &&
|
||||||
&& (modifiers.readonly || mode === 'readonly')
|
(modifiers.readonly || mode === 'readonly') &&
|
||||||
&& !element.widget.isSet()
|
!element.widget.isSet()
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
keepBaseMode: !!options.keepBaseMode,
|
keepBaseMode: !!options.keepBaseMode,
|
||||||
@@ -618,10 +622,10 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
* possible.
|
* possible.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderView: function () {
|
_renderView: function () {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Instantiate custom widgets
|
* Instantiate custom widgets
|
||||||
@@ -637,22 +641,17 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
|
|
||||||
this.widgets.push(widget);
|
this.widgets.push(widget);
|
||||||
|
|
||||||
// Prepare widget rendering and save the related deferred
|
// Prepare widget rendering and save the related promise
|
||||||
var def = widget._widgetRenderAndInsert(function () {});
|
var def = widget._widgetRenderAndInsert(function () {});
|
||||||
var async = def.state() === 'pending';
|
this.defs.push(def);
|
||||||
if (async) {
|
var $el = $('<div>');
|
||||||
this.defs.push(def);
|
|
||||||
}
|
|
||||||
var $el = async ? $('<div>') : widget.$el;
|
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
def.then(function () {
|
def.then(function () {
|
||||||
self._handleAttributes(widget.$el, node);
|
self._handleAttributes(widget.$el, node);
|
||||||
self._registerModifiers(node, record, widget);
|
self._registerModifiers(node, record, widget);
|
||||||
widget.$el.addClass('o_widget');
|
widget.$el.addClass('o_widget');
|
||||||
if (async) {
|
$el.replaceWith(widget.$el);
|
||||||
$el.replaceWith(widget.$el);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return $el;
|
return $el;
|
||||||
@@ -709,7 +708,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @returns {Deferred} resolved once finished
|
* @returns {Promise} resolved once finished
|
||||||
*/
|
*/
|
||||||
_updateAllModifiers: function (record) {
|
_updateAllModifiers: function (record) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -722,7 +721,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
});
|
});
|
||||||
delete this.defs;
|
delete this.defs;
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ var BasicView = AbstractView.extend({
|
|||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadData: function (model) {
|
_loadData: function (model) {
|
||||||
if (this.recordID) {
|
if (this.recordID) {
|
||||||
@@ -195,7 +195,7 @@ var BasicView = AbstractView.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when(def).then(function () {
|
return Promise.resolve(def).then(function () {
|
||||||
return model.get(record.id);
|
return model.get(record.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ var CalendarController = AbstractController.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} to either 'prev', 'next' or 'today'
|
* @param {string} to either 'prev', 'next' or 'today'
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_move: function (to) {
|
_move: function (to) {
|
||||||
this.model[to]();
|
this.model[to]();
|
||||||
@@ -153,7 +153,7 @@ var CalendarController = AbstractController.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {integer} record.id
|
* @param {integer} record.id
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_updateRecord: function (record) {
|
_updateRecord: function (record) {
|
||||||
return this.model.updateRecord(record).then(this.reload.bind(this, {}));
|
return this.model.updateRecord(record).then(this.reload.bind(this, {}));
|
||||||
@@ -388,18 +388,18 @@ var CalendarController = AbstractController.extend({
|
|||||||
self.quick.destroy();
|
self.quick.destroy();
|
||||||
self.quick = null;
|
self.quick = null;
|
||||||
self.reload();
|
self.reload();
|
||||||
|
self.quickCreating = false;
|
||||||
})
|
})
|
||||||
.fail(function (error, errorEvent) {
|
.guardedCatch(function (result) {
|
||||||
|
var errorEvent = result.event;
|
||||||
// This will occurs if there are some more fields required
|
// This will occurs if there are some more fields required
|
||||||
// Preventdefaulting the error event will prevent the traceback window
|
// Preventdefaulting the error event will prevent the traceback window
|
||||||
errorEvent.preventDefault();
|
errorEvent.preventDefault();
|
||||||
event.data.options.disableQuickCreate = true;
|
event.data.options.disableQuickCreate = true;
|
||||||
event.data.data.on_save = self.quick.destroy.bind(self.quick);
|
event.data.data.on_save = self.quick.destroy.bind(self.quick);
|
||||||
self._onOpenCreate(event.data);
|
self._onOpenCreate(event.data);
|
||||||
})
|
|
||||||
.always(function () {
|
|
||||||
self.quickCreating = false;
|
self.quickCreating = false;
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when we want to open or close the sidebar.
|
* Called when we want to open or close the sidebar.
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ return AbstractModel.extend({
|
|||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @param {any} params
|
* @param {any} params
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
load: function (params) {
|
load: function (params) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -199,15 +199,18 @@ return AbstractModel.extend({
|
|||||||
|
|
||||||
// fields to display color, e.g.: user_id.partner_id
|
// fields to display color, e.g.: user_id.partner_id
|
||||||
this.fieldColor = params.fieldColor;
|
this.fieldColor = params.fieldColor;
|
||||||
if (!this.preload_def) {
|
if (!this.preloadPromise) {
|
||||||
this.preload_def = $.Deferred();
|
this.preloadPromise = new Promise(function (resolve, reject) {
|
||||||
$.when(
|
Promise.all([
|
||||||
this._rpc({model: this.modelName, method: 'check_access_rights', args: ["write", false]}),
|
self._rpc({model: self.modelName, method: 'check_access_rights', args: ["write", false]}),
|
||||||
this._rpc({model: this.modelName, method: 'check_access_rights', args: ["create", false]}))
|
self._rpc({model: self.modelName, method: 'check_access_rights', args: ["create", false]})
|
||||||
.then(function (write, create) {
|
]).then(function (result) {
|
||||||
self.write_right = write;
|
var write = result[0];
|
||||||
self.create_right = create;
|
var create = result[1];
|
||||||
self.preload_def.resolve();
|
self.write_right = write;
|
||||||
|
self.create_right = create;
|
||||||
|
resolve();
|
||||||
|
}).guardedCatch(reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +232,7 @@ return AbstractModel.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.preload_def.then(this._loadCalendar.bind(this));
|
return this.preloadPromise.then(this._loadCalendar.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Move the current date range to the next period
|
* Move the current date range to the next period
|
||||||
@@ -247,7 +250,7 @@ return AbstractModel.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @param {Object} [params.context]
|
* @param {Object} [params.context]
|
||||||
* @param {Array} [params.domain]
|
* @param {Array} [params.domain]
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reload: function (handle, params) {
|
reload: function (handle, params) {
|
||||||
if (params.domain) {
|
if (params.domain) {
|
||||||
@@ -308,7 +311,7 @@ return AbstractModel.extend({
|
|||||||
/**
|
/**
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {integer} record.id
|
* @param {integer} record.id
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
updateRecord: function (record) {
|
updateRecord: function (record) {
|
||||||
// Cannot modify actual name yet
|
// Cannot modify actual name yet
|
||||||
@@ -430,7 +433,7 @@ return AbstractModel.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadCalendar: function () {
|
_loadCalendar: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -439,7 +442,7 @@ return AbstractModel.extend({
|
|||||||
|
|
||||||
var defs = _.map(this.data.filters, this._loadFilter.bind(this));
|
var defs = _.map(this.data.filters, this._loadFilter.bind(this));
|
||||||
|
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
return self._rpc({
|
return self._rpc({
|
||||||
model: self.modelName,
|
model: self.modelName,
|
||||||
method: 'search_read',
|
method: 'search_read',
|
||||||
@@ -450,10 +453,10 @@ return AbstractModel.extend({
|
|||||||
.then(function (events) {
|
.then(function (events) {
|
||||||
self._parseServerData(events);
|
self._parseServerData(events);
|
||||||
self.data.data = _.map(events, self._recordToCalendarEvent.bind(self));
|
self.data.data = _.map(events, self._recordToCalendarEvent.bind(self));
|
||||||
return $.when(
|
return Promise.all([
|
||||||
self._loadColors(self.data, self.data.data),
|
self._loadColors(self.data, self.data.data),
|
||||||
self._loadRecordsToFilters(self.data, self.data.data)
|
self._loadRecordsToFilters(self.data, self.data.data)
|
||||||
);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -461,7 +464,7 @@ return AbstractModel.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {any} element
|
* @param {any} element
|
||||||
* @param {any} events
|
* @param {any} events
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadColors: function (element, events) {
|
_loadColors: function (element, events) {
|
||||||
if (this.fieldColor) {
|
if (this.fieldColor) {
|
||||||
@@ -472,16 +475,16 @@ return AbstractModel.extend({
|
|||||||
});
|
});
|
||||||
this.model_color = this.fields[fieldName].relation || element.model;
|
this.model_color = this.fields[fieldName].relation || element.model;
|
||||||
}
|
}
|
||||||
return $.Deferred().resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {any} filter
|
* @param {any} filter
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadFilter: function (filter) {
|
_loadFilter: function (filter) {
|
||||||
if (!filter.write_model) {
|
if (!filter.write_model) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
var field = this.fields[filter.fieldName];
|
var field = this.fields[filter.fieldName];
|
||||||
@@ -540,7 +543,7 @@ return AbstractModel.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {any} element
|
* @param {any} element
|
||||||
* @param {any} events
|
* @param {any} events
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadRecordsToFilters: function (element, events) {
|
_loadRecordsToFilters: function (element, events) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -608,7 +611,7 @@ return AbstractModel.extend({
|
|||||||
to_read[model] = _.object(res);
|
to_read[model] = _.object(res);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
_.each(self.data.filters, function (filter) {
|
_.each(self.data.filters, function (filter) {
|
||||||
if (filter.write_model) {
|
if (filter.write_model) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
|
|||||||
});
|
});
|
||||||
defs.push(def);
|
defs.push(def);
|
||||||
}
|
}
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -193,7 +193,7 @@ return AbstractRenderer.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this._initSidebar();
|
this._initSidebar();
|
||||||
@@ -460,7 +460,7 @@ return AbstractRenderer.extend({
|
|||||||
*
|
*
|
||||||
* @override method from AbstractRenderer
|
* @override method from AbstractRenderer
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
var $calendar = this.$calendar;
|
var $calendar = this.$calendar;
|
||||||
@@ -504,7 +504,7 @@ return AbstractRenderer.extend({
|
|||||||
this.$sidebar_container.toggleClass('o_sidebar_hidden', fullWidth);
|
this.$sidebar_container.toggleClass('o_sidebar_hidden', fullWidth);
|
||||||
this.$sidebar.toggleClass('o_hidden', fullWidth);
|
this.$sidebar.toggleClass('o_hidden', fullWidth);
|
||||||
|
|
||||||
this._renderFilters();
|
var filterProm = this._renderFilters();
|
||||||
this.$calendar.appendTo('body');
|
this.$calendar.appendTo('body');
|
||||||
if (scrollTop) {
|
if (scrollTop) {
|
||||||
this.$calendar.fullCalendar('reinitView');
|
this.$calendar.fullCalendar('reinitView');
|
||||||
@@ -514,7 +514,7 @@ return AbstractRenderer.extend({
|
|||||||
this._renderEvents();
|
this._renderEvents();
|
||||||
this.$calendar.prependTo(this.$('.o_calendar_view'));
|
this.$calendar.prependTo(this.$('.o_calendar_view'));
|
||||||
|
|
||||||
return this._super.apply(this, arguments);
|
return Promise.all([filterProm, this._super.apply(this, arguments)]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Render all events
|
* Render all events
|
||||||
@@ -529,25 +529,47 @@ return AbstractRenderer.extend({
|
|||||||
* Render all filters
|
* Render all filters
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
|
* @returns {Promise} resolved when all filters have been rendered
|
||||||
*/
|
*/
|
||||||
_renderFilters: function () {
|
_renderFilters: function () {
|
||||||
var self = this;
|
|
||||||
_.each(this.filters || (this.filters = []), function (filter) {
|
_.each(this.filters || (this.filters = []), function (filter) {
|
||||||
filter.destroy();
|
filter.destroy();
|
||||||
});
|
});
|
||||||
if (this.state.fullWidth) {
|
if (this.state.fullWidth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_.each(this.state.filters, function (options) {
|
return this._renderFiltersOneByOne();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Renders each filter one by one, waiting for the first filter finished to
|
||||||
|
* be rendered and appended to render the next one.
|
||||||
|
* We need to do like this since render a filter is asynchronous, we don't
|
||||||
|
* know which one will be appened at first and we want tp force them to be
|
||||||
|
* rendered in order.
|
||||||
|
*
|
||||||
|
* @param {number} filterIndex if not set, 0 by default
|
||||||
|
* @returns {Promise} resolved when all filters have been rendered
|
||||||
|
*/
|
||||||
|
_renderFiltersOneByOne: function (filterIndex) {
|
||||||
|
filterIndex = filterIndex || 0;
|
||||||
|
var arrFilters = _.toArray(this.state.filters);
|
||||||
|
var prom;
|
||||||
|
if (filterIndex < arrFilters.length) {
|
||||||
|
var options = arrFilters[filterIndex];
|
||||||
if (!_.find(options.filters, function (f) {return f.display == null || f.display;})) {
|
if (!_.find(options.filters, function (f) {return f.display == null || f.display;})) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
options.getColor = self.getColor.bind(self);
|
|
||||||
options.fields = self.state.fields;
|
var self = this;
|
||||||
|
options.getColor = this.getColor.bind(this);
|
||||||
|
options.fields = this.state.fields;
|
||||||
var filter = new SidebarFilter(self, options);
|
var filter = new SidebarFilter(self, options);
|
||||||
filter.appendTo(self.$sidebar);
|
prom = filter.appendTo(this.$sidebar).then(function () {
|
||||||
self.filters.push(filter);
|
return self._renderFiltersOneByOne(filterIndex + 1);
|
||||||
});
|
});
|
||||||
|
this.filters.push(filter);
|
||||||
|
}
|
||||||
|
return Promise.resolve(prom);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -63,14 +63,14 @@ var ControlPanelController = mvc.Controller.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {Object} state a ControlPanelModel state
|
* @param {Object} state a ControlPanelModel state
|
||||||
* @returns {Deferred<Object>} the result of `getSearchState`
|
* @returns {Promise<Object>} the result of `getSearchState`
|
||||||
*/
|
*/
|
||||||
importState: function (state) {
|
importState: function (state) {
|
||||||
var defs = [];
|
var defs = [];
|
||||||
this.model.importState(state);
|
this.model.importState(state);
|
||||||
defs.push(this.getSearchQuery());
|
defs.push(this.getSearchQuery());
|
||||||
defs.push(this.renderer.updateState(this.model.get()));
|
defs.push(this.renderer.updateState(this.model.get()));
|
||||||
return $.when(defs).then(function (defsResults) {
|
return Promise.all(defs).then(function (defsResults) {
|
||||||
return defsResults[0];
|
return defsResults[0];
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -127,7 +127,7 @@ var ControlPanelController = mvc.Controller.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_reportNewQueryAndRender: function () {
|
_reportNewQueryAndRender: function () {
|
||||||
this.trigger_up('search', this.model.getQuery());
|
this.trigger_up('search', this.model.getQuery());
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ var ControlPanelModel = mvc.Model.extend({
|
|||||||
* @param {string[]} [params.searchMenuTypes=[]]
|
* @param {string[]} [params.searchMenuTypes=[]]
|
||||||
* @param {Object} [params.timeRanges]
|
* @param {Object} [params.timeRanges]
|
||||||
* @param {boolean} [params.withSearchBar]
|
* @param {boolean} [params.withSearchBar]
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
load: function (params) {
|
load: function (params) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -337,11 +337,11 @@ var ControlPanelModel = mvc.Model.extend({
|
|||||||
// and the info comming from arch put in params.groups (if any)
|
// and the info comming from arch put in params.groups (if any)
|
||||||
// will be lost. This is not a problem because the state
|
// will be lost. This is not a problem because the state
|
||||||
// won't be use elsewhere.
|
// won't be use elsewhere.
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
if (params.initialState) {
|
if (params.initialState) {
|
||||||
this.importState(params.initialState);
|
this.importState(params.initialState);
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
var groups = params.groups || [];
|
var groups = params.groups || [];
|
||||||
groups.forEach(function (group) {
|
groups.forEach(function (group) {
|
||||||
@@ -351,7 +351,7 @@ var ControlPanelModel = mvc.Model.extend({
|
|||||||
this._createEmptyGroup('groupBy');
|
this._createEmptyGroup('groupBy');
|
||||||
}
|
}
|
||||||
this._createGroupOfTimeRanges();
|
this._createGroupOfTimeRanges();
|
||||||
return $.when.apply($, self._loadSearchDefaults()).then(function () {
|
return Promise.all(self._loadSearchDefaults()).then(function () {
|
||||||
return self._loadFavorites().then(function () {
|
return self._loadFavorites().then(function () {
|
||||||
if (self.query.length === 0) {
|
if (self.query.length === 0) {
|
||||||
self._activateDefaultFilters();
|
self._activateDefaultFilters();
|
||||||
@@ -910,7 +910,7 @@ var ControlPanelModel = mvc.Model.extend({
|
|||||||
* if this.activateDefaultFavorite is true.
|
* if this.activateDefaultFavorite is true.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadFavorites: function () {
|
_loadFavorites: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -980,7 +980,7 @@ var ControlPanelModel = mvc.Model.extend({
|
|||||||
* is asynchronous.
|
* is asynchronous.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred[]}
|
* @returns {Promise[]}
|
||||||
*/
|
*/
|
||||||
_loadSearchDefaults: function () {
|
_loadSearchDefaults: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -1044,7 +1044,7 @@ var ControlPanelModel = mvc.Model.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} favorite
|
* @param {Object} favorite
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_saveQuery: function (favorite) {
|
_saveQuery: function (favorite) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ var ControlPanelRenderer = Renderer.extend({
|
|||||||
|
|
||||||
var superDef = this._super.apply(this, arguments);
|
var superDef = this._super.apply(this, arguments);
|
||||||
var searchDef = this._renderSearch();
|
var searchDef = this._renderSearch();
|
||||||
return $.when(superDef, searchDef).then(function () {
|
return Promise.all([superDef, searchDef]).then(function () {
|
||||||
self._setSearchMenusVisibility();
|
self._setSearchMenusVisibility();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -148,7 +148,7 @@ var ControlPanelRenderer = Renderer.extend({
|
|||||||
* Update the state of the renderer state. It retriggers a full rerendering.
|
* Update the state of the renderer state. It retriggers a full rerendering.
|
||||||
*
|
*
|
||||||
* @param {Object} state
|
* @param {Object} state
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
updateState: function (state) {
|
updateState: function (state) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
@@ -261,7 +261,7 @@ var ControlPanelRenderer = Renderer.extend({
|
|||||||
* Renderer the search bar and the search menus
|
* Renderer the search bar and the search menus
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderSearch: function () {
|
_renderSearch: function () {
|
||||||
var defs = [];
|
var defs = [];
|
||||||
@@ -274,11 +274,11 @@ var ControlPanelRenderer = Renderer.extend({
|
|||||||
if (this.withSearchBar) {
|
if (this.withSearchBar) {
|
||||||
defs.push(this._renderSearchBar());
|
defs.push(this._renderSearchBar());
|
||||||
}
|
}
|
||||||
return $.when(this, defs).then(this._focusSearchInput.bind(this));
|
return Promise.all(defs).then(this._focusSearchInput.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderSearchBar: function () {
|
_renderSearchBar: function () {
|
||||||
// TODO: might need a reload instead of a destroy/instantiate
|
// TODO: might need a reload instead of a destroy/instantiate
|
||||||
@@ -315,7 +315,7 @@ var ControlPanelRenderer = Renderer.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} menuType
|
* @param {string} menuType
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_setupMenu: function (menuType) {
|
_setupMenu: function (menuType) {
|
||||||
var Menu;
|
var Menu;
|
||||||
@@ -345,7 +345,7 @@ var ControlPanelRenderer = Renderer.extend({
|
|||||||
* Instantiate the search menu determined by this.searchMenuTypes.
|
* Instantiate the search menu determined by this.searchMenuTypes.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred[]}
|
* @returns {Promise[]}
|
||||||
*/
|
*/
|
||||||
_setupMenus: function () {
|
_setupMenus: function () {
|
||||||
this.$subMenus = this._getSubMenusPlace();
|
this.$subMenus = this._getSubMenusPlace();
|
||||||
|
|||||||
@@ -67,14 +67,14 @@ var FilterMenu = DropdownMenu.extend({
|
|||||||
* Add a proposition inside the custom filter edition menu.
|
* Add a proposition inside the custom filter edition menu.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {$.Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_appendProposition: function () {
|
_appendProposition: function () {
|
||||||
// make modern sear_filters code!!! It works but...
|
// make modern sear_filters code!!! It works but...
|
||||||
var prop = new search_filters.ExtendedSearchProposition(this, this.fields);
|
var prop = new search_filters.ExtendedSearchProposition(this, this.fields);
|
||||||
this.propositions.push(prop);
|
this.propositions.push(prop);
|
||||||
this.$('.o_apply_filter').prop('disabled', false);
|
this.$('.o_apply_filter').prop('disabled', false);
|
||||||
prop.insertBefore(this.$addFilterMenu);
|
return prop.insertBefore(this.$addFilterMenu);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Confirm a filter proposition, creates it and add it to the menu.
|
* Confirm a filter proposition, creates it and add it to the menu.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ var SearchBar = Widget.extend({
|
|||||||
defs.push(self._renderFacet(facet));
|
defs.push(self._renderFacet(facet));
|
||||||
});
|
});
|
||||||
defs.push(this._setupAutoCompletion());
|
defs.push(this._setupAutoCompletion());
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -116,18 +116,21 @@ var SearchBar = Widget.extend({
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
_getAutoCompleteSources: function (req, callback) {
|
_getAutoCompleteSources: function (req, callback) {
|
||||||
var defs = _.invoke(this.autoCompleteSources, 'getAutocompletionValues', req.term);
|
var defs = this.autoCompleteSources.map(function (source) {
|
||||||
$.when.apply($, defs).then(function () {
|
return source.getAutocompletionValues(req.term);
|
||||||
callback(_(arguments).chain()
|
});
|
||||||
|
Promise.all(defs).then(function (result) {
|
||||||
|
var resultCleaned = _(result).chain()
|
||||||
.compact()
|
.compact()
|
||||||
.flatten(true)
|
.flatten(true)
|
||||||
.value());
|
.value();
|
||||||
});
|
callback(resultCleaned);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} facet
|
* @param {Object} facet
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderFacet: function (facet) {
|
_renderFacet: function (facet) {
|
||||||
var searchFacet = new SearchFacet(this, facet);
|
var searchFacet = new SearchFacet(this, facet);
|
||||||
@@ -136,7 +139,7 @@ var SearchBar = Widget.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_setupAutoCompletion: function () {
|
_setupAutoCompletion: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
+12
-12
@@ -32,7 +32,7 @@ var FilterInterface = Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
* label. They will be used by search_bar in @_onAutoCompleteSelected.
|
* label. They will be used by search_bar in @_onAutoCompleteSelected.
|
||||||
*
|
*
|
||||||
* @param {string} value value to getAutocompletionValues
|
* @param {string} value value to getAutocompletionValues
|
||||||
* @returns {Deferred<null|Array>}
|
* @returns {Promise<null|Array>}
|
||||||
*/
|
*/
|
||||||
getAutocompletionValues: function (value) {
|
getAutocompletionValues: function (value) {
|
||||||
var result;
|
var result;
|
||||||
@@ -46,7 +46,7 @@ var FilterInterface = Class.extend(mixins.EventDispatcherMixin, {
|
|||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
return $.when(result);
|
return Promise.resolve(result);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ var Field = FilterInterface.extend(ServicesMixin, {
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
getAutocompletionValues: function (value) {
|
getAutocompletionValues: function (value) {
|
||||||
return $.when([{
|
return Promise.resolve([{
|
||||||
label: this._getAutocompletionLabel(value),
|
label: this._getAutocompletionLabel(value),
|
||||||
facet: this._getFacetValue(value),
|
facet: this._getFacetValue(value),
|
||||||
}]);
|
}]);
|
||||||
@@ -194,7 +194,7 @@ var CharField = Field.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
getAutocompletionValues: function (value) {
|
getAutocompletionValues: function (value) {
|
||||||
if (_.isEmpty(value)) { return $.when(null); }
|
if (_.isEmpty(value)) { return Promise.resolve(null); }
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -210,7 +210,7 @@ var NumberField = Field.extend({
|
|||||||
*/
|
*/
|
||||||
getAutocompletionValues: function (value) {
|
getAutocompletionValues: function (value) {
|
||||||
var val = this.parse(value);
|
var val = this.parse(value);
|
||||||
if (isNaN(val)) { return $.when(); }
|
if (isNaN(val)) { return Promise.resolve(); }
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -261,8 +261,8 @@ var SelectionField = Field.extend({
|
|||||||
facet: self._getFacetValue(sel)
|
facet: self._getFacetValue(sel)
|
||||||
};
|
};
|
||||||
}).value();
|
}).value();
|
||||||
if (_.isEmpty(results)) { return $.when(null); }
|
if (_.isEmpty(results)) { return Promise.resolve(null); }
|
||||||
return $.when.call(null, [{
|
return Promise.resolve([{
|
||||||
label: _.escape(this.attrs.string)
|
label: _.escape(this.attrs.string)
|
||||||
}].concat(results));
|
}].concat(results));
|
||||||
},
|
},
|
||||||
@@ -310,15 +310,15 @@ var DateField = Field.extend({
|
|||||||
t = (this.attrs && this.attrs.type === 'datetime') ? 'datetime' : 'date';
|
t = (this.attrs && this.attrs.type === 'datetime') ? 'datetime' : 'date';
|
||||||
v = field_utils.parse[t](value, {type: t}, {timezone: true});
|
v = field_utils.parse[t](value, {type: t}, {timezone: true});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return $.when(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var m = moment(v, t === 'datetime' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
|
var m = moment(v, t === 'datetime' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
|
||||||
if (!m.isValid()) { return $.when(null); }
|
if (!m.isValid()) { return Promise.resolve(null); }
|
||||||
var dateString = field_utils.format[t](m, {type: t});
|
var dateString = field_utils.format[t](m, {type: t});
|
||||||
var label = this._getAutocompletionLabel(dateString);
|
var label = this._getAutocompletionLabel(dateString);
|
||||||
var facet = this._getFacetValue(dateString, m.toDate());
|
var facet = this._getFacetValue(dateString, m.toDate());
|
||||||
return $.when([{
|
return Promise.resolve([{
|
||||||
label: label,
|
label: label,
|
||||||
facet: facet,
|
facet: facet,
|
||||||
}]);
|
}]);
|
||||||
@@ -379,9 +379,9 @@ var ManyToOneField = CharField.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
getAutocompletionValues: function (value) {
|
getAutocompletionValues: function (value) {
|
||||||
if (_.isEmpty(value)) { return $.when(null); }
|
if (_.isEmpty(value)) { return Promise.resolve(null); }
|
||||||
var label = this._getAutocompletionLabel(value);
|
var label = this._getAutocompletionLabel(value);
|
||||||
return $.when([{
|
return Promise.resolve([{
|
||||||
label: label,
|
label: label,
|
||||||
facet: this._getFacetValue(value),
|
facet: this._getFacetValue(value),
|
||||||
expand: this._expand.bind(this),
|
expand: this._expand.bind(this),
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ var ExtendedSearchProposition = Widget.extend({
|
|||||||
this.value = null;
|
this.value = null;
|
||||||
},
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
return this._super().done(this.proxy('changed'));
|
var parent = this._super();
|
||||||
|
parent.then(this.proxy('changed'));
|
||||||
|
return parent;
|
||||||
},
|
},
|
||||||
changed: function () {
|
changed: function () {
|
||||||
var nval = this.$(".o_searchview_extended_prop_field").val();
|
var nval = this.$(".o_searchview_extended_prop_field").val();
|
||||||
@@ -234,10 +236,10 @@ var DateTime = Field.extend({
|
|||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
return $.when(
|
return Promise.all([
|
||||||
this._super.apply(this, arguments),
|
this._super.apply(this, arguments),
|
||||||
this._create_new_widget("datewidget_0")
|
this._create_new_widget("datewidget_0")
|
||||||
);
|
]);
|
||||||
},
|
},
|
||||||
_create_new_widget: function (name) {
|
_create_new_widget: function (name) {
|
||||||
this[name] = new (this._get_widget_class())(this);
|
this[name] = new (this._get_widget_class())(this);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ var FieldManagerMixin = {
|
|||||||
* @param {string} dataPointID
|
* @param {string} dataPointID
|
||||||
* @param {Object} changes
|
* @param {Object} changes
|
||||||
* @param {OdooEvent} event
|
* @param {OdooEvent} event
|
||||||
* @returns {Deferred} resolves when the change has been done, and the UI
|
* @returns {Promise} resolves when the change has been done, and the UI
|
||||||
* updated
|
* updated
|
||||||
*/
|
*/
|
||||||
_applyChanges: function (dataPointID, changes, event) {
|
_applyChanges: function (dataPointID, changes, event) {
|
||||||
@@ -66,10 +66,10 @@ var FieldManagerMixin = {
|
|||||||
* @param {string} id basicModel Id for the changed record
|
* @param {string} id basicModel Id for the changed record
|
||||||
* @param {string[]} fields the fields (names) that have been changed
|
* @param {string[]} fields the fields (names) that have been changed
|
||||||
* @param {OdooEvent} event the event that triggered the change
|
* @param {OdooEvent} event the event that triggered the change
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmChange: function (id, fields, event) {
|
_confirmChange: function (id, fields, event) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* This method will be called whenever a save has been triggered by a change
|
* This method will be called whenever a save has been triggered by a change
|
||||||
@@ -79,10 +79,10 @@ var FieldManagerMixin = {
|
|||||||
* @see _onFieldChanged
|
* @see _onFieldChanged
|
||||||
* @abstract
|
* @abstract
|
||||||
* @param {string} id The basicModel ID for the saved record
|
* @param {string} id The basicModel ID for the saved record
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmSave: function (id) {
|
_confirmSave: function (id) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -103,9 +103,9 @@ var FieldManagerMixin = {
|
|||||||
// subrecord's form view), otherwise it bubbles up to the main form view
|
// subrecord's form view), otherwise it bubbles up to the main form view
|
||||||
// but its model doesn't have any data related to the given dataPointID
|
// but its model doesn't have any data related to the given dataPointID
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this._applyChanges(event.data.dataPointID, event.data.changes, event)
|
return this._applyChanges(event.data.dataPointID, event.data.changes, event)
|
||||||
.done(event.data.onSuccess || function () {})
|
.then(event.data.onSuccess || function () {})
|
||||||
.fail(event.data.onFailure || function () {});
|
.guardedCatch(event.data.onFailure || function () {});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Some widgets need to trigger a reload of their data. For example, a
|
* Some widgets need to trigger a reload of their data. For example, a
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ var FormController = BasicController.extend({
|
|||||||
* @todo make record creation a basic controller feature
|
* @todo make record creation a basic controller feature
|
||||||
* @param {string} [parentID] if given, the parentID will be used as parent
|
* @param {string} [parentID] if given, the parentID will be used as parent
|
||||||
* for the new record.
|
* for the new record.
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
createRecord: function (parentID) {
|
createRecord: function (parentID) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -165,20 +165,23 @@ var FormController = BasicController.extend({
|
|||||||
* @override method from BasicController
|
* @override method from BasicController
|
||||||
* @param {jQueryElement} $node
|
* @param {jQueryElement} $node
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renderPager: function ($node, options) {
|
renderPager: function ($node, options) {
|
||||||
options = _.extend({}, options, {
|
options = _.extend({}, options, {
|
||||||
validate: this.canBeDiscarded.bind(this),
|
validate: this.canBeDiscarded.bind(this),
|
||||||
});
|
});
|
||||||
this._super($node, options);
|
return this._super($node, options);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Instantiate and render the sidebar if a sidebar is requested
|
* Instantiate and render the sidebar if a sidebar is requested
|
||||||
* Sets this.sidebar
|
* Sets this.sidebar
|
||||||
* @param {jQuery} [$node] a jQuery node where the sidebar should be
|
* @param {jQuery} [$node] a jQuery node where the sidebar should be
|
||||||
* inserted
|
* inserted
|
||||||
|
* @return {Promise}
|
||||||
**/
|
**/
|
||||||
renderSidebar: function ($node) {
|
renderSidebar: function ($node) {
|
||||||
|
var self = this;
|
||||||
if (this.hasSidebar) {
|
if (this.hasSidebar) {
|
||||||
var otherItems = [];
|
var otherItems = [];
|
||||||
if (this.is_action_enabled('delete')) {
|
if (this.is_action_enabled('delete')) {
|
||||||
@@ -203,11 +206,12 @@ var FormController = BasicController.extend({
|
|||||||
},
|
},
|
||||||
actions: _.extend(this.toolbarActions, {other: otherItems}),
|
actions: _.extend(this.toolbarActions, {other: otherItems}),
|
||||||
});
|
});
|
||||||
this.sidebar.appendTo($node);
|
return this.sidebar.appendTo($node).then(function() {
|
||||||
|
// Show or hide the sidebar according to the view mode
|
||||||
// Show or hide the sidebar according to the view mode
|
self._updateSidebar();
|
||||||
this._updateSidebar();
|
});
|
||||||
}
|
}
|
||||||
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Show a warning message if the user modified a translated field. For each
|
* Show a warning message if the user modified a translated field. For each
|
||||||
@@ -296,7 +300,7 @@ var FormController = BasicController.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @override method from field manager mixin
|
* @override method from field manager mixin
|
||||||
* @param {string} id - id of the previously changed record
|
* @param {string} id - id of the previously changed record
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmSave: function (id) {
|
_confirmSave: function (id) {
|
||||||
if (id === this.handle) {
|
if (id === this.handle) {
|
||||||
@@ -393,14 +397,19 @@ var FormController = BasicController.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} state
|
* @param {Object} state
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_update: function () {
|
_update: function () {
|
||||||
var title = this.getTitle();
|
var self = this;
|
||||||
this._setTitle(title);
|
|
||||||
this._updateButtons();
|
return this._super.apply(this, arguments).then(function() {
|
||||||
this._updateSidebar();
|
var title = self.getTitle();
|
||||||
return this._super.apply(this, arguments).then(this.autofocus.bind(this));
|
self._setTitle(title);
|
||||||
|
self._updateButtons();
|
||||||
|
self._updateSidebar();
|
||||||
|
|
||||||
|
self.autofocus();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -470,13 +479,11 @@ var FormController = BasicController.extend({
|
|||||||
}
|
}
|
||||||
var attrs = ev.data.attrs;
|
var attrs = ev.data.attrs;
|
||||||
if (attrs.confirm) {
|
if (attrs.confirm) {
|
||||||
var d = $.Deferred();
|
def = new Promise(function (resolve, reject) {
|
||||||
Dialog.confirm(this, attrs.confirm, {
|
Dialog.confirm(this, attrs.confirm, {
|
||||||
confirm_callback: saveAndExecuteAction,
|
confirm_callback: saveAndExecuteAction,
|
||||||
}).on("closed", null, function () {
|
}).on("closed", null, resolve);
|
||||||
d.resolve();
|
|
||||||
});
|
});
|
||||||
def = d.promise();
|
|
||||||
} else if (attrs.special === 'cancel') {
|
} else if (attrs.special === 'cancel') {
|
||||||
def = this._callButtonAction(attrs, ev.data.record);
|
def = this._callButtonAction(attrs, ev.data.record);
|
||||||
} else if (!attrs.special || attrs.special === 'save') {
|
} else if (!attrs.special || attrs.special === 'save') {
|
||||||
@@ -484,7 +491,7 @@ var FormController = BasicController.extend({
|
|||||||
def = saveAndExecuteAction();
|
def = saveAndExecuteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
def.always(this._enableButtons.bind(this));
|
def.then(this._enableButtons.bind(this)).guardedCatch(this._enableButtons.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when the user wants to create a new record -> @see createRecord
|
* Called when the user wants to create a new record -> @see createRecord
|
||||||
@@ -659,9 +666,7 @@ var FormController = BasicController.extend({
|
|||||||
ev.stopPropagation(); // Prevent x2m lines to be auto-saved
|
ev.stopPropagation(); // Prevent x2m lines to be auto-saved
|
||||||
var self = this;
|
var self = this;
|
||||||
this._disableButtons();
|
this._disableButtons();
|
||||||
this.saveRecord().always(function () {
|
this.saveRecord().then(this._enableButtons.bind(this)).guardedCatch(this._enableButtons.bind(this));
|
||||||
self._enableButtons();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when user swipes left. Move to next record.
|
* Called when user swipes left. Move to next record.
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
* @param {string} [params.mode] new mode, either 'edit' or 'readonly'
|
* @param {string} [params.mode] new mode, either 'edit' or 'readonly'
|
||||||
* @param {string[]} [params.fieldNames] if given, the renderer will only
|
* @param {string[]} [params.fieldNames] if given, the renderer will only
|
||||||
* update the fields in this list
|
* update the fields in this list
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
updateState: function (state, params) {
|
updateState: function (state, params) {
|
||||||
this.mode = (params && 'mode' in params) ? params.mode : this.mode;
|
this.mode = (params && 'mode' in params) ? params.mode : this.mode;
|
||||||
@@ -342,8 +342,8 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_postProcessField: function (widget, node) {
|
_postProcessField: function (widget, node) {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
this._setIDForLabel(widget, this._getIDForLabel(node.attrs.name));
|
this._setIDForLabel(widget, this._getIDForLabel(node.attrs.name));
|
||||||
this._handleAttributes(widget.$el, node);
|
|
||||||
if (JSON.parse(node.attrs.default_focus || "0")) {
|
if (JSON.parse(node.attrs.default_focus || "0")) {
|
||||||
this.defaultFocusField = widget;
|
this.defaultFocusField = widget;
|
||||||
}
|
}
|
||||||
@@ -355,7 +355,14 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
*/
|
*/
|
||||||
_renderButtonBox: function (node) {
|
_renderButtonBox: function (node) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var $result = $('<' + node.tag + '>', { 'class': 'o_not_full' });
|
var $result = $('<' + node.tag + '>', {class: 'o_not_full'});
|
||||||
|
|
||||||
|
// The rendering of buttons may be async (see renderFieldWidget), so we
|
||||||
|
// must wait for the buttons to be ready (and their modifiers to be
|
||||||
|
// applied) before manipulating them, as we check if they are visible or
|
||||||
|
// not. To do so, we extract from this.defs the promises corresponding
|
||||||
|
// to the buttonbox buttons, and wait for them to be resolved.
|
||||||
|
var nextDefIndex = this.defs.length;
|
||||||
var buttons = _.map(node.children, function (child) {
|
var buttons = _.map(node.children, function (child) {
|
||||||
if (child.tag === 'button') {
|
if (child.tag === 'button') {
|
||||||
return self._renderStatButton(child);
|
return self._renderStatButton(child);
|
||||||
@@ -363,48 +370,62 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
return self._renderNode(child);
|
return self._renderNode(child);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var buttons_partition = _.partition(buttons, function ($button) {
|
|
||||||
return $button.is('.o_invisible_modifier');
|
// At this point, each button is an empty div that will be replaced by
|
||||||
|
// the real $el of the button when it is ready (with replaceWith).
|
||||||
|
// However, this only works if the empty div is appended somewhere, so
|
||||||
|
// we here append them into a wrapper, and unwrap them once they have
|
||||||
|
// been replaced.
|
||||||
|
var $tempWrapper = $('<div>');
|
||||||
|
_.each(buttons, function ($button) {
|
||||||
|
$button.appendTo($tempWrapper);
|
||||||
});
|
});
|
||||||
var invisible_buttons = buttons_partition[0];
|
var defs = this.defs.slice(nextDefIndex);
|
||||||
var visible_buttons = buttons_partition[1];
|
Promise.all(defs).then(function () {
|
||||||
|
buttons = $tempWrapper.children();
|
||||||
// Get the unfolded buttons according to window size
|
var buttons_partition = _.partition(buttons, function (button) {
|
||||||
var nb_buttons = [2, 2, 4, 6][config.device.size_class] || 7;
|
return $(button).is('.o_invisible_modifier');
|
||||||
var unfolded_buttons = visible_buttons.slice(0, nb_buttons).concat(invisible_buttons);
|
|
||||||
|
|
||||||
// Get the folded buttons
|
|
||||||
var folded_buttons = visible_buttons.slice(nb_buttons);
|
|
||||||
if (folded_buttons.length === 1) {
|
|
||||||
unfolded_buttons = buttons;
|
|
||||||
folded_buttons = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle class to tell if the button box is full (CSS requirement)
|
|
||||||
var full = (visible_buttons.length > nb_buttons);
|
|
||||||
$result.toggleClass('o_full', full).toggleClass('o_not_full', !full);
|
|
||||||
|
|
||||||
// Add the unfolded buttons
|
|
||||||
_.each(unfolded_buttons, function ($button) {
|
|
||||||
$button.appendTo($result);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add the dropdown with folded buttons if any
|
|
||||||
if (folded_buttons.length) {
|
|
||||||
$result.append(dom.renderButton({
|
|
||||||
attrs: {
|
|
||||||
class: 'oe_stat_button o_button_more dropdown-toggle',
|
|
||||||
'data-toggle': 'dropdown',
|
|
||||||
},
|
|
||||||
text: _t("More"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
var $dropdown = $("<div>", {'class': "dropdown-menu o_dropdown_more", role: "menu"});
|
|
||||||
_.each(folded_buttons, function ($button) {
|
|
||||||
$button.addClass('dropdown-item').appendTo($dropdown);
|
|
||||||
});
|
});
|
||||||
$dropdown.appendTo($result);
|
var invisible_buttons = buttons_partition[0];
|
||||||
}
|
var visible_buttons = buttons_partition[1];
|
||||||
|
|
||||||
|
// Get the unfolded buttons according to window size
|
||||||
|
var nb_buttons = [2, 2, 4, 6][config.device.size_class] || 7;
|
||||||
|
var unfolded_buttons = visible_buttons.slice(0, nb_buttons).concat(invisible_buttons);
|
||||||
|
|
||||||
|
// Get the folded buttons
|
||||||
|
var folded_buttons = visible_buttons.slice(nb_buttons);
|
||||||
|
if (folded_buttons.length === 1) {
|
||||||
|
unfolded_buttons = buttons;
|
||||||
|
folded_buttons = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle class to tell if the button box is full (CSS requirement)
|
||||||
|
var full = (visible_buttons.length > nb_buttons);
|
||||||
|
$result.toggleClass('o_full', full).toggleClass('o_not_full', !full);
|
||||||
|
|
||||||
|
// Add the unfolded buttons
|
||||||
|
_.each(unfolded_buttons, function (button) {
|
||||||
|
$(button).appendTo($result);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the dropdown with folded buttons if any
|
||||||
|
if (folded_buttons.length) {
|
||||||
|
$result.append(dom.renderButton({
|
||||||
|
attrs: {
|
||||||
|
'class': 'oe_stat_button o_button_more dropdown-toggle',
|
||||||
|
'data-toggle': 'dropdown',
|
||||||
|
},
|
||||||
|
text: _t("More"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
var $dropdown = $("<div>", {class: "dropdown-menu o_dropdown_more", role: "menu"});
|
||||||
|
_.each(folded_buttons, function (button) {
|
||||||
|
$(button).addClass('dropdown-item').appendTo($dropdown);
|
||||||
|
});
|
||||||
|
$dropdown.appendTo($result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this._handleAttributes($result, node);
|
this._handleAttributes($result, node);
|
||||||
this._registerModifiers(node, this.state, $result);
|
this._registerModifiers(node, this.state, $result);
|
||||||
@@ -477,6 +498,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
_renderInnerGroup: function (node) {
|
_renderInnerGroup: function (node) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var $result = $('<table/>', {class: 'o_group o_inner_group'});
|
var $result = $('<table/>', {class: 'o_group o_inner_group'});
|
||||||
|
var $tbody = $('<tbody />').appendTo($result);
|
||||||
this._handleAttributes($result, node);
|
this._handleAttributes($result, node);
|
||||||
this._registerModifiers(node, this.state, $result);
|
this._registerModifiers(node, this.state, $result);
|
||||||
|
|
||||||
@@ -490,7 +512,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
var rows = [];
|
var rows = [];
|
||||||
var $currentRow = $('<tr/>');
|
var $currentRow = $('<tr/>');
|
||||||
var currentColspan = 0;
|
var currentColspan = 0;
|
||||||
_.each(node.children, function (child) {
|
node.children.forEach(function (child) {
|
||||||
if (child.tag === 'newline') {
|
if (child.tag === 'newline') {
|
||||||
rows.push($currentRow);
|
rows.push($currentRow);
|
||||||
$currentRow = $('<tr/>');
|
$currentRow = $('<tr/>');
|
||||||
@@ -537,7 +559,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
var $el = $(el);
|
var $el = $(el);
|
||||||
$el.css('width', ((parseInt($el.attr('colspan'), 10) || 1) * nonLabelColSize) + '%');
|
$el.css('width', ((parseInt($el.attr('colspan'), 10) || 1) * nonLabelColSize) + '%');
|
||||||
});
|
});
|
||||||
$result.append($tr);
|
$tbody.append($tr);
|
||||||
});
|
});
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@@ -575,7 +597,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
*
|
*
|
||||||
* For fields, it will return the $el of the field widget. Note that this
|
* For fields, it will return the $el of the field widget. Note that this
|
||||||
* method is synchronous, field widgets are instantiated and appended, but
|
* method is synchronous, field widgets are instantiated and appended, but
|
||||||
* if they are asynchronous, they register their deferred in this.defs, and
|
* if they are asynchronous, they register their promises in this.defs, and
|
||||||
* the _renderView method will properly wait.
|
* the _renderView method will properly wait.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
@@ -705,7 +727,8 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
if (node.attrs.class) {
|
if (node.attrs.class) {
|
||||||
$result.addClass(node.attrs.class);
|
$result.addClass(node.attrs.class);
|
||||||
}
|
}
|
||||||
$result.append(_.map(node.children, this._renderNode.bind(this)));
|
var allNodes = node.children.map(this._renderNode.bind(this));
|
||||||
|
$result.append(allNodes);
|
||||||
return $result;
|
return $result;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -870,7 +893,7 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
_renderTagSheet: function (node) {
|
_renderTagSheet: function (node) {
|
||||||
this.has_sheet = true;
|
this.has_sheet = true;
|
||||||
var $sheet = $('<div>', {class: 'clearfix o_form_sheet'});
|
var $sheet = $('<div>', {class: 'clearfix o_form_sheet'});
|
||||||
$sheet.append(_.map(node.children, this._renderNode.bind(this)));
|
$sheet.append(node.children.map(this._renderNode.bind(this)));
|
||||||
return $sheet;
|
return $sheet;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -885,12 +908,12 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Main entry point for the rendering. From here, we call _renderNode on
|
* Main entry point for the rendering. From here, we call _renderNode on
|
||||||
* the root of the arch, then, when every deferred (from the field widgets)
|
* the root of the arch, then, when every promise (from the field widgets)
|
||||||
* are done, it will resolves itself.
|
* are done, it will resolves itself.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @override method from BasicRenderer
|
* @override method from BasicRenderer
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderView: function () {
|
_renderView: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -901,17 +924,17 @@ var FormRenderer = BasicRenderer.extend({
|
|||||||
var $form = this._renderNode(this.arch).addClass(this.className);
|
var $form = this._renderNode(this.arch).addClass(this.className);
|
||||||
delete this.defs;
|
delete this.defs;
|
||||||
|
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
self._updateView($form.contents());
|
self._updateView($form.contents());
|
||||||
if (self.state.res_id in self.alertFields) {
|
if (self.state.res_id in self.alertFields) {
|
||||||
self.displayTranslationAlert();
|
self.displayTranslationAlert();
|
||||||
}
|
}
|
||||||
}, function () {
|
|
||||||
$form.remove();
|
|
||||||
}).then(function(){
|
}).then(function(){
|
||||||
if (self.lastActivatedFieldIndex >= 0) {
|
if (self.lastActivatedFieldIndex >= 0) {
|
||||||
self._activateNextFieldWidget(self.state, self.lastActivatedFieldIndex);
|
self._activateNextFieldWidget(self.state, self.lastActivatedFieldIndex);
|
||||||
}
|
}
|
||||||
|
}).guardedCatch(function () {
|
||||||
|
$form.remove();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ var FormView = BasicView.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Widget} parent the parent of the model, if it has to be created
|
* @param {Widget} parent the parent of the model, if it has to be created
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadSubviews: function (parent) {
|
_loadSubviews: function (parent) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -145,7 +145,7 @@ var FormView = BasicView.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* We set here the limit for the number of records fetched (in one page).
|
* We set here the limit for the number of records fetched (in one page).
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ return AbstractModel.extend({
|
|||||||
* @param {string[]} params.domain
|
* @param {string[]} params.domain
|
||||||
* @param {Object} params.intervalMapping object linking fieldNames with intervals.
|
* @param {Object} params.intervalMapping object linking fieldNames with intervals.
|
||||||
* this could be useful to simplify the code. For now this parameter is not used.
|
* this could be useful to simplify the code. For now this parameter is not used.
|
||||||
* @returns {Deferred} The deferred does not return a handle, we don't need
|
* @returns {Promise} The promise does not return a handle, we don't need
|
||||||
* to keep track of various entities.
|
* to keep track of various entities.
|
||||||
*/
|
*/
|
||||||
load: function (params) {
|
load: function (params) {
|
||||||
@@ -92,7 +92,7 @@ return AbstractModel.extend({
|
|||||||
* @param {string[]} [params.groupBy]
|
* @param {string[]} [params.groupBy]
|
||||||
* @param {string} [params.mode] one of 'bar', 'pie', 'line'
|
* @param {string} [params.mode] one of 'bar', 'pie', 'line'
|
||||||
* @param {string} [params.measure] a valid field name
|
* @param {string} [params.measure] a valid field name
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reload: function (handle, params) {
|
reload: function (handle, params) {
|
||||||
if ('context' in params) {
|
if ('context' in params) {
|
||||||
@@ -130,7 +130,7 @@ return AbstractModel.extend({
|
|||||||
}
|
}
|
||||||
if ('mode' in params) {
|
if ('mode' in params) {
|
||||||
this.chart.mode = params.mode;
|
this.chart.mode = params.mode;
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return this._loadGraph();
|
return this._loadGraph();
|
||||||
},
|
},
|
||||||
@@ -145,7 +145,7 @@ return AbstractModel.extend({
|
|||||||
* in the field list, because they can be defined with an aggregation
|
* in the field list, because they can be defined with an aggregation
|
||||||
* function, such as my_date:week
|
* function, such as my_date:week
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadGraph: function () {
|
_loadGraph: function () {
|
||||||
var groupedBy = this.chart.groupedBy;
|
var groupedBy = this.chart.groupedBy;
|
||||||
@@ -186,7 +186,7 @@ return AbstractModel.extend({
|
|||||||
}).then(this._processData.bind(this, 'comparisonData')));
|
}).then(this._processData.bind(this, 'comparisonData')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Since read_group is insane and returns its result on different keys
|
* Since read_group is insane and returns its result on different keys
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ return AbstractRenderer.extend({
|
|||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred} The _super deferred is actually resolved immediately
|
* @returns {Promise} The _super promise is actually resolved immediately
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
if (this.to_remove) {
|
if (this.to_remove) {
|
||||||
|
|||||||
@@ -97,10 +97,7 @@ var KanbanColumn = Widget.extend({
|
|||||||
this.$header = this.$('.o_kanban_header');
|
this.$header = this.$('.o_kanban_header');
|
||||||
|
|
||||||
for (var i = 0; i < this.data_records.length; i++) {
|
for (var i = 0; i < this.data_records.length; i++) {
|
||||||
var def = this._addRecord(this.data_records[i]);
|
defs.push(this._addRecord(this.data_records[i]));
|
||||||
if (def.state() === 'pending') {
|
|
||||||
defs.push(def);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.device.isMobile) {
|
if (!config.device.isMobile) {
|
||||||
@@ -170,7 +167,7 @@ var KanbanColumn = Widget.extend({
|
|||||||
this.$('.o_kanban_load_more').html(QWeb.render('KanbanView.LoadMore', {widget: this}));
|
this.$('.o_kanban_load_more').html(QWeb.render('KanbanView.LoadMore', {widget: this}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called when a record has been quick created, as a new column is rendered
|
* Called when a record has been quick created, as a new column is rendered
|
||||||
@@ -193,7 +190,7 @@ var KanbanColumn = Widget.extend({
|
|||||||
/**
|
/**
|
||||||
* Adds the quick create record to the top of the column.
|
* Adds the quick create record to the top of the column.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
addQuickCreate: function () {
|
addQuickCreate: function () {
|
||||||
if (this.folded) {
|
if (this.folded) {
|
||||||
@@ -205,7 +202,7 @@ var KanbanColumn = Widget.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.quickCreateWidget) {
|
if (this.quickCreateWidget) {
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
this.trigger_up('close_quick_create'); // close other quick create widgets
|
this.trigger_up('close_quick_create'); // close other quick create widgets
|
||||||
this.trigger_up('start_quick_create');
|
this.trigger_up('start_quick_create');
|
||||||
@@ -245,7 +242,7 @@ var KanbanColumn = Widget.extend({
|
|||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {string} [options.position]
|
* @param {string} [options.position]
|
||||||
* 'before' to add at the top, add at the bottom by default
|
* 'before' to add at the top, add at the bottom by default
|
||||||
* @return {Deferred}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
_addRecord: function (recordState, options) {
|
_addRecord: function (recordState, options) {
|
||||||
var record = new this.KanbanRecord(this, recordState, this.record_options);
|
var record = new this.KanbanRecord(this, recordState, this.record_options);
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ var KanbanController = BasicController.extend({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {jQueryElement} $node
|
* @param {jQueryElement} $node
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renderButtons: function ($node) {
|
renderButtons: function ($node) {
|
||||||
if (this.hasButtons && this.is_action_enabled('create')) {
|
if (this.hasButtons && this.is_action_enabled('create')) {
|
||||||
@@ -86,8 +87,9 @@ var KanbanController = BasicController.extend({
|
|||||||
this.$buttons.on('click', 'button.o-kanban-button-new', this._onButtonNew.bind(this));
|
this.$buttons.on('click', 'button.o-kanban-button-new', this._onButtonNew.bind(this));
|
||||||
this.$buttons.on('keydown', this._onButtonsKeyDown.bind(this));
|
this.$buttons.on('keydown', this._onButtonsKeyDown.bind(this));
|
||||||
this._updateButtons();
|
this._updateButtons();
|
||||||
this.$buttons.appendTo($node);
|
return Promise.resolve(this.$buttons.appendTo($node));
|
||||||
}
|
}
|
||||||
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Override to add the domain coming from the searchPanel (if any) to the
|
* Override to add the domain coming from the searchPanel (if any) to the
|
||||||
@@ -110,7 +112,7 @@ var KanbanController = BasicController.extend({
|
|||||||
params.domain = this.controlPanelDomain.concat(this.searchPanelDomain);
|
params.domain = this.controlPanelDomain.concat(this.searchPanelDomain);
|
||||||
var superProm = this._super.apply(this, arguments);
|
var superProm = this._super.apply(this, arguments);
|
||||||
var searchPanelProm = this._updateSearchPanel();
|
var searchPanelProm = this._updateSearchPanel();
|
||||||
return $.when(superProm, searchPanelProm).then(function () {
|
return Promise.all([superProm, searchPanelProm]).then(function () {
|
||||||
// searchPanel has been re-rendered, so re-render the view
|
// searchPanel has been re-rendered, so re-render the view
|
||||||
return self.renderer.render();
|
return self.renderer.render();
|
||||||
});
|
});
|
||||||
@@ -124,7 +126,7 @@ var KanbanController = BasicController.extend({
|
|||||||
* @override method comes from field manager mixin
|
* @override method comes from field manager mixin
|
||||||
* @private
|
* @private
|
||||||
* @param {string} id local id from the basic record data
|
* @param {string} id local id from the basic record data
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmSave: function (id) {
|
_confirmSave: function (id) {
|
||||||
var data = this.model.get(this.handle, {raw: true});
|
var data = this.model.get(this.handle, {raw: true});
|
||||||
@@ -197,7 +199,7 @@ var KanbanController = BasicController.extend({
|
|||||||
/**
|
/**
|
||||||
* @param {number[]} ids
|
* @param {number[]} ids
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_resequenceColumns: function (ids) {
|
_resequenceColumns: function (ids) {
|
||||||
var state = this.model.get(this.handle, {raw: true});
|
var state = this.model.get(this.handle, {raw: true});
|
||||||
@@ -212,7 +214,7 @@ var KanbanController = BasicController.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {string} column_id
|
* @param {string} column_id
|
||||||
* @param {string[]} ids
|
* @param {string[]} ids
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_resequenceRecords: function (column_id, ids) {
|
_resequenceRecords: function (column_id, ids) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -246,7 +248,7 @@ var KanbanController = BasicController.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {$.Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_updateSearchPanel: function () {
|
_updateSearchPanel: function () {
|
||||||
return this._searchPanel.update({searchDomain: this.controlPanelDomain});
|
return this._searchPanel.update({searchDomain: this.controlPanelDomain});
|
||||||
@@ -296,7 +298,7 @@ var KanbanController = BasicController.extend({
|
|||||||
self.renderer.updateColumn(db_id, data);
|
self.renderer.updateColumn(db_id, data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).fail(this.reload.bind(this, {}));
|
}).guardedCatch(this.reload.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -400,7 +402,7 @@ var KanbanController = BasicController.extend({
|
|||||||
var relatedModelName = state.fields[state.groupedBy[0]].relation;
|
var relatedModelName = state.fields[state.groupedBy[0]].relation;
|
||||||
this.model
|
this.model
|
||||||
.deleteRecords([column.db_id], relatedModelName)
|
.deleteRecords([column.db_id], relatedModelName)
|
||||||
.done(function () {
|
.then(function () {
|
||||||
self.update({}, {reload: !column.isEmpty()});
|
self.update({}, {reload: !column.isEmpty()});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -415,11 +417,12 @@ var KanbanController = BasicController.extend({
|
|||||||
var self = this;
|
var self = this;
|
||||||
this.model.loadColumnRecords(ev.data.columnID).then(function (dbID) {
|
this.model.loadColumnRecords(ev.data.columnID).then(function (dbID) {
|
||||||
var data = self.model.get(dbID);
|
var data = self.model.get(dbID);
|
||||||
self.renderer.updateColumn(dbID, data);
|
return self.renderer.updateColumn(dbID, data).then(function() {
|
||||||
self._updateEnv();
|
self._updateEnv();
|
||||||
if (ev.data.onSuccess) {
|
if (ev.data.onSuccess) {
|
||||||
ev.data.onSuccess();
|
ev.data.onSuccess();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -470,8 +473,8 @@ var KanbanController = BasicController.extend({
|
|||||||
|
|
||||||
this.model.createRecordInGroup(column.db_id, values)
|
this.model.createRecordInGroup(column.db_id, values)
|
||||||
.then(update)
|
.then(update)
|
||||||
.fail(function (error, ev) {
|
.guardedCatch(function (reason) {
|
||||||
ev.preventDefault();
|
reason.event.preventDefault();
|
||||||
var columnState = self.model.get(column.db_id, {raw: true});
|
var columnState = self.model.get(column.db_id, {raw: true});
|
||||||
var context = columnState.getContext();
|
var context = columnState.getContext();
|
||||||
var state = self.model.get(self.handle, {raw: true});
|
var state = self.model.get(self.handle, {raw: true});
|
||||||
@@ -538,11 +541,15 @@ var KanbanController = BasicController.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {OdooEvent} ev
|
* @param {OdooEvent} ev
|
||||||
|
* @param {function} [ev.data.onSuccess] callback to execute after applying
|
||||||
|
* changes
|
||||||
*/
|
*/
|
||||||
_onUpdateRecord: function (ev) {
|
_onUpdateRecord: function (ev) {
|
||||||
|
var onSuccess = ev.data.onSuccess;
|
||||||
|
delete ev.data.onSuccess;
|
||||||
var changes = _.clone(ev.data);
|
var changes = _.clone(ev.data);
|
||||||
ev.data.force_save = true;
|
ev.data.force_save = true;
|
||||||
this._applyChanges(ev.target.db_id, changes, ev);
|
this._applyChanges(ev.target.db_id, changes, ev).then(onSuccess);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Allow the user to archive/restore all the records of a column.
|
* Allow the user to archive/restore all the records of a column.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
*
|
*
|
||||||
* @param {string} groupID localID of the group
|
* @param {string} groupID localID of the group
|
||||||
* @param {integer} resId id of the record
|
* @param {integer} resId id of the record
|
||||||
* @returns {Deferred<string>} resolves to the local id of the new record
|
* @returns {Promise<string>} resolves to the local id of the new record
|
||||||
*/
|
*/
|
||||||
addRecordToGroup: function (groupID, resId) {
|
addRecordToGroup: function (groupID, resId) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -52,7 +52,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
*
|
*
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {string} parentID localID of the parent of the group
|
* @param {string} parentID localID of the parent of the group
|
||||||
* @returns {Deferred<string>} resolves to the local id of the new group
|
* @returns {Promise<string>} resolves to the local id of the new group
|
||||||
*/
|
*/
|
||||||
createGroup: function (name, parentID) {
|
createGroup: function (name, parentID) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -60,7 +60,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
var groupBy = parent.groupedBy[0];
|
var groupBy = parent.groupedBy[0];
|
||||||
var groupByField = parent.fields[groupBy];
|
var groupByField = parent.fields[groupBy];
|
||||||
if (!groupByField || groupByField.type !== 'many2one') {
|
if (!groupByField || groupByField.type !== 'many2one') {
|
||||||
return $.Deferred().reject(); // only supported when grouped on m2o
|
return Promise.reject(); // only supported when grouped on m2o
|
||||||
}
|
}
|
||||||
return this._rpc({
|
return this._rpc({
|
||||||
model: groupByField.relation,
|
model: groupByField.relation,
|
||||||
@@ -99,7 +99,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
*
|
*
|
||||||
* @param {string} groupID
|
* @param {string} groupID
|
||||||
* @param {Object} values
|
* @param {Object} values
|
||||||
* @returns {Deferred} resolved with the local id of the created record
|
* @returns {Promise} resolved with the local id of the created record
|
||||||
*/
|
*/
|
||||||
createRecordInGroup: function (groupID, values) {
|
createRecordInGroup: function (groupID, values) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -187,7 +187,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
* Opens a given group and loads its <limit> first records
|
* Opens a given group and loads its <limit> first records
|
||||||
*
|
*
|
||||||
* @param {string} groupID
|
* @param {string} groupID
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
loadColumnRecords: function (groupID) {
|
loadColumnRecords: function (groupID) {
|
||||||
var dataPoint = this.localData[groupID];
|
var dataPoint = this.localData[groupID];
|
||||||
@@ -198,7 +198,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
* Load more records in a group.
|
* Load more records in a group.
|
||||||
*
|
*
|
||||||
* @param {string} groupID localID of the group
|
* @param {string} groupID localID of the group
|
||||||
* @returns {Deferred<string>} resolves to the localID of the group
|
* @returns {Promise<string>} resolves to the localID of the group
|
||||||
*/
|
*/
|
||||||
loadMore: function (groupID) {
|
loadMore: function (groupID) {
|
||||||
var group = this.localData[groupID];
|
var group = this.localData[groupID];
|
||||||
@@ -213,7 +213,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
* @param {string} recordID localID of the record
|
* @param {string} recordID localID of the record
|
||||||
* @param {string} groupID localID of the new group of the record
|
* @param {string} groupID localID of the new group of the record
|
||||||
* @param {string} parentID localID of the parent
|
* @param {string} parentID localID of the parent
|
||||||
* @returns {Deferred<string[]>} resolves to a pair [oldGroupID, newGroupID]
|
* @returns {Promise<string[]>} resolves to a pair [oldGroupID, newGroupID]
|
||||||
*/
|
*/
|
||||||
moveRecord: function (recordID, groupID, parentID) {
|
moveRecord: function (recordID, groupID, parentID) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -330,7 +330,7 @@ var KanbanModel = BasicModel.extend({
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} dataPoint
|
* @param {Object} dataPoint
|
||||||
* @returns {Deferred<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
_readProgressBarGroup: function (list, options) {
|
_readProgressBarGroup: function (list, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -345,7 +345,8 @@ var KanbanModel = BasicModel.extend({
|
|||||||
context: list.context,
|
context: list.context,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return $.when(groupsDef, progressBarDef).then(function (a, data) {
|
return Promise.all([groupsDef, progressBarDef]).then(function (results) {
|
||||||
|
var data = results[1];
|
||||||
_.each(list.data, function (groupID) {
|
_.each(list.data, function (groupID) {
|
||||||
var group = self.localData[groupID];
|
var group = self.localData[groupID];
|
||||||
group.progressBarValues = _.extend({
|
group.progressBarValues = _.extend({
|
||||||
@@ -365,13 +366,13 @@ var KanbanModel = BasicModel.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} list a list of groups
|
* @param {Object} list a list of groups
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_readTooltipFields: function (list) {
|
_readTooltipFields: function (list) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var groupedByField = list.fields[list.groupedBy[0].split(':')[0]];
|
var groupedByField = list.fields[list.groupedBy[0].split(':')[0]];
|
||||||
if (groupedByField.type !== 'many2one') {
|
if (groupedByField.type !== 'many2one') {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var groupIds = _.reduce(list.data, function (groupIds, id) {
|
var groupIds = _.reduce(list.data, function (groupIds, id) {
|
||||||
var res_id = self.get(id, {raw: true}).res_id;
|
var res_id = self.get(id, {raw: true}).res_id;
|
||||||
@@ -400,16 +401,16 @@ var KanbanModel = BasicModel.extend({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Reloads all progressbar data. This is done after given deferred and
|
* Reloads all progressbar data. This is done after given promise and
|
||||||
* insures that the given deferred's result is not lost.
|
* insures that the given promise's result is not lost.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} recordID
|
* @param {string} recordID
|
||||||
* @param {Deferred} def
|
* @param {Promise} def
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_reloadProgressBarGroupFromRecord: function (recordID, def) {
|
_reloadProgressBarGroupFromRecord: function (recordID, def) {
|
||||||
var element = this.localData[recordID];
|
var element = this.localData[recordID];
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
return $.when(this._super.apply(this, arguments), this._render());
|
return Promise.all([this._super.apply(this, arguments), this._render()]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Called each time the record is attached to the DOM.
|
* Called each time the record is attached to the DOM.
|
||||||
@@ -85,7 +85,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
* Re-renders the record with a new state
|
* Re-renders the record with a new state
|
||||||
*
|
*
|
||||||
* @param {Object} state
|
* @param {Object} state
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
update: function (state) {
|
update: function (state) {
|
||||||
// detach the widgets because the record will empty its $el, which will
|
// detach the widgets because the record will empty its $el, which will
|
||||||
@@ -147,7 +147,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
}
|
}
|
||||||
if (typeof variable === 'string') {
|
if (typeof variable === 'string') {
|
||||||
var index = 0;
|
var index = 0;
|
||||||
for (var i = 0 ; i < variable.length ; i++) {
|
for (var i = 0; i < variable.length; i++) {
|
||||||
index += variable.charCodeAt(i);
|
index += variable.charCodeAt(i);
|
||||||
}
|
}
|
||||||
return index % NB_KANBAN_RECORD_COLORS;
|
return index % NB_KANBAN_RECORD_COLORS;
|
||||||
@@ -195,7 +195,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
field = options.preview_image;
|
field = options.preview_image;
|
||||||
var unique = this.record.__last_update && this.record.__last_update.value.replace(/[^0-9]/g, '');
|
var unique = this.record.__last_update && this.record.__last_update.value.replace(/[^0-9]/g, '');
|
||||||
var session = this.getSession();
|
var session = this.getSession();
|
||||||
url = session.url('/web/image', {model: model, field: field, id: id, unique: unique});
|
url = session.url('/web/image', { model: model, field: field, id: id, unique: unique });
|
||||||
if (cache !== undefined) {
|
if (cache !== undefined) {
|
||||||
// Set the cache duration in seconds.
|
// Set the cache duration in seconds.
|
||||||
url += '&cache=' + parseInt(cache, 10);
|
url += '&cache=' + parseInt(cache, 10);
|
||||||
@@ -300,6 +300,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
// field's widgets point of view
|
// field's widgets point of view
|
||||||
// that dict being shared between records, we don't modify it
|
// that dict being shared between records, we don't modify it
|
||||||
// in place
|
// in place
|
||||||
|
var self = this;
|
||||||
var attrs = Object.create(null);
|
var attrs = Object.create(null);
|
||||||
_.each(this.fieldsInfo[field_name], function (value, key) {
|
_.each(this.fieldsInfo[field_name], function (value, key) {
|
||||||
if (_.str.startsWith(key, 't-att-')) {
|
if (_.str.startsWith(key, 't-att-')) {
|
||||||
@@ -308,13 +309,13 @@ var KanbanRecord = Widget.extend({
|
|||||||
}
|
}
|
||||||
attrs[key] = value;
|
attrs[key] = value;
|
||||||
});
|
});
|
||||||
var options = _.extend({}, this.options, {attrs: attrs});
|
var options = _.extend({}, this.options, { attrs: attrs });
|
||||||
var widget = new Widget(this, field_name, this.state, options);
|
var widget = new Widget(this, field_name, this.state, options);
|
||||||
var def = widget.replace($field);
|
var def = widget.replace($field);
|
||||||
if (def.state() === 'pending') {
|
this.defs.push(def);
|
||||||
this.defs.push(def);
|
def.then(function () {
|
||||||
}
|
self._setFieldDisplay(widget.$el, field_name);
|
||||||
this._setFieldDisplay(widget.$el, field_name);
|
});
|
||||||
return widget;
|
return widget;
|
||||||
},
|
},
|
||||||
_processWidgets: function () {
|
_processWidgets: function () {
|
||||||
@@ -324,23 +325,23 @@ var KanbanRecord = Widget.extend({
|
|||||||
var Widget = widgetRegistry.get($field.attr('name'));
|
var Widget = widgetRegistry.get($field.attr('name'));
|
||||||
var widget = new Widget(self, self.state);
|
var widget = new Widget(self, self.state);
|
||||||
|
|
||||||
var def = widget._widgetRenderAndInsert(function () {});
|
var def = widget._widgetRenderAndInsert(function () { });
|
||||||
if (def.state() === 'pending') {
|
self.defs.push(def);
|
||||||
self.defs.push(def);
|
def.then(function () {
|
||||||
}
|
$field.replaceWith(widget.$el);
|
||||||
widget.$el.addClass('o_widget');
|
widget.$el.addClass('o_widget');
|
||||||
$field.replaceWith(widget.$el);
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders the record
|
* Renders the record
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
this.defs = [];
|
this.defs = [];
|
||||||
this._replaceElement(this.qweb.render('kanban-box', this.qweb_context));
|
this._replaceElement(this.qweb.render('kanban-box', this.qweb_context));
|
||||||
this.$el.addClass('o_kanban_record').attr("tabindex",0);
|
this.$el.addClass('o_kanban_record').attr("tabindex", 0);
|
||||||
this.$el.attr('role', 'article');
|
this.$el.attr('role', 'article');
|
||||||
this.$el.data('record', this);
|
this.$el.data('record', this);
|
||||||
if (this.$el.hasClass('oe_kanban_global_click') ||
|
if (this.$el.hasClass('oe_kanban_global_click') ||
|
||||||
@@ -357,9 +358,9 @@ var KanbanRecord = Widget.extend({
|
|||||||
this._attachTooltip();
|
this._attachTooltip();
|
||||||
|
|
||||||
// We use boostrap tooltips for better and faster display
|
// We use boostrap tooltips for better and faster display
|
||||||
this.$('span.o_tag').tooltip({delay: {'show': 50}});
|
this.$('span.o_tag').tooltip({ delay: { 'show': 50 } });
|
||||||
|
|
||||||
return $.when.apply(this, this.defs);
|
return Promise.all(this.defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Sets particular classnames on a field $el according to the
|
* Sets particular classnames on a field $el according to the
|
||||||
@@ -419,7 +420,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
var colorHelp = _.str.sprintf(_t("Card color: %s"), this._getColorname(this.recordData[color_field]));
|
var colorHelp = _.str.sprintf(_t("Card color: %s"), this._getColorname(this.recordData[color_field]));
|
||||||
var colorClass = this._getColorClassname(this.recordData[color_field]);
|
var colorClass = this._getColorClassname(this.recordData[color_field]);
|
||||||
this.$el.addClass(colorClass);
|
this.$el.addClass(colorClass);
|
||||||
this.$el.prepend('<span title="' + colorHelp + '" aria-label="' + colorHelp +'" role="img" class="oe_kanban_color_help"/>');
|
this.$el.prepend('<span title="' + colorHelp + '" aria-label="' + colorHelp + '" role="img" class="oe_kanban_color_help"/>');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -454,7 +455,7 @@ var KanbanRecord = Widget.extend({
|
|||||||
r.raw_value = value.toDate();
|
r.raw_value = value.toDate();
|
||||||
} else if (r.type === 'one2many' || r.type === 'many2many') {
|
} else if (r.type === 'one2many' || r.type === 'many2many') {
|
||||||
r.raw_value = value.count ? value.res_ids : [];
|
r.raw_value = value.count ? value.res_ids : [];
|
||||||
} else if (r.type === 'many2one' ) {
|
} else if (r.type === 'many2one') {
|
||||||
r.raw_value = value && value.res_id || false;
|
r.raw_value = value && value.res_id || false;
|
||||||
} else {
|
} else {
|
||||||
r.raw_value = value;
|
r.raw_value = value;
|
||||||
@@ -554,13 +555,13 @@ var KanbanRecord = Widget.extend({
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'edit':
|
case 'edit':
|
||||||
this.trigger_up('open_record', {id: this.db_id, mode: 'edit'});
|
this.trigger_up('open_record', { id: this.db_id, mode: 'edit' });
|
||||||
break;
|
break;
|
||||||
case 'open':
|
case 'open':
|
||||||
this.trigger_up('open_record', {id: this.db_id});
|
this.trigger_up('open_record', { id: this.db_id });
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
this.trigger_up('kanban_record_delete', {id: this.db_id, record: this});
|
this.trigger_up('kanban_record_delete', { id: this.db_id, record: this });
|
||||||
break;
|
break;
|
||||||
case 'action':
|
case 'action':
|
||||||
case 'object':
|
case 'object':
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
willStart: function () {
|
willStart: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var def1 = this._super.apply(this, arguments);
|
var superWillStart = this._super.apply(this, arguments);
|
||||||
var def2;
|
var viewsLoaded;
|
||||||
if (this.formViewRef) {
|
if (this.formViewRef) {
|
||||||
var views = [[false, 'form']];
|
var views = [[false, 'form']];
|
||||||
var context = _.extend({}, this.context, {
|
var context = _.extend({}, this.context, {
|
||||||
form_view_ref: this.formViewRef,
|
form_view_ref: this.formViewRef,
|
||||||
});
|
});
|
||||||
def2 = this.loadViews(this.model, context, views);
|
viewsLoaded = this.loadViews(this.model, context, views);
|
||||||
} else {
|
} else {
|
||||||
var fieldsView = {};
|
var fieldsView = {};
|
||||||
fieldsView.arch = '<form>' +
|
fieldsView.arch = '<form>' +
|
||||||
@@ -65,9 +65,9 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
};
|
};
|
||||||
fieldsView.fields = fields;
|
fieldsView.fields = fields;
|
||||||
fieldsView.viewFields = fields;
|
fieldsView.viewFields = fields;
|
||||||
def2 = $.when({form: fieldsView});
|
viewsLoaded = Promise.resolve({form: fieldsView});
|
||||||
}
|
}
|
||||||
def2 = def2.then(function (fieldsViews) {
|
viewsLoaded = viewsLoaded.then(function (fieldsViews) {
|
||||||
var formView = new QuickCreateFormView(fieldsViews.form, {
|
var formView = new QuickCreateFormView(fieldsViews.form, {
|
||||||
context: self.context,
|
context: self.context,
|
||||||
modelName: self.model,
|
modelName: self.model,
|
||||||
@@ -78,7 +78,7 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
return self.controller.appendTo(document.createDocumentFragment());
|
return self.controller.appendTo(document.createDocumentFragment());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return $.when(def1, def2);
|
return Promise.all([superWillStart, viewsLoaded]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -113,7 +113,7 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
* have been made yet
|
* have been made yet
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
cancel: function () {
|
cancel: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -129,7 +129,6 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {boolean} [options.openRecord] set to true to directly open the
|
* @param {boolean} [options.openRecord] set to true to directly open the
|
||||||
@@ -139,7 +138,7 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
var self = this;
|
var self = this;
|
||||||
if (this._disabled) {
|
if (this._disabled) {
|
||||||
// don't do anything if we are already creating a record
|
// don't do anything if we are already creating a record
|
||||||
return $.Deferred();
|
return;
|
||||||
}
|
}
|
||||||
// disable the widget to prevent the user from creating multiple records
|
// disable the widget to prevent the user from creating multiple records
|
||||||
// with the current values ; if the create works, the widget will be
|
// with the current values ; if the create works, the widget will be
|
||||||
@@ -157,13 +156,13 @@ var RecordQuickCreate = Widget.extend({
|
|||||||
} else {
|
} else {
|
||||||
self._enableQuickCreate();
|
self._enableQuickCreate();
|
||||||
}
|
}
|
||||||
}).fail(this._enableQuickCreate.bind(this));
|
}).guardedCatch(this._enableQuickCreate.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Notifies the environment that the quick creation must be cancelled
|
* Notifies the environment that the quick creation must be cancelled
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_cancel: function () {
|
_cancel: function () {
|
||||||
this.trigger_up('cancel_quick_create');
|
this.trigger_up('cancel_quick_create');
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
/**
|
/**
|
||||||
* Displays the quick create record in the first column.
|
* Displays the quick create record in the first column.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
addQuickCreate: function () {
|
addQuickCreate: function () {
|
||||||
return this.widgets[0].addQuickCreate();
|
return this.widgets[0].addQuickCreate();
|
||||||
@@ -187,7 +187,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
* @param {boolean} [options.openQuickCreate] if true, directly opens the
|
* @param {boolean} [options.openQuickCreate] if true, directly opens the
|
||||||
* QuickCreate widget in the updated column
|
* QuickCreate widget in the updated column
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
updateColumn: function (localID, columnState, options) {
|
updateColumn: function (localID, columnState, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -204,7 +204,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
if (options && options.openQuickCreate) {
|
if (options && options.openQuickCreate) {
|
||||||
def = newColumn.addQuickCreate();
|
def = newColumn.addQuickCreate();
|
||||||
}
|
}
|
||||||
return $.when(def).then(function () {
|
return Promise.resolve(def).then(function () {
|
||||||
newColumn.$el.insertAfter(column.$el);
|
newColumn.$el.insertAfter(column.$el);
|
||||||
self._toggleNoContentHelper();
|
self._toggleNoContentHelper();
|
||||||
// When a record has been quick created, the new column directly
|
// When a record has been quick created, the new column directly
|
||||||
@@ -221,7 +221,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
* Updates a given record with its new state.
|
* Updates a given record with its new state.
|
||||||
*
|
*
|
||||||
* @param {Object} recordState
|
* @param {Object} recordState
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
updateRecord: function (recordState) {
|
updateRecord: function (recordState) {
|
||||||
var isGrouped = !!this.state.groupedBy.length;
|
var isGrouped = !!this.state.groupedBy.length;
|
||||||
@@ -242,7 +242,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
if (record) {
|
if (record) {
|
||||||
return record.update(recordState);
|
return record.update(recordState);
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -295,10 +295,13 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
* @param {integer} nbDivs the number of divs to append
|
* @param {integer} nbDivs the number of divs to append
|
||||||
*/
|
*/
|
||||||
_renderGhostDivs: function (fragment, nbDivs) {
|
_renderGhostDivs: function (fragment, nbDivs) {
|
||||||
|
var ghostDefs = [];
|
||||||
for (var $ghost, i = 0; i < nbDivs; i++) {
|
for (var $ghost, i = 0; i < nbDivs; i++) {
|
||||||
$ghost = $('<div>').addClass('o_kanban_record o_kanban_ghost');
|
$ghost = $('<div>').addClass('o_kanban_record o_kanban_ghost');
|
||||||
$ghost.appendTo(fragment);
|
var def = $ghost.appendTo(fragment);
|
||||||
|
ghostDefs.push(def);
|
||||||
}
|
}
|
||||||
|
return Promise.all(ghostDefs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Renders an grouped kanban view in a fragment.
|
* Renders an grouped kanban view in a fragment.
|
||||||
@@ -321,10 +324,7 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
def = column.appendTo(fragment);
|
def = column.appendTo(fragment);
|
||||||
self.widgets.push(column);
|
self.widgets.push(column);
|
||||||
}
|
}
|
||||||
if (def.state() === 'pending') {
|
self.defs.push(def);
|
||||||
self.defs.push(def);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove previous sorting
|
// remove previous sorting
|
||||||
@@ -359,13 +359,13 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
this.quickCreate = new ColumnQuickCreate(this, {
|
this.quickCreate = new ColumnQuickCreate(this, {
|
||||||
examples: this.examples,
|
examples: this.examples,
|
||||||
});
|
});
|
||||||
this.quickCreate.appendTo(fragment).then(function () {
|
this.defs.push(this.quickCreate.appendTo(fragment).then(function () {
|
||||||
// Open it directly if there is no column yet
|
// Open it directly if there is no column yet
|
||||||
if (!self.state.data.length) {
|
if (!self.state.data.length) {
|
||||||
self.quickCreate.toggleFold();
|
self.quickCreate.toggleFold();
|
||||||
self._renderExampleBackground(fragment);
|
self._renderExampleBackground(fragment);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -382,13 +382,14 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
var kanbanRecord = new KanbanRecord(self, record, self.recordOptions);
|
var kanbanRecord = new KanbanRecord(self, record, self.recordOptions);
|
||||||
self.widgets.push(kanbanRecord);
|
self.widgets.push(kanbanRecord);
|
||||||
var def = kanbanRecord.appendTo(fragment);
|
var def = kanbanRecord.appendTo(fragment);
|
||||||
if (def.state() === 'pending') {
|
self.defs.push(def);
|
||||||
self.defs.push(def);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// append ghost divs to ensure that all kanban records are left aligned
|
// append ghost divs to ensure that all kanban records are left aligned
|
||||||
this._renderGhostDivs(fragment, 6);
|
var prom = Promise.all(self.defs).then(function () {
|
||||||
|
return self._renderGhostDivs(fragment, 6);
|
||||||
|
});
|
||||||
|
this.defs.push(prom);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -398,28 +399,31 @@ var KanbanRenderer = BasicRenderer.extend({
|
|||||||
var self = this;
|
var self = this;
|
||||||
var oldWidgets = this.widgets;
|
var oldWidgets = this.widgets;
|
||||||
this.widgets = [];
|
this.widgets = [];
|
||||||
this.$el.empty();
|
|
||||||
|
|
||||||
var isGrouped = !!this.state.groupedBy.length;
|
|
||||||
this.$el.toggleClass('o_kanban_grouped', isGrouped);
|
|
||||||
this.$el.toggleClass('o_kanban_ungrouped', !isGrouped);
|
|
||||||
var fragment = document.createDocumentFragment();
|
|
||||||
// render the kanban view
|
// render the kanban view
|
||||||
this.defs = [];
|
var isGrouped = !!this.state.groupedBy.length;
|
||||||
|
var fragment = document.createDocumentFragment();
|
||||||
|
var defs = [];
|
||||||
|
this.defs = defs;
|
||||||
if (isGrouped) {
|
if (isGrouped) {
|
||||||
this._renderGrouped(fragment);
|
this._renderGrouped(fragment);
|
||||||
} else {
|
} else {
|
||||||
this._renderUngrouped(fragment);
|
this._renderUngrouped(fragment);
|
||||||
}
|
}
|
||||||
this.$el.append(fragment);
|
delete this.defs;
|
||||||
this._toggleNoContentHelper();
|
|
||||||
var defs = this.defs;
|
|
||||||
return this._super.apply(this, arguments).then(function () {
|
return this._super.apply(this, arguments).then(function () {
|
||||||
_.invoke(oldWidgets, 'destroy');
|
return Promise.all(defs).then(function () {
|
||||||
if (self._isInDom) {
|
self.$el.empty();
|
||||||
_.invoke(self.widgets, 'on_attach_callback');
|
self.$el.toggleClass('o_kanban_grouped', isGrouped);
|
||||||
}
|
self.$el.toggleClass('o_kanban_ungrouped', !isGrouped);
|
||||||
return $.when.apply(null, defs);
|
self.$el.append(fragment);
|
||||||
|
self._toggleNoContentHelper();
|
||||||
|
_.invoke(oldWidgets, 'destroy');
|
||||||
|
if (self._isInDom) {
|
||||||
|
_.invoke(self.widgets, 'on_attach_callback');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ KanbanRenderer.include({
|
|||||||
/**
|
/**
|
||||||
* Displays the quick create record in the active column
|
* Displays the quick create record in the active column
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
addQuickCreate: function () {
|
addQuickCreate: function () {
|
||||||
return this.widgets[this.activeColumnIndex].addQuickCreate();
|
return this.widgets[this.activeColumnIndex].addQuickCreate();
|
||||||
@@ -260,26 +260,26 @@ KanbanRenderer.include({
|
|||||||
* @private
|
* @private
|
||||||
* @param {integer} moveToIndex index of the column to move to
|
* @param {integer} moveToIndex index of the column to move to
|
||||||
* @param {boolean} [animate=false] set to true to animate
|
* @param {boolean} [animate=false] set to true to animate
|
||||||
* @returns {Deferred} resolved when the new current group has been loaded
|
* @returns {Promise} resolved when the new current group has been loaded
|
||||||
* and displayed
|
* and displayed
|
||||||
*/
|
*/
|
||||||
_moveToGroup: function (moveToIndex, animate) {
|
_moveToGroup: function (moveToIndex, animate) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (moveToIndex < 0 || moveToIndex >= this.widgets.length) {
|
if (moveToIndex < 0 || moveToIndex >= this.widgets.length) {
|
||||||
this._layoutUpdate(animate);
|
this._layoutUpdate(animate);
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var def = $.Deferred();
|
|
||||||
this.activeColumnIndex = moveToIndex;
|
this.activeColumnIndex = moveToIndex;
|
||||||
var column = this.widgets[this.activeColumnIndex];
|
var column = this.widgets[this.activeColumnIndex];
|
||||||
this.trigger_up('kanban_load_records', {
|
return new Promise(function (resolve) {
|
||||||
columnID: column.db_id,
|
self.trigger_up('kanban_load_records', {
|
||||||
onSuccess: function () {
|
columnID: column.db_id,
|
||||||
self._layoutUpdate(animate);
|
onSuccess: function () {
|
||||||
def.resolve();
|
self._layoutUpdate(animate);
|
||||||
},
|
resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,22 +287,23 @@ KanbanRenderer.include({
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_renderGrouped: function (fragment) {
|
_renderGrouped: function (fragment) {
|
||||||
var result = this._super.apply(this, arguments);
|
var self = this;
|
||||||
var data = [];
|
this._super.apply(this, arguments);
|
||||||
_.each(this.state.data, function (group) {
|
this.defs.push(Promise.all(this.defs).then(function () {
|
||||||
if (!group.value) {
|
var data = [];
|
||||||
group = _.extend({}, group, {value: _t('Undefined')});
|
_.each(self.state.data, function (group) {
|
||||||
data.unshift(group);
|
if (!group.value) {
|
||||||
}
|
group = _.extend({}, group, {value: _t('Undefined')});
|
||||||
else {
|
data.unshift(group);
|
||||||
data.push(group);
|
} else {
|
||||||
}
|
data.push(group);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$(qweb.render('KanbanView.MobileTabs', {
|
$(qweb.render('KanbanView.MobileTabs', {
|
||||||
data: data,
|
data: data,
|
||||||
})).prependTo(fragment);
|
})).prependTo(fragment);
|
||||||
return result;
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ var KanbanView = BasicView.extend({
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {Widget} parent
|
* @param {Widget} parent
|
||||||
* @returns {$.Promise} resolved when the searchPanel is ready
|
* @returns {Promise} resolved when the searchPanel is ready
|
||||||
*/
|
*/
|
||||||
_createSearchPanel: function (parent) {
|
_createSearchPanel: function (parent) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ var QuickCreateFormController = FormController.extend({
|
|||||||
*/
|
*/
|
||||||
commitChanges: function () {
|
commitChanges: function () {
|
||||||
var mutexDef = this.mutex.getUnlockedDef();
|
var mutexDef = this.mutex.getUnlockedDef();
|
||||||
return $.when(mutexDef, this.renderer.commitChanges(this.handle));
|
return Promise.all([mutexDef, this.renderer.commitChanges(this.handle)]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @returns {Object} the changes done on the current record
|
* @returns {Object} the changes done on the current record
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ var SearchPanel = Widget.extend({
|
|||||||
var loadProm = this._fetchCategories().then(function () {
|
var loadProm = this._fetchCategories().then(function () {
|
||||||
return self._fetchFilters().then(self._applyDefaultFilterValues.bind(self));
|
return self._fetchFilters().then(self._applyDefaultFilterValues.bind(self));
|
||||||
});
|
});
|
||||||
return $.when(loadProm, this._super.apply(this, arguments));
|
return Promise.all([loadProm, this._super.apply(this, arguments)]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -82,18 +82,18 @@ var SearchPanel = Widget.extend({
|
|||||||
*
|
*
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {Array[]} params.searchDomain domain coming from controlPanel
|
* @param {Array[]} params.searchDomain domain coming from controlPanel
|
||||||
* @returns {$.Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
update: function (params) {
|
update: function (params) {
|
||||||
var currentSearchDomainStr = JSON.stringify(this.searchDomain);
|
var currentSearchDomainStr = JSON.stringify(this.searchDomain);
|
||||||
var newSearchDomainStr = JSON.stringify(params.searchDomain);
|
var newSearchDomainStr = JSON.stringify(params.searchDomain);
|
||||||
var def;
|
var filtersProm;
|
||||||
if (this.needReload || (currentSearchDomainStr !== newSearchDomainStr)) {
|
if (this.needReload || (currentSearchDomainStr !== newSearchDomainStr)) {
|
||||||
this.needReload = false;
|
this.needReload = false;
|
||||||
this.searchDomain = params.searchDomain;
|
this.searchDomain = params.searchDomain;
|
||||||
def = this._fetchFilters();
|
filtersProm = this._fetchFilters();
|
||||||
}
|
}
|
||||||
return $.when(def).then(this._render.bind(this));
|
return Promise.resolve(filtersProm).then(this._render.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -231,21 +231,21 @@ var SearchPanel = Widget.extend({
|
|||||||
* Fetch values for each category. This is done only once, at startup.
|
* Fetch values for each category. This is done only once, at startup.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {$.Promise} resolved when all categories have been fetched
|
* @returns {Promise} resolved when all categories have been fetched
|
||||||
*/
|
*/
|
||||||
_fetchCategories: function () {
|
_fetchCategories: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var defs = Object.keys(this.categories).map(function (categoryId) {
|
var proms = Object.keys(this.categories).map(function (categoryId) {
|
||||||
var category = self.categories[categoryId];
|
var category = self.categories[categoryId];
|
||||||
var field = self.fields[category.fieldName];
|
var field = self.fields[category.fieldName];
|
||||||
var def;
|
var categoriesProm;
|
||||||
if (field.type === 'selection') {
|
if (field.type === 'selection') {
|
||||||
var values = field.selection.map(function (value) {
|
var values = field.selection.map(function (value) {
|
||||||
return {id: value[0], display_name: value[1]};
|
return {id: value[0], display_name: value[1]};
|
||||||
});
|
});
|
||||||
def = $.when(values);
|
categoriesProm = Promise.resolve(values);
|
||||||
} else {
|
} else {
|
||||||
def = self._rpc({
|
categoriesProm = self._rpc({
|
||||||
method: 'search_panel_select_range',
|
method: 'search_panel_select_range',
|
||||||
model: self.model,
|
model: self.model,
|
||||||
args: [category.fieldName],
|
args: [category.fieldName],
|
||||||
@@ -254,18 +254,18 @@ var SearchPanel = Widget.extend({
|
|||||||
return result.values;
|
return result.values;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return def.then(function (values) {
|
return categoriesProm.then(function (values) {
|
||||||
self._createCategoryTree(categoryId, values);
|
self._createCategoryTree(categoryId, values);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(proms);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Fetch values for each filter. This is done at startup, and at each reload
|
* Fetch values for each filter. This is done at startup, and at each reload
|
||||||
* (when the controlPanel or searchPanel domain changes).
|
* (when the controlPanel or searchPanel domain changes).
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {$.Promise} resolved when all filters have been fetched
|
* @returns {Promise} resolved when all filters have been fetched
|
||||||
*/
|
*/
|
||||||
_fetchFilters: function () {
|
_fetchFilters: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -276,7 +276,7 @@ var SearchPanel = Widget.extend({
|
|||||||
});
|
});
|
||||||
var categoryDomain = this._getCategoryDomain();
|
var categoryDomain = this._getCategoryDomain();
|
||||||
var filterDomain = this._getFilterDomain();
|
var filterDomain = this._getFilterDomain();
|
||||||
var defs = Object.keys(this.filters).map(function (filterId) {
|
var proms = Object.keys(this.filters).map(function (filterId) {
|
||||||
var filter = self.filters[filterId];
|
var filter = self.filters[filterId];
|
||||||
return self._rpc({
|
return self._rpc({
|
||||||
method: 'search_panel_select_multi_range',
|
method: 'search_panel_select_multi_range',
|
||||||
@@ -294,7 +294,7 @@ var SearchPanel = Widget.extend({
|
|||||||
self._createFilterTree(filterId, values);
|
self._createFilterTree(filterId, values);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return $.when.apply($, defs);
|
return Promise.all(proms);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Compute and return the domain based on the current active categories.
|
* Compute and return the domain based on the current active categories.
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ var ListController = BasicController.extend({
|
|||||||
* this method should be private, most of the code in the sidebar should be
|
* this method should be private, most of the code in the sidebar should be
|
||||||
* moved to the controller, and we should not use the getParent method...
|
* moved to the controller, and we should not use the getParent method...
|
||||||
*
|
*
|
||||||
* @returns {Deferred<array[]>} a deferred that resolve to the active domain
|
* @returns {Promise<array[]>} a promise that resolve to the active domain
|
||||||
*/
|
*/
|
||||||
getActiveDomain: function () {
|
getActiveDomain: function () {
|
||||||
// TODO: this method should be synchronous...
|
// TODO: this method should be synchronous...
|
||||||
@@ -73,9 +73,9 @@ var ListController = BasicController.extend({
|
|||||||
if (this.$('thead .o_list_record_selector input').prop('checked')) {
|
if (this.$('thead .o_list_record_selector input').prop('checked')) {
|
||||||
var searchQuery = this._controlPanel ? this._controlPanel.getSearchQuery() : {};
|
var searchQuery = this._controlPanel ? this._controlPanel.getSearchQuery() : {};
|
||||||
var record = self.model.get(self.handle, {raw: true});
|
var record = self.model.get(self.handle, {raw: true});
|
||||||
return $.when(record.getDomain().concat(searchQuery.domain || []));
|
return Promise.all(record.getDomain().concat(searchQuery.domain || []));
|
||||||
} else {
|
} else {
|
||||||
return $.Deferred().resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
@@ -143,6 +143,7 @@ var ListController = BasicController.extend({
|
|||||||
* main buttons)
|
* main buttons)
|
||||||
*
|
*
|
||||||
* @param {jQuery Node} $node
|
* @param {jQuery Node} $node
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renderSidebar: function ($node) {
|
renderSidebar: function ($node) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -180,10 +181,11 @@ var ListController = BasicController.extend({
|
|||||||
},
|
},
|
||||||
actions: _.extend(this.toolbarActions, {other: other}),
|
actions: _.extend(this.toolbarActions, {other: other}),
|
||||||
});
|
});
|
||||||
this.sidebar.appendTo($node);
|
return this.sidebar.appendTo($node).then(function() {
|
||||||
|
self._toggleSidebar();
|
||||||
this._toggleSidebar();
|
});
|
||||||
}
|
}
|
||||||
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Overrides to update the list of selected records
|
* Overrides to update the list of selected records
|
||||||
@@ -243,10 +245,11 @@ var ListController = BasicController.extend({
|
|||||||
});
|
});
|
||||||
}).then(function (recordID) {
|
}).then(function (recordID) {
|
||||||
var state = self.model.get(self.handle);
|
var state = self.model.get(self.handle);
|
||||||
self.renderer.updateState(state, {});
|
self.renderer.updateState(state, {})
|
||||||
self.renderer.editRecord(recordID);
|
.then(function () {
|
||||||
self._updatePager();
|
self.renderer.editRecord(recordID);
|
||||||
}).always(this._enableButtons.bind(this));
|
}).then(self._updatePager.bind(self));
|
||||||
|
}).then(this._enableButtons.bind(this)).guardedCatch(this._enableButtons.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Archive the current selection
|
* Archive the current selection
|
||||||
@@ -254,11 +257,11 @@ var ListController = BasicController.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {string[]} ids
|
* @param {string[]} ids
|
||||||
* @param {boolean} archive
|
* @param {boolean} archive
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_archive: function (ids, archive) {
|
_archive: function (ids, archive) {
|
||||||
if (ids.length === 0) {
|
if (ids.length === 0) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return this.model
|
return this.model
|
||||||
.toggleActive(ids, !archive, this.handle)
|
.toggleActive(ids, !archive, this.handle)
|
||||||
@@ -298,7 +301,7 @@ var ListController = BasicController.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @param {string} id a basicmodel valid resource handle. It is supposed to
|
* @param {string} id a basicmodel valid resource handle. It is supposed to
|
||||||
* be a record from the list view.
|
* be a record from the list view.
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmSave: function (id) {
|
_confirmSave: function (id) {
|
||||||
var state = this.model.get(this.handle);
|
var state = this.model.get(this.handle);
|
||||||
@@ -313,13 +316,13 @@ var ListController = BasicController.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @param {string} [recordID] - default to main recordID
|
* @param {string} [recordID] - default to main recordID
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_discardChanges: function (recordID) {
|
_discardChanges: function (recordID) {
|
||||||
if ((recordID || this.handle) === this.handle) {
|
if ((recordID || this.handle) === this.handle) {
|
||||||
recordID = this.renderer.getEditableRecordID();
|
recordID = this.renderer.getEditableRecordID();
|
||||||
if (recordID === null) {
|
if (recordID === null) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -343,7 +346,7 @@ var ListController = BasicController.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {string} mode
|
* @param {string} mode
|
||||||
* @param {string} [recordID] - default to main recordID
|
* @param {string} [recordID] - default to main recordID
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_setMode: function (mode, recordID) {
|
_setMode: function (mode, recordID) {
|
||||||
if ((recordID || this.handle) !== this.handle) {
|
if ((recordID || this.handle) !== this.handle) {
|
||||||
@@ -373,11 +376,13 @@ var ListController = BasicController.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_update: function () {
|
_update: function () {
|
||||||
this._toggleSidebar();
|
var self = this;
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments).then(function() {
|
||||||
|
self._toggleSidebar();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* This helper simply makes sure that the control panel buttons matches the
|
* This helper simply makes sure that the control panel buttons matches the
|
||||||
@@ -471,7 +476,7 @@ var ListController = BasicController.extend({
|
|||||||
var record = self.model.get(self.handle);
|
var record = self.model.get(self.handle);
|
||||||
var editedRecord = record.data[ev.data.index];
|
var editedRecord = record.data[ev.data.index];
|
||||||
self._setMode('edit', editedRecord.id)
|
self._setMode('edit', editedRecord.id)
|
||||||
.done(ev.data.onSuccess);
|
.then(ev.data.onSuccess);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -523,8 +528,8 @@ var ListController = BasicController.extend({
|
|||||||
_onSaveLine: function (ev) {
|
_onSaveLine: function (ev) {
|
||||||
var recordID = ev.data.recordID;
|
var recordID = ev.data.recordID;
|
||||||
this.saveRecord(recordID)
|
this.saveRecord(recordID)
|
||||||
.done(ev.data.onSuccess)
|
.then(ev.data.onSuccess)
|
||||||
.fail(ev.data.onFailure);
|
.guardedCatch(ev.data.onFailure);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* When the current selection changes (by clicking on the checkboxes on the
|
* When the current selection changes (by clicking on the checkboxes on the
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ ListRenderer.include({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
// deliberately use the 'editable' attribute instead of '_isEditable'
|
// deliberately use the 'editable' attribute instead of '_isEditable'
|
||||||
@@ -171,7 +171,7 @@ ListRenderer.include({
|
|||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @param {string[]} fields
|
* @param {string[]} fields
|
||||||
* @param {OdooEvent} ev
|
* @param {OdooEvent} ev
|
||||||
* @returns {Deferred<AbstractField[]>} resolved with the list of widgets
|
* @returns {Promise<AbstractField[]>} resolved with the list of widgets
|
||||||
* that have been reset
|
* that have been reset
|
||||||
*/
|
*/
|
||||||
confirmUpdate: function (state, id, fields, ev) {
|
confirmUpdate: function (state, id, fields, ev) {
|
||||||
@@ -180,7 +180,7 @@ ListRenderer.include({
|
|||||||
// store the cursor position to restore it once potential onchanges have
|
// store the cursor position to restore it once potential onchanges have
|
||||||
// been applied
|
// been applied
|
||||||
var currentRowID, currentWidget, focusedElement, selectionRange;
|
var currentRowID, currentWidget, focusedElement, selectionRange;
|
||||||
if (self.currentRow !== null) {
|
if (this.currentRow !== null) {
|
||||||
currentRowID = this.state.data[this.currentRow].id;
|
currentRowID = this.state.data[this.currentRow].id;
|
||||||
currentWidget = this.allFieldWidgets[currentRowID][this.currentFieldIndex];
|
currentWidget = this.allFieldWidgets[currentRowID][this.currentFieldIndex];
|
||||||
if (currentWidget) {
|
if (currentWidget) {
|
||||||
@@ -212,10 +212,14 @@ ListRenderer.include({
|
|||||||
});
|
});
|
||||||
var newRowIndex = _.findIndex(state.data, {id: id});
|
var newRowIndex = _.findIndex(state.data, {id: id});
|
||||||
var $lastRow = $row;
|
var $lastRow = $row;
|
||||||
_.each(state.data, function (record, index) {
|
var defs = [];
|
||||||
|
self.defs = defs;
|
||||||
|
state.data.forEach(function (record, index) {
|
||||||
if (index === newRowIndex) {
|
if (index === newRowIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// FIXME: as we are manipulating the DOM directly, it will
|
||||||
|
// flicker if there is an async widget in the row
|
||||||
var $newRow = self._renderRow(record);
|
var $newRow = self._renderRow(record);
|
||||||
if (index < newRowIndex) {
|
if (index < newRowIndex) {
|
||||||
$newRow.insertBefore($row);
|
$newRow.insertBefore($row);
|
||||||
@@ -224,20 +228,23 @@ ListRenderer.include({
|
|||||||
$lastRow = $newRow;
|
$lastRow = $newRow;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (self.currentRow !== null) {
|
delete self.defs;
|
||||||
self.currentRow = newRowIndex;
|
return Promise.all(defs).then(function () {
|
||||||
return self._selectCell(newRowIndex, self.currentFieldIndex, {force: true}).then(function () {
|
if (self.currentRow !== null) {
|
||||||
// restore the cursor position
|
self.currentRow = newRowIndex;
|
||||||
currentRowID = self.state.data[newRowIndex].id;
|
return self._selectCell(newRowIndex, self.currentFieldIndex, {force: true});
|
||||||
currentWidget = self.allFieldWidgets[currentRowID][self.currentFieldIndex];
|
}
|
||||||
if (currentWidget) {
|
}).then(function () {
|
||||||
focusedElement = currentWidget.getFocusableElement().get(0);
|
// restore the cursor position
|
||||||
if (selectionRange) {
|
currentRowID = self.state.data[newRowIndex].id;
|
||||||
dom.setSelectionRange(focusedElement, selectionRange);
|
currentWidget = self.allFieldWidgets[currentRowID][self.currentFieldIndex];
|
||||||
}
|
if (currentWidget) {
|
||||||
|
focusedElement = currentWidget.getFocusableElement().get(0);
|
||||||
|
if (selectionRange) {
|
||||||
|
dom.setSelectionRange(focusedElement, selectionRange);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -294,7 +301,7 @@ ListRenderer.include({
|
|||||||
*
|
*
|
||||||
* @param {string} recordID
|
* @param {string} recordID
|
||||||
* @param {string} mode
|
* @param {string} mode
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
setRowMode: function (recordID, mode) {
|
setRowMode: function (recordID, mode) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -321,7 +328,7 @@ ListRenderer.include({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rowIndex < 0) {
|
if (rowIndex < 0) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var editMode = (mode === 'edit');
|
var editMode = (mode === 'edit');
|
||||||
|
|
||||||
@@ -350,7 +357,7 @@ ListRenderer.include({
|
|||||||
options.mode = editMode ? 'edit' : 'readonly';
|
options.mode = editMode ? 'edit' : 'readonly';
|
||||||
|
|
||||||
// Switch each cell to the new mode; note: the '_renderBodyCell'
|
// Switch each cell to the new mode; note: the '_renderBodyCell'
|
||||||
// function might fill the 'this.defs' variables with multiple deferred
|
// function might fill the 'this.defs' variables with multiple promise
|
||||||
// so we create the array and delete it after the rendering.
|
// so we create the array and delete it after the rendering.
|
||||||
var defs = [];
|
var defs = [];
|
||||||
this.defs = defs;
|
this.defs = defs;
|
||||||
@@ -385,7 +392,7 @@ ListRenderer.include({
|
|||||||
$row.toggleClass('o_selected_row', editMode);
|
$row.toggleClass('o_selected_row', editMode);
|
||||||
$row.find('.o_list_record_selector input').prop('disabled', !record.res_id);
|
$row.find('.o_list_record_selector input').prop('disabled', !record.res_id);
|
||||||
|
|
||||||
return $.when.apply($, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
// necessary to trigger resize on fieldtexts
|
// necessary to trigger resize on fieldtexts
|
||||||
core.bus.trigger('DOM_updated');
|
core.bus.trigger('DOM_updated');
|
||||||
});
|
});
|
||||||
@@ -399,27 +406,32 @@ ListRenderer.include({
|
|||||||
* prevent subsequent editions. These edits would be lost, because the list
|
* prevent subsequent editions. These edits would be lost, because the list
|
||||||
* view only saves records when unselecting a row.
|
* view only saves records when unselecting a row.
|
||||||
*
|
*
|
||||||
* @returns {Deferred} The deferred resolves if the row was unselected (and
|
* @returns {Promise} The promise resolves if the row was unselected (and
|
||||||
* possibly removed). If may be rejected, when the row is dirty and the
|
* possibly removed). If may be rejected, when the row is dirty and the
|
||||||
* user refuses to discard its changes.
|
* user refuses to discard its changes.
|
||||||
*/
|
*/
|
||||||
unselectRow: function () {
|
unselectRow: function () {
|
||||||
|
var self = this;
|
||||||
// Protect against calling this method when no row is selected
|
// Protect against calling this method when no row is selected
|
||||||
if (this.currentRow === null) {
|
if (this.currentRow === null) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
var record = this.state.data[this.currentRow];
|
var record = this.state.data[this.currentRow];
|
||||||
var recordWidgets = this.allFieldWidgets[record.id];
|
var recordWidgets = this.allFieldWidgets[record.id];
|
||||||
toggleWidgets(true);
|
toggleWidgets(true);
|
||||||
|
|
||||||
var def = $.Deferred();
|
var prom = new Promise(function (resolve, reject) {
|
||||||
this.trigger_up('save_line', {
|
self.trigger_up('save_line', {
|
||||||
recordID: record.id,
|
recordID: record.id,
|
||||||
onSuccess: def.resolve.bind(def),
|
onSuccess: resolve,
|
||||||
onFailure: def.reject.bind(def),
|
onFailure: reject,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def.fail(toggleWidgets.bind(null, false));
|
prom.guardedCatch(function() {
|
||||||
|
toggleWidgets(false);
|
||||||
|
});
|
||||||
|
return prom;
|
||||||
|
|
||||||
function toggleWidgets(disabled) {
|
function toggleWidgets(disabled) {
|
||||||
_.each(recordWidgets, function (widget) {
|
_.each(recordWidgets, function (widget) {
|
||||||
@@ -511,7 +523,7 @@ ListRenderer.include({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
this.currentRow = null;
|
this.currentRow = null;
|
||||||
@@ -524,10 +536,9 @@ ListRenderer.include({
|
|||||||
* widget.
|
* widget.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @returns {jQueryElement}
|
|
||||||
*/
|
*/
|
||||||
_renderBody: function () {
|
_renderBody: function () {
|
||||||
var $body = this._super();
|
var $body = this._super.apply(this, arguments);
|
||||||
if (this.hasHandle) {
|
if (this.hasHandle) {
|
||||||
$body.sortable({
|
$body.sortable({
|
||||||
axis: 'y',
|
axis: 'y',
|
||||||
@@ -568,15 +579,13 @@ ListRenderer.include({
|
|||||||
* on the first real column.
|
* on the first real column.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @returns {jQueryElement}
|
* @returns {jQueryElement[]}
|
||||||
*/
|
*/
|
||||||
_renderRows: function () {
|
_renderRows: function () {
|
||||||
var self = this;
|
|
||||||
var $rows = this._super();
|
var $rows = this._super();
|
||||||
|
|
||||||
if (this.addCreateLine) {
|
if (this.addCreateLine) {
|
||||||
var $tr = $('<tr>');
|
var $tr = $('<tr>');
|
||||||
var colspan = self._getNumberOfCols();
|
var colspan = this._getNumberOfCols();
|
||||||
|
|
||||||
if (this.handleField) {
|
if (this.handleField) {
|
||||||
colspan = colspan - 1;
|
colspan = colspan - 1;
|
||||||
@@ -589,7 +598,7 @@ ListRenderer.include({
|
|||||||
$tr.append($td);
|
$tr.append($td);
|
||||||
$rows.push($tr);
|
$rows.push($tr);
|
||||||
|
|
||||||
_.each(self.creates, function (create, index) {
|
_.each(this.creates, function (create, index) {
|
||||||
var $a = $('<a href="#" role="button">')
|
var $a = $('<a href="#" role="button">')
|
||||||
.attr('data-context', create.context)
|
.attr('data-context', create.context)
|
||||||
.text(create.string);
|
.text(create.string);
|
||||||
@@ -604,7 +613,7 @@ ListRenderer.include({
|
|||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred} this deferred is resolved immediately
|
* @returns {Promise} this promise is resolved immediately
|
||||||
*/
|
*/
|
||||||
_renderView: function () {
|
_renderView: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -692,13 +701,13 @@ ListRenderer.include({
|
|||||||
* @param {boolean} [options.force=false] if true, force selecting the cell
|
* @param {boolean} [options.force=false] if true, force selecting the cell
|
||||||
* even if seems to be already the selected one (useful after a re-
|
* even if seems to be already the selected one (useful after a re-
|
||||||
* rendering, to reset the focus on the correct field)
|
* rendering, to reset the focus on the correct field)
|
||||||
* @return {Deferred} fails if no cell could be selected
|
* @return {Promise} fails if no cell could be selected
|
||||||
*/
|
*/
|
||||||
_selectCell: function (rowIndex, fieldIndex, options) {
|
_selectCell: function (rowIndex, fieldIndex, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
// Do nothing if the user tries to select current cell
|
// Do nothing if the user tries to select current cell
|
||||||
if (!options.force && rowIndex === this.currentRow && fieldIndex === this.currentFieldIndex) {
|
if (!options.force && rowIndex === this.currentRow && fieldIndex === this.currentFieldIndex) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var wrap = options.wrap === undefined ? true : options.wrap;
|
var wrap = options.wrap === undefined ? true : options.wrap;
|
||||||
|
|
||||||
@@ -707,7 +716,7 @@ ListRenderer.include({
|
|||||||
return this._selectRow(rowIndex).then(function () {
|
return this._selectRow(rowIndex).then(function () {
|
||||||
var record = self.state.data[rowIndex];
|
var record = self.state.data[rowIndex];
|
||||||
if (fieldIndex >= (self.allFieldWidgets[record.id] || []).length) {
|
if (fieldIndex >= (self.allFieldWidgets[record.id] || []).length) {
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
// _activateFieldWidget might trigger an onchange,
|
// _activateFieldWidget might trigger an onchange,
|
||||||
// which requires currentFieldIndex to be set
|
// which requires currentFieldIndex to be set
|
||||||
@@ -721,7 +730,7 @@ ListRenderer.include({
|
|||||||
});
|
});
|
||||||
if (fieldIndex < 0) {
|
if (fieldIndex < 0) {
|
||||||
self.currentFieldIndex = oldFieldIndex;
|
self.currentFieldIndex = oldFieldIndex;
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
self.currentFieldIndex = fieldIndex;
|
self.currentFieldIndex = fieldIndex;
|
||||||
});
|
});
|
||||||
@@ -730,12 +739,12 @@ ListRenderer.include({
|
|||||||
* Activates the row at the given row index.
|
* Activates the row at the given row index.
|
||||||
*
|
*
|
||||||
* @param {integer} rowIndex
|
* @param {integer} rowIndex
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_selectRow: function (rowIndex) {
|
_selectRow: function (rowIndex) {
|
||||||
// Do nothing if already selected
|
// Do nothing if already selected
|
||||||
if (rowIndex === this.currentRow) {
|
if (rowIndex === this.currentRow) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
// To select a row, the currently selected one must be unselected first
|
// To select a row, the currently selected one must be unselected first
|
||||||
@@ -745,15 +754,15 @@ ListRenderer.include({
|
|||||||
// The row to selected doesn't exist anymore (probably because
|
// The row to selected doesn't exist anymore (probably because
|
||||||
// an onchange triggered when unselecting the previous one
|
// an onchange triggered when unselecting the previous one
|
||||||
// removes rows)
|
// removes rows)
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
// Notify the controller we want to make a record editable
|
// Notify the controller we want to make a record editable
|
||||||
var def = $.Deferred();
|
return new Promise(function (resolve) {
|
||||||
self.trigger_up('edit_line', {
|
self.trigger_up('edit_line', {
|
||||||
index: rowIndex,
|
index: rowIndex,
|
||||||
onSuccess: def.resolve.bind(def),
|
onSuccess: resolve,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return def;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -778,7 +787,7 @@ ListRenderer.include({
|
|||||||
// but we do want to unselect current row
|
// but we do want to unselect current row
|
||||||
var self = this;
|
var self = this;
|
||||||
this.unselectRow().then(function () {
|
this.unselectRow().then(function () {
|
||||||
self.trigger_up('add_record', {context: ev.currentTarget.dataset.context && [ev.currentTarget.dataset.context]}); // TODO write a test, the deferred was not considered
|
self.trigger_up('add_record', {context: ev.currentTarget.dataset.context && [ev.currentTarget.dataset.context]}); // TODO write a test, the promise was not considered
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -864,12 +873,13 @@ ListRenderer.include({
|
|||||||
* @param {OdooEvent} ev
|
* @param {OdooEvent} ev
|
||||||
*/
|
*/
|
||||||
_onNavigationMove: function (ev) {
|
_onNavigationMove: function (ev) {
|
||||||
|
var self = this;
|
||||||
ev.stopPropagation(); // stop the event, the action is done by this renderer
|
ev.stopPropagation(); // stop the event, the action is done by this renderer
|
||||||
switch (ev.data.direction) {
|
switch (ev.data.direction) {
|
||||||
case 'previous':
|
case 'previous':
|
||||||
if (this.currentFieldIndex > 0) {
|
if (this.currentFieldIndex > 0) {
|
||||||
this._selectCell(this.currentRow, this.currentFieldIndex - 1, {wrap: false})
|
this._selectCell(this.currentRow, this.currentFieldIndex - 1, {wrap: false})
|
||||||
.fail(this._moveToPreviousLine.bind(this));
|
.guardedCatch(this._moveToPreviousLine.bind(this));
|
||||||
} else {
|
} else {
|
||||||
this._moveToPreviousLine();
|
this._moveToPreviousLine();
|
||||||
}
|
}
|
||||||
@@ -882,7 +892,7 @@ ListRenderer.include({
|
|||||||
if (column.attrs.name === lastWidget.name) {
|
if (column.attrs.name === lastWidget.name) {
|
||||||
if (this.currentRow + 1 < this.state.data.length) {
|
if (this.currentRow + 1 < this.state.data.length) {
|
||||||
this._selectCell(this.currentRow+1, 0, {wrap:false})
|
this._selectCell(this.currentRow+1, 0, {wrap:false})
|
||||||
.fail(this._moveToNextLine.bind(this));
|
.guardedCatch(this._moveToNextLine.bind(this));
|
||||||
} else {
|
} else {
|
||||||
var currentRowData = this.state.data[this.currentRow];
|
var currentRowData = this.state.data[this.currentRow];
|
||||||
if (currentRowData.isDirty(currentRowData.id)) {
|
if (currentRowData.isDirty(currentRowData.id)) {
|
||||||
@@ -895,7 +905,7 @@ ListRenderer.include({
|
|||||||
} else {
|
} else {
|
||||||
if (this.currentFieldIndex + 1 < this.columns.length) {
|
if (this.currentFieldIndex + 1 < this.columns.length) {
|
||||||
this._selectCell(this.currentRow, this.currentFieldIndex + 1, {wrap: false})
|
this._selectCell(this.currentRow, this.currentFieldIndex + 1, {wrap: false})
|
||||||
.fail(this._moveToNextLine.bind(this));
|
.guardedCatch(this._moveToNextLine.bind(this));
|
||||||
} else {
|
} else {
|
||||||
this._moveToNextLine();
|
this._moveToNextLine();
|
||||||
}
|
}
|
||||||
@@ -911,8 +921,10 @@ ListRenderer.include({
|
|||||||
ev.data.originalEvent.stopPropagation();
|
ev.data.originalEvent.stopPropagation();
|
||||||
this.trigger_up('discard_changes', {
|
this.trigger_up('discard_changes', {
|
||||||
recordID: ev.target.dataPointID,
|
recordID: ev.target.dataPointID,
|
||||||
|
onSuccess: function () {
|
||||||
|
self.$('.o_field_x2many_list_row_add a:first').focus();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.$('.o_field_x2many_list_row_add a:first').focus();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -250,9 +250,10 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
* @returns {jQueryElement} a jquery element <tbody>
|
* @returns {jQueryElement} a jquery element <tbody>
|
||||||
*/
|
*/
|
||||||
_renderBody: function () {
|
_renderBody: function () {
|
||||||
|
var self = this;
|
||||||
var $rows = this._renderRows();
|
var $rows = this._renderRows();
|
||||||
while ($rows.length < 4) {
|
while ($rows.length < 4) {
|
||||||
$rows.push(this._renderEmptyRow());
|
$rows.push(self._renderEmptyRow());
|
||||||
}
|
}
|
||||||
return $('<tbody>').append($rows);
|
return $('<tbody>').append($rows);
|
||||||
},
|
},
|
||||||
@@ -306,7 +307,6 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
}
|
}
|
||||||
if (node.attrs.widget || (options && options.renderWidgets)) {
|
if (node.attrs.widget || (options && options.renderWidgets)) {
|
||||||
var $el = this._renderFieldWidget(node, record, _.pick(options, 'mode'));
|
var $el = this._renderFieldWidget(node, record, _.pick(options, 'mode'));
|
||||||
this._handleAttributes($el, node);
|
|
||||||
return $td.append($el);
|
return $td.append($el);
|
||||||
}
|
}
|
||||||
var name = node.attrs.name;
|
var name = node.attrs.name;
|
||||||
@@ -395,7 +395,7 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} group
|
* @param {Object} group
|
||||||
* @returns {JQueryElement} the pager's $el
|
* @returns {jQueryElement} the pager's $el
|
||||||
*/
|
*/
|
||||||
_renderGroupPager: function (group) {
|
_renderGroupPager: function (group) {
|
||||||
var pager = new Pager(this, group.count, group.offset + 1, group.limit);
|
var pager = new Pager(this, group.count, group.offset + 1, group.limit);
|
||||||
@@ -416,9 +416,14 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
// register the pager so that it can be destroyed on next rendering
|
// register the pager so that it can be destroyed on next rendering
|
||||||
this.pagers.push(pager);
|
this.pagers.push(pager);
|
||||||
|
|
||||||
var fragment = document.createDocumentFragment();
|
var pagerProm = pager._widgetRenderAndInsert(function () {}); // start the pager
|
||||||
pager.appendTo(fragment); // starts the pager
|
this.defs.push(pagerProm);
|
||||||
return pager.$el;
|
var $el = $('<div>');
|
||||||
|
pagerProm.then(function () {
|
||||||
|
$el.replaceWith(pager.$el);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $el;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Render the row that represent a group
|
* Render the row that represent a group
|
||||||
@@ -514,10 +519,9 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
* with the name of each fields
|
* with the name of each fields
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {boolean} isGrouped
|
|
||||||
* @returns {jQueryElement} a <thead> element
|
* @returns {jQueryElement} a <thead> element
|
||||||
*/
|
*/
|
||||||
_renderHeader: function (isGrouped) {
|
_renderHeader: function () {
|
||||||
var $tr = $('<tr>')
|
var $tr = $('<tr>')
|
||||||
.append(_.map(this.columns, this._renderHeaderCell.bind(this)));
|
.append(_.map(this.columns, this._renderHeaderCell.bind(this)));
|
||||||
if (this.hasSelectors) {
|
if (this.hasSelectors) {
|
||||||
@@ -585,11 +589,9 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
*/
|
*/
|
||||||
_renderRow: function (record) {
|
_renderRow: function (record) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.defs = []; // TODO maybe wait for those somewhere ?
|
var $cells = this.columns.map(function (node, index) {
|
||||||
var $cells = _.map(this.columns, function (node, index) {
|
|
||||||
return self._renderBodyCell(record, node, index, { mode: 'readonly' });
|
return self._renderBodyCell(record, node, index, { mode: 'readonly' });
|
||||||
});
|
});
|
||||||
delete this.defs;
|
|
||||||
|
|
||||||
var $tr = $('<tr/>', { class: 'o_data_row' })
|
var $tr = $('<tr/>', { class: 'o_data_row' })
|
||||||
.data('id', record.id)
|
.data('id', record.id)
|
||||||
@@ -608,7 +610,7 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
* @returns {jQueryElement[]} a list of <tr>
|
* @returns {jQueryElement[]} a list of <tr>
|
||||||
*/
|
*/
|
||||||
_renderRows: function () {
|
_renderRows: function () {
|
||||||
return _.map(this.state.data, this._renderRow.bind(this));
|
return this.state.data.map(this._renderRow.bind(this));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* A 'selector' is the small checkbox on the left of a record in a list
|
* A 'selector' is the small checkbox on the left of a record in a list
|
||||||
@@ -638,52 +640,60 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* returns {Deferred} this deferred is resolved immediately
|
* @returns {Promise} resolved when the view has been rendered
|
||||||
*/
|
*/
|
||||||
_renderView: function () {
|
_renderView: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.$el
|
var oldPagers = this.pagers;
|
||||||
.removeClass('table-responsive')
|
|
||||||
.empty();
|
|
||||||
|
|
||||||
// destroy the previously instantiated pagers, if any
|
|
||||||
_.invoke(this.pagers, 'destroy');
|
|
||||||
this.pagers = [];
|
this.pagers = [];
|
||||||
|
|
||||||
var displayNoContentHelper = !this._hasContent() && !!this.noContentHelp;
|
|
||||||
// display the no content helper if there is no data to display
|
// display the no content helper if there is no data to display
|
||||||
|
var displayNoContentHelper = !this._hasContent() && !!this.noContentHelp;
|
||||||
if (displayNoContentHelper) {
|
if (displayNoContentHelper) {
|
||||||
|
// destroy the previously instantiated pagers, if any
|
||||||
|
_.invoke(oldPagers, 'destroy');
|
||||||
|
|
||||||
|
this.$el.removeClass('table-responsive');
|
||||||
this.$el.html(this._renderNoContentHelper());
|
this.$el.html(this._renderNoContentHelper());
|
||||||
return this._super();
|
return this._super.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
var $table = $('<table>').addClass('o_list_view table table-sm table-hover table-striped');
|
var orderedBy = this.state.orderedBy;
|
||||||
this.$el.addClass('table-responsive')
|
this.hasHandle = orderedBy.length === 0 || orderedBy[0].name === this.handleField;
|
||||||
.append($table);
|
|
||||||
this._computeAggregates();
|
this._computeAggregates();
|
||||||
|
|
||||||
|
var $table = $('<table>').addClass('o_list_view table table-sm table-hover table-striped');
|
||||||
$table.toggleClass('o_list_view_grouped', this.isGrouped);
|
$table.toggleClass('o_list_view_grouped', this.isGrouped);
|
||||||
$table.toggleClass('o_list_view_ungrouped', !this.isGrouped);
|
$table.toggleClass('o_list_view_ungrouped', !this.isGrouped);
|
||||||
this.hasHandle = this.state.orderedBy.length === 0 ||
|
var defs = [];
|
||||||
this.state.orderedBy[0].name === this.handleField;
|
this.defs = defs;
|
||||||
if (this.isGrouped) {
|
if (this.isGrouped) {
|
||||||
$table
|
$table.append(this._renderHeader());
|
||||||
.append(this._renderHeader(true))
|
$table.append(this._renderGroups(this.state.data));
|
||||||
.append(this._renderGroups(this.state.data))
|
$table.append(this._renderFooter());
|
||||||
.append(this._renderFooter());
|
|
||||||
} else {
|
} else {
|
||||||
$table
|
$table.append(this._renderHeader());
|
||||||
.append(this._renderHeader())
|
$table.append(this._renderBody());
|
||||||
.append(this._renderBody())
|
$table.append(this._renderFooter());
|
||||||
.append(this._renderFooter());
|
|
||||||
}
|
}
|
||||||
if (this.selection.length) {
|
delete this.defs;
|
||||||
var $checked_rows = this.$('tr').filter(function (index, el) {
|
|
||||||
return _.contains(self.selection, $(el).data('id'));
|
var prom = Promise.all(defs).then(function () {
|
||||||
});
|
// destroy the previously instantiated pagers, if any
|
||||||
$checked_rows.find('.o_list_record_selector input').prop('checked', true);
|
_.invoke(oldPagers, 'destroy');
|
||||||
}
|
|
||||||
return this._super();
|
self.$el.addClass('table-responsive').html($table);
|
||||||
|
|
||||||
|
if (self.selection.length) {
|
||||||
|
var $checked_rows = self.$('tr').filter(function (index, el) {
|
||||||
|
return _.contains(self.selection, $(el).data('id'));
|
||||||
|
});
|
||||||
|
$checked_rows.find('.o_list_record_selector input').prop('checked', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Promise.all([this._super.apply(this, arguments), prom]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Each line can be decorated according to a few simple rules. The arch
|
* Each line can be decorated according to a few simple rules. The arch
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ var PivotModel = AbstractModel.extend({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Close a header. This method is actually synchronous, but returns a
|
* Close a header. This method is actually synchronous, but returns a
|
||||||
* deferred.
|
* promise.
|
||||||
*
|
*
|
||||||
* @param {any} headerID
|
* @param {any} headerID
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
closeHeader: function (headerID) {
|
closeHeader: function (headerID) {
|
||||||
var header = this.data.headers[headerID];
|
var header = this.data.headers[headerID];
|
||||||
@@ -58,7 +58,7 @@ var PivotModel = AbstractModel.extend({
|
|||||||
header.root.groupbys.splice(newGroupbyLength);
|
header.root.groupbys.splice(newGroupbyLength);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
expandAll: function () {
|
expandAll: function () {
|
||||||
return this._loadData();
|
return this._loadData();
|
||||||
@@ -118,8 +118,8 @@ var PivotModel = AbstractModel.extend({
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return $.when.apply(null, defs).then(function () {
|
return Promise.all(defs).then(function () {
|
||||||
var results = Array.prototype.slice.call(arguments);
|
var results = Array.prototype.slice.call(arguments[0]);
|
||||||
var data = [];
|
var data = [];
|
||||||
var comparisonData = [];
|
var comparisonData = [];
|
||||||
_.each(results, function (result) {
|
_.each(results, function (result) {
|
||||||
@@ -358,7 +358,7 @@ var PivotModel = AbstractModel.extend({
|
|||||||
* @param {string[]} params.compare
|
* @param {string[]} params.compare
|
||||||
* @param {Object} params.fields
|
* @param {Object} params.fields
|
||||||
* @param {string} params.default_order
|
* @param {string} params.default_order
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
load: function (params) {
|
load: function (params) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -394,7 +394,7 @@ var PivotModel = AbstractModel.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @param {any} handle this parameter is ignored
|
* @param {any} handle this parameter is ignored
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
reload: function (handle, params) {
|
reload: function (handle, params) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -507,14 +507,14 @@ var PivotModel = AbstractModel.extend({
|
|||||||
* Toggle the active state for a given measure, then reload the data.
|
* Toggle the active state for a given measure, then reload the data.
|
||||||
*
|
*
|
||||||
* @param {string} field
|
* @param {string} field
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
toggleMeasure: function (field) {
|
toggleMeasure: function (field) {
|
||||||
if (_.contains(this.data.measures, field)) {
|
if (_.contains(this.data.measures, field)) {
|
||||||
this.data.measures = _.without(this.data.measures, field);
|
this.data.measures = _.without(this.data.measures, field);
|
||||||
// in this case, we already have all data in memory, no need to
|
// in this case, we already have all data in memory, no need to
|
||||||
// actually reload a lesser amount of information
|
// actually reload a lesser amount of information
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
this.data.measures.push(field);
|
this.data.measures.push(field);
|
||||||
}
|
}
|
||||||
@@ -705,7 +705,7 @@ var PivotModel = AbstractModel.extend({
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_loadData: function () {
|
_loadData: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -755,8 +755,7 @@ var PivotModel = AbstractModel.extend({
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._loadDataDropPrevious.add($.when.apply(null, defs)).then(function () {
|
return this._loadDataDropPrevious.add(Promise.all(defs)).then(function (results) {
|
||||||
var results = Array.prototype.slice.call(arguments);
|
|
||||||
var data = [];
|
var data = [];
|
||||||
var comparisonData = [];
|
var comparisonData = [];
|
||||||
_.each(results, function (result) {
|
_.each(results, function (result) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ var PivotRenderer = AbstractRenderer.extend({
|
|||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_render: function () {
|
_render: function () {
|
||||||
if (!this._hasContent()) {
|
if (!this._hasContent()) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ var StandaloneFieldManagerMixin = _.extend({}, FieldManagerMixin, {
|
|||||||
* @param {string} id basicModel Id for the changed record
|
* @param {string} id basicModel Id for the changed record
|
||||||
* @param {string[]} fields the fields (names) that have been changed
|
* @param {string[]} fields the fields (names) that have been changed
|
||||||
* @param {OdooEvent} event the event that triggered the change
|
* @param {OdooEvent} event the event that triggered the change
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_confirmChange: function (id, fields, event) {
|
_confirmChange: function (id, fields, event) {
|
||||||
var result = FieldManagerMixin._confirmChange.apply(this, arguments);
|
var result = FieldManagerMixin._confirmChange.apply(this, arguments);
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
text: (multi_select ? _t("Save & Close") : _t("Save")),
|
text: (multi_select ? _t("Save & Close") : _t("Save")),
|
||||||
classes: "btn-primary",
|
classes: "btn-primary",
|
||||||
click: function () {
|
click: function () {
|
||||||
this._save().then(self.close.bind(self));
|
self._save().then(self.close.bind(self));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
text: _t("Save & New"),
|
text: _t("Save & New"),
|
||||||
classes: "btn-primary",
|
classes: "btn-primary",
|
||||||
click: function () {
|
click: function () {
|
||||||
this._save()
|
self._save()
|
||||||
.then(self.form_view.createRecord.bind(self.form_view, self.parentID))
|
.then(self.form_view.createRecord.bind(self.form_view, self.parentID))
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (!self.deletable) {
|
if (!self.deletable) {
|
||||||
@@ -183,7 +183,7 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
var FormView = view_registry.get('form');
|
var FormView = view_registry.get('form');
|
||||||
var fields_view_def;
|
var fields_view_def;
|
||||||
if (this.options.fields_view) {
|
if (this.options.fields_view) {
|
||||||
fields_view_def = $.when(this.options.fields_view);
|
fields_view_def = Promise.resolve(this.options.fields_view);
|
||||||
} else {
|
} else {
|
||||||
fields_view_def = this.loadFieldView(this.res_model, this.context, this.options.view_id, 'form');
|
fields_view_def = this.loadFieldView(this.res_model, this.context, this.options.view_id, 'form');
|
||||||
}
|
}
|
||||||
@@ -213,9 +213,9 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
if (self.recordID && self.shouldSaveLocally) {
|
if (self.recordID && self.shouldSaveLocally) {
|
||||||
self.model.save(self.recordID, {savePoint: true});
|
self.model.save(self.recordID, {savePoint: true});
|
||||||
}
|
}
|
||||||
self.form_view.appendTo(fragment)
|
return self.form_view.appendTo(fragment)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
self.opened().always(function () {
|
self.opened().then(function () {
|
||||||
var $buttons = $('<div>');
|
var $buttons = $('<div>');
|
||||||
self.form_view.renderButtons($buttons);
|
self.form_view.renderButtons($buttons);
|
||||||
if ($buttons.children().length) {
|
if ($buttons.children().length) {
|
||||||
@@ -226,7 +226,7 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
in_DOM: true,
|
in_DOM: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
_super();
|
return _super();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_save: function () {
|
_save: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -332,7 +332,7 @@ var SelectCreateDialog = ViewDialog.extend({
|
|||||||
}
|
}
|
||||||
var self = this;
|
var self = this;
|
||||||
var _super = this._super.bind(this);
|
var _super = this._super.bind(this);
|
||||||
this.loadViews(this.res_model, this.context, [[false, 'list'], [false, 'search']], {})
|
return this.loadViews(this.res_model, this.context, [[false, 'list'], [false, 'search']], {})
|
||||||
.then(this.setup.bind(this))
|
.then(this.setup.bind(this))
|
||||||
.then(function (fragment) {
|
.then(function (fragment) {
|
||||||
self.opened().then(function () {
|
self.opened().then(function () {
|
||||||
@@ -342,9 +342,8 @@ var SelectCreateDialog = ViewDialog.extend({
|
|||||||
});
|
});
|
||||||
self.set_buttons(self.__buttons);
|
self.set_buttons(self.__buttons);
|
||||||
});
|
});
|
||||||
_super();
|
return _super();
|
||||||
});
|
});
|
||||||
return this;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: function (fieldsViews) {
|
setup: function (fieldsViews) {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ var viewUtils = {
|
|||||||
*/
|
*/
|
||||||
renderButtonFromNode: function (node, options) {
|
renderButtonFromNode: function (node, options) {
|
||||||
var btnOptions = {
|
var btnOptions = {
|
||||||
attrs: _.omit(node.attrs, 'icon', 'string', 'type', 'attrs', 'modifiers', 'options'),
|
attrs: _.omit(node.attrs, 'icon', 'string', 'type', 'attrs', 'modifiers', 'options', 'effect'),
|
||||||
icon: node.attrs.icon,
|
icon: node.attrs.icon,
|
||||||
};
|
};
|
||||||
if (options && options.extraClass) {
|
if (options && options.extraClass) {
|
||||||
|
|||||||
@@ -108,26 +108,27 @@ var AttachDocument = Widget.extend({
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_onFileLoaded: function () {
|
_onFileLoaded: function () {
|
||||||
var self = this,
|
var self = this;
|
||||||
def = $.Deferred();
|
return new Promise(function (resolve) {
|
||||||
|
if (self.node.attrs.action) {
|
||||||
if (self.node.attrs.action) {
|
// the first argument isn't a file but the jQuery.Event
|
||||||
// the first argument isn't a file but the jQuery.Event
|
var files = Array.prototype.slice.call(arguments, 1);
|
||||||
var files = Array.prototype.slice.call(arguments, 1);
|
self._rpc({
|
||||||
self._rpc({
|
model: self.res_model,
|
||||||
model: self.res_model,
|
method: self.node.attrs.action,
|
||||||
method: self.node.attrs.action,
|
args: [self.res_id],
|
||||||
args: [self.res_id],
|
kwargs: {
|
||||||
kwargs: {
|
attachment_ids: _.map(files, function (file) {
|
||||||
'attachment_ids': _.map(files, function (file) {return file.id;}),
|
return file.id;
|
||||||
}
|
}),
|
||||||
}).then( function () {
|
}
|
||||||
def.resolve();
|
}).then(function () {
|
||||||
});
|
resolve();
|
||||||
} else {
|
});
|
||||||
def.resolve();
|
} else {
|
||||||
}
|
resolve();
|
||||||
return $.when(def).then(function () {
|
}
|
||||||
|
}).then(function () {
|
||||||
self.trigger_up('reload');
|
self.trigger_up('reload');
|
||||||
framework.unblockUI();
|
framework.unblockUI();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ var ChangePassword = AbstractAction.extend({
|
|||||||
* @fixme: weird interaction with the parent for the $buttons handling
|
* @fixme: weird interaction with the parent for the $buttons handling
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -37,7 +37,7 @@ var ChangePassword = AbstractAction.extend({
|
|||||||
fields: $('form[name=change_password_form]').serializeArray()
|
fields: $('form[name=change_password_form]').serializeArray()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.done(function (result) {
|
.then(function (result) {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
self._display_error(result);
|
self._display_error(result);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ var DataExport = Dialog.extend({
|
|||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var defs = [this._super.apply(this, arguments)];
|
var proms = [this._super.apply(this, arguments)];
|
||||||
|
|
||||||
// The default for the ".modal_content" element is "max-height: 100%;"
|
// The default for the ".modal_content" element is "max-height: 100%;"
|
||||||
// but we want it to always expand to "height: 100%;" for this modal.
|
// but we want it to always expand to "height: 100%;" for this modal.
|
||||||
@@ -67,10 +67,10 @@ var DataExport = Dialog.extend({
|
|||||||
this.$fieldsList = this.$('.o_fields_list');
|
this.$fieldsList = this.$('.o_fields_list');
|
||||||
this.$importCompatRadios = this.$('.o_import_compat input');
|
this.$importCompatRadios = this.$('.o_import_compat input');
|
||||||
|
|
||||||
defs.push(this._rpc({route: '/web/export/formats'}).then(doSetupExportFormats));
|
proms.push(this._rpc({route: '/web/export/formats'}).then(doSetupExportFormats));
|
||||||
defs.push(this._onChangeCompatibleInput());
|
proms.push(this._onChangeCompatibleInput());
|
||||||
|
|
||||||
defs.push(this.getParent().getActiveDomain().then(function (domain) {
|
proms.push(this.getParent().getActiveDomain().then(function (domain) {
|
||||||
if (domain === undefined) {
|
if (domain === undefined) {
|
||||||
self.idsToExport = self.getParent().getSelectedIds();
|
self.idsToExport = self.getParent().getSelectedIds();
|
||||||
self.domain = self.record.domain;
|
self.domain = self.record.domain;
|
||||||
@@ -80,7 +80,7 @@ var DataExport = Dialog.extend({
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
defs.push(this._showExportsList().then(function () {
|
proms.push(this._showExportsList().then(function () {
|
||||||
_.each(self.records, function (record, key) {
|
_.each(self.records, function (record, key) {
|
||||||
if (_.contains(self.defaultExportFields, key)) {
|
if (_.contains(self.defaultExportFields, key)) {
|
||||||
self._addField(key, record.string);
|
self._addField(key, record.string);
|
||||||
@@ -88,12 +88,12 @@ var DataExport = Dialog.extend({
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(proms);
|
||||||
|
|
||||||
function doSetupExportFormats (formats) {
|
function doSetupExportFormats(formats) {
|
||||||
var $fmts = self.$('.o_export_format');
|
var $fmts = self.$('.o_export_format');
|
||||||
|
|
||||||
_.each(formats, function (format, i) {
|
_.each(formats, function (format) {
|
||||||
var $radio = $('<input/>', {type: 'radio', value: format.tag, name: 'o_export_format_name'});
|
var $radio = $('<input/>', {type: 'radio', value: format.tag, name: 'o_export_format_name'});
|
||||||
var $label = $('<span/>', {html: format.label});
|
var $label = $('<span/>', {html: format.label});
|
||||||
|
|
||||||
@@ -192,16 +192,16 @@ var DataExport = Dialog.extend({
|
|||||||
*/
|
*/
|
||||||
_onExpandAction: function (record) {
|
_onExpandAction: function (record) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!record['children']) {
|
if (!record.children) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var model = record['params']['model'];
|
var model = record.params.model;
|
||||||
var prefix = record['params']['prefix'];
|
var prefix = record.params.prefix;
|
||||||
var name = record['params']['name'];
|
var name = record.params.name;
|
||||||
var excludeFields = [];
|
var excludeFields = [];
|
||||||
if (record['relation_field']) {
|
if (record.relation_field) {
|
||||||
excludeFields.push(record['relation_field']);
|
excludeFields.push(record.relation_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!record.loaded) {
|
if (!record.loaded) {
|
||||||
@@ -212,11 +212,11 @@ var DataExport = Dialog.extend({
|
|||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
parent_name: name,
|
parent_name: name,
|
||||||
import_compat: !!this.$importCompatRadios.filter(':checked').val(),
|
import_compat: !!this.$importCompatRadios.filter(':checked').val(),
|
||||||
parent_field_type: record['field_type'],
|
parent_field_type: record.field_type,
|
||||||
parent_field: record['params']['parent_field'],
|
parent_field: record.params.parent_field,
|
||||||
exclude: excludeFields,
|
exclude: excludeFields,
|
||||||
},
|
},
|
||||||
}).done(function (results) {
|
}).then(function (results) {
|
||||||
record.loaded = true;
|
record.loaded = true;
|
||||||
self._onShowData(results, record.id);
|
self._onShowData(results, record.id);
|
||||||
});
|
});
|
||||||
@@ -294,7 +294,7 @@ var DataExport = Dialog.extend({
|
|||||||
var self = this;
|
var self = this;
|
||||||
if (this.$('.o_exported_lists_select').is(':hidden')) {
|
if (this.$('.o_exported_lists_select').is(':hidden')) {
|
||||||
this.$('.o_exported_lists').show();
|
this.$('.o_exported_lists').show();
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._rpc({
|
return this._rpc({
|
||||||
@@ -355,7 +355,7 @@ var DataExport = Dialog.extend({
|
|||||||
model: this.record.model,
|
model: this.record.model,
|
||||||
import_compat: this.isCompatibleMode,
|
import_compat: this.isCompatibleMode,
|
||||||
},
|
},
|
||||||
}).done(function (records) {
|
}).then(function (records) {
|
||||||
var compatibleFields = _.map(records, function (record) { return record.id; });
|
var compatibleFields = _.map(records, function (record) { return record.id; });
|
||||||
self.$fieldsList
|
self.$fieldsList
|
||||||
.find('option')
|
.find('option')
|
||||||
|
|||||||
@@ -188,11 +188,14 @@ var DomainTree = DomainNode.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @see DomainNode.start
|
* @see DomainNode.start
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this._postRender();
|
this._postRender();
|
||||||
return $.when(this._super.apply(this, arguments), this._renderChildrenTo(this.$childrenContainer));
|
return Promise.all([
|
||||||
|
this._super.apply(this, arguments),
|
||||||
|
this._renderChildrenTo(this.$childrenContainer)
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -369,11 +372,11 @@ var DomainTree = DomainNode.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {jQuery} $to - the jQuery node to which the children must be added
|
* @param {jQuery} $to - the jQuery node to which the children must be added
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_renderChildrenTo: function ($to) {
|
_renderChildrenTo: function ($to) {
|
||||||
var $div = $("<div/>");
|
var $div = $("<div/>");
|
||||||
return $.when.apply($, _.map(this.children, (function (child) {
|
return Promise.all(_.map(this.children, (function (child) {
|
||||||
return child.appendTo($div);
|
return child.appendTo($div);
|
||||||
}).bind(this))).then((function () {
|
}).bind(this))).then((function () {
|
||||||
_.each(this.children, function (child) {
|
_.each(this.children, function (child) {
|
||||||
@@ -485,11 +488,11 @@ var DomainSelector = DomainTree.extend({
|
|||||||
* does nothing.
|
* does nothing.
|
||||||
*
|
*
|
||||||
* @param {string} domain
|
* @param {string} domain
|
||||||
* @returns {Deferred} resolved when the rerendering is finished
|
* @returns {Promise} resolved when the rerendering is finished
|
||||||
*/
|
*/
|
||||||
setDomain: function (domain) {
|
setDomain: function (domain) {
|
||||||
if (domain === Domain.prototype.arrayToString(this.getDomain())) {
|
if (domain === Domain.prototype.arrayToString(this.getDomain())) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
var parsedDomain = this._parseDomain(domain);
|
var parsedDomain = this._parseDomain(domain);
|
||||||
if (parsedDomain) {
|
if (parsedDomain) {
|
||||||
@@ -542,7 +545,7 @@ var DomainSelector = DomainTree.extend({
|
|||||||
* This method is ugly but achieves the right behavior without flickering.
|
* This method is ugly but achieves the right behavior without flickering.
|
||||||
*
|
*
|
||||||
* @param {Array|string} domain
|
* @param {Array|string} domain
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
_redraw: function (domain) {
|
_redraw: function (domain) {
|
||||||
var oldChildren = this.children.slice();
|
var oldChildren = this.children.slice();
|
||||||
@@ -637,7 +640,7 @@ var DomainLeaf = DomainNode.extend({
|
|||||||
* Prepares the information the rendering of the widget will need by
|
* Prepares the information the rendering of the widget will need by
|
||||||
* pre-instantiating its internal field selector widget.
|
* pre-instantiating its internal field selector widget.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
willStart: function () {
|
willStart: function () {
|
||||||
var defs = [this._super.apply(this, arguments)];
|
var defs = [this._super.apply(this, arguments)];
|
||||||
@@ -702,17 +705,17 @@ var DomainLeaf = DomainNode.extend({
|
|||||||
}).bind(this)));
|
}).bind(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $.when.apply($, wDefs);
|
return Promise.all(wDefs);
|
||||||
}
|
}
|
||||||
}).bind(this)));
|
}).bind(this)));
|
||||||
|
|
||||||
return $.when.apply($, defs);
|
return Promise.all(defs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @see DomainNode.start
|
* @see DomainNode.start
|
||||||
* Appends the prepared field selector and value widget.
|
* Appends the prepared field selector and value widget.
|
||||||
*
|
*
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this.fieldSelector.$el.prependTo(this.$("> .o_domain_leaf_info, > .o_domain_leaf_edition")); // place the field selector
|
this.fieldSelector.$el.prependTo(this.$("> .o_domain_leaf_info, > .o_domain_leaf_edition")); // place the field selector
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ return Dialog.extend({
|
|||||||
// this restores default modal height (bootstrap) and allows field selector to overflow
|
// this restores default modal height (bootstrap) and allows field selector to overflow
|
||||||
self.$el.css('overflow', 'visible').closest('.modal-dialog').css('height', 'auto');
|
self.$el.css('overflow', 'visible').closest('.modal-dialog').css('height', 'auto');
|
||||||
});
|
});
|
||||||
return $.when(
|
return Promise.all([
|
||||||
this._super.apply(this, arguments),
|
this._super.apply(this, arguments),
|
||||||
this.domainSelector.appendTo(this.$el)
|
this.domainSelector.appendTo(this.$el)
|
||||||
);
|
]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ var IFrameWidget = Widget.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this.$el.css({height: '100%', width: '100%', border: 0});
|
this.$el.css({height: '100%', width: '100%', border: 0});
|
||||||
|
|||||||
@@ -112,17 +112,17 @@ var ModelFieldSelector = Widget.extend({
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @see Widget.willStart()
|
* @see Widget.willStart()
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
willStart: function () {
|
willStart: function () {
|
||||||
return $.when(
|
return Promise.all([
|
||||||
this._super.apply(this, arguments),
|
this._super.apply(this, arguments),
|
||||||
this._prefill()
|
this._prefill()
|
||||||
);
|
]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @see Widget.start
|
* @see Widget.start
|
||||||
* @returns {Deferred}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
start: function () {
|
start: function () {
|
||||||
this.$value = this.$(".o_field_selector_value");
|
this.$value = this.$(".o_field_selector_value");
|
||||||
@@ -162,11 +162,11 @@ var ModelFieldSelector = Widget.extend({
|
|||||||
* Saves a new field chain (array) and re-render.
|
* Saves a new field chain (array) and re-render.
|
||||||
*
|
*
|
||||||
* @param {string[]} chain - the new field chain
|
* @param {string[]} chain - the new field chain
|
||||||
* @returns {Deferred} resolved once the re-rendering is finished
|
* @returns {Promise} resolved once the re-rendering is finished
|
||||||
*/
|
*/
|
||||||
setChain: function (chain) {
|
setChain: function (chain) {
|
||||||
if (_.isEqual(chain, this.chain)) {
|
if (_.isEqual(chain, this.chain)) {
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chain = chain;
|
this.chain = chain;
|
||||||
@@ -290,14 +290,14 @@ var ModelFieldSelector = Widget.extend({
|
|||||||
* chain.
|
* chain.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Deferred} resolved once the whole field chain has been
|
* @returns {Promise} resolved once the whole field chain has been
|
||||||
* processed
|
* processed
|
||||||
*/
|
*/
|
||||||
_prefill: function () {
|
_prefill: function () {
|
||||||
this.pages = [];
|
this.pages = [];
|
||||||
return this._pushPageData(this.model).then((function () {
|
return this._pushPageData(this.model).then((function () {
|
||||||
this._validate(true);
|
this._validate(true);
|
||||||
return (this.chain.length ? processChain.call(this, this.chain.slice().reverse()) : $.when());
|
return (this.chain.length ? processChain.call(this, this.chain.slice().reverse()) : Promise.resolve());
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
|
|
||||||
function processChain(chain) {
|
function processChain(chain) {
|
||||||
@@ -305,11 +305,11 @@ var ModelFieldSelector = Widget.extend({
|
|||||||
if (field && field.relation && chain.length > 0) { // Fetch next chain node if any and possible
|
if (field && field.relation && chain.length > 0) { // Fetch next chain node if any and possible
|
||||||
return this._pushPageData(field.relation).then(processChain.bind(this, chain));
|
return this._pushPageData(field.relation).then(processChain.bind(this, chain));
|
||||||
} else if (field && chain.length === 0) { // Last node fetched
|
} else if (field && chain.length === 0) { // Last node fetched
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
} else { // Wrong node chain
|
} else { // Wrong node chain
|
||||||
this._validate(false);
|
this._validate(false);
|
||||||
}
|
}
|
||||||
return $.when();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -318,12 +318,12 @@ var ModelFieldSelector = Widget.extend({
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} model - the model name whose fields have to be fetched
|
* @param {string} model - the model name whose fields have to be fetched
|
||||||
* @returns {Deferred} resolved once the fields have been added
|
* @returns {Promise} resolved once the fields have been added
|
||||||
*/
|
*/
|
||||||
_pushPageData: function (model) {
|
_pushPageData: function (model) {
|
||||||
var def;
|
var def;
|
||||||
if (this.model === model && this.options.fields) {
|
if (this.model === model && this.options.fields) {
|
||||||
def = $.when(sortFields(this.options.fields, model, this.options.order));
|
def = Promise.resolve(sortFields(this.options.fields, model, this.options.order));
|
||||||
} else {
|
} else {
|
||||||
def = this._getModelFieldsFromCache(model, this.options.filters);
|
def = this._getModelFieldsFromCache(model, this.options.filters);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,14 +80,12 @@ var NameAndSignature = Widget.extend({
|
|||||||
*/
|
*/
|
||||||
willStart: function () {
|
willStart: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
return Promise.all([
|
||||||
return $.when(
|
|
||||||
this._super.apply(this, arguments),
|
this._super.apply(this, arguments),
|
||||||
this._rpc({route: '/web/sign/get_fonts/' + self.defaultFont}).then(function (data) {
|
this._rpc({route: '/web/sign/get_fonts/' + self.defaultFont}).then(function (data) {
|
||||||
self.fonts = data;
|
self.fonts = data;
|
||||||
})
|
})
|
||||||
);
|
]);
|
||||||
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Finds the DOM elements, initializes the signature area,
|
* Finds the DOM elements, initializes the signature area,
|
||||||
@@ -217,7 +215,7 @@ var NameAndSignature = Widget.extend({
|
|||||||
resetSignature: function () {
|
resetSignature: function () {
|
||||||
if (!this.$signatureField) {
|
if (!this.$signatureField) {
|
||||||
// no action if called before start
|
// no action if called before start
|
||||||
return $.Deferred().reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
// recompute size based on the current width
|
// recompute size based on the current width
|
||||||
this.$signatureField.css({width: 'unset'});
|
this.$signatureField.css({width: 'unset'});
|
||||||
@@ -248,7 +246,7 @@ var NameAndSignature = Widget.extend({
|
|||||||
|
|
||||||
this.focusName();
|
this.focusName();
|
||||||
|
|
||||||
return $.Deferred().resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Changes the signature mode. Toggles the display of the relevant
|
* Changes the signature mode. Toggles the display of the relevant
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ var PieChart = Widget.extend({
|
|||||||
self.controller = controller;
|
self.controller = controller;
|
||||||
return self.controller.appendTo(document.createDocumentFragment());
|
return self.controller.appendTo(document.createDocumentFragment());
|
||||||
});
|
});
|
||||||
return $.when(def1, def2);
|
return Promise.all([def1, def2]);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ var SwitchCompanyMenu = Widget.extend({
|
|||||||
this.isMobile = config.device.isMobile;
|
this.isMobile = config.device.isMobile;
|
||||||
this._onClick = _.debounce(this._onClick, 1500, true);
|
this._onClick = _.debounce(this._onClick, 1500, true);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
willStart: function () {
|
|
||||||
return session.user_companies ? this._super() : $.Deferred().reject();
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -81,9 +75,9 @@ var SwitchCompanyMenu = Widget.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (session.user_companies) {
|
||||||
SystrayMenu.Items.push(SwitchCompanyMenu);
|
SystrayMenu.Items.push(SwitchCompanyMenu);
|
||||||
|
}
|
||||||
return SwitchCompanyMenu;
|
return SwitchCompanyMenu;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,23 +53,23 @@ QUnit.module('ActionManager', {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
}, function () {
|
}, function () {
|
||||||
QUnit.test('uses a mobile-friendly view by default (if possible)', function (assert) {
|
QUnit.test('uses a mobile-friendly view by default (if possible)', async function (assert) {
|
||||||
assert.expect(4);
|
assert.expect(4);
|
||||||
|
|
||||||
var actionManager = createActionManager({
|
var actionManager = await createActionManager({
|
||||||
actions: this.actions,
|
actions: this.actions,
|
||||||
archs: this.archs,
|
archs: this.archs,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
// should default on a mobile-friendly view (kanban) for action 1
|
// should default on a mobile-friendly view (kanban) for action 1
|
||||||
actionManager.doAction(1);
|
await actionManager.doAction(1);
|
||||||
|
|
||||||
assert.containsNone(actionManager, '.o_list_view');
|
assert.containsNone(actionManager, '.o_list_view');
|
||||||
assert.containsOnce(actionManager, '.o_kanban_view');
|
assert.containsOnce(actionManager, '.o_kanban_view');
|
||||||
|
|
||||||
// there is no mobile-friendly view for action 2, should use the first one (list)
|
// there is no mobile-friendly view for action 2, should use the first one (list)
|
||||||
actionManager.doAction(2);
|
await actionManager.doAction(2);
|
||||||
|
|
||||||
assert.containsOnce(actionManager, '.o_list_view');
|
assert.containsOnce(actionManager, '.o_list_view');
|
||||||
assert.containsNone(actionManager, '.o_kanban_view');
|
assert.containsNone(actionManager, '.o_kanban_view');
|
||||||
@@ -77,10 +77,10 @@ QUnit.module('ActionManager', {
|
|||||||
actionManager.destroy();
|
actionManager.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test('lazy load mobile-friendly view', function (assert) {
|
QUnit.test('lazy load mobile-friendly view', async function (assert) {
|
||||||
assert.expect(11);
|
assert.expect(11);
|
||||||
|
|
||||||
var actionManager = createActionManager({
|
var actionManager = await createActionManager({
|
||||||
actions: this.actions,
|
actions: this.actions,
|
||||||
archs: this.archs,
|
archs: this.archs,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
@@ -89,7 +89,7 @@ QUnit.module('ActionManager', {
|
|||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
actionManager.loadState({
|
await actionManager.loadState({
|
||||||
action: 1,
|
action: 1,
|
||||||
view_type: 'form',
|
view_type: 'form',
|
||||||
});
|
});
|
||||||
@@ -99,7 +99,7 @@ QUnit.module('ActionManager', {
|
|||||||
assert.containsOnce(actionManager, '.o_form_view');
|
assert.containsOnce(actionManager, '.o_form_view');
|
||||||
|
|
||||||
// go back to lazy loaded view
|
// go back to lazy loaded view
|
||||||
testUtils.dom.click(actionManager.$('.o_control_panel .breadcrumb .breadcrumb-item:first'));
|
await testUtils.dom.click(actionManager.$('.o_control_panel .breadcrumb .breadcrumb-item:first'));
|
||||||
assert.containsNone(actionManager, '.o_form_view');
|
assert.containsNone(actionManager, '.o_form_view');
|
||||||
assert.containsNone(actionManager, '.o_list_view');
|
assert.containsNone(actionManager, '.o_list_view');
|
||||||
assert.containsOnce(actionManager, '.o_kanban_view');
|
assert.containsOnce(actionManager, '.o_kanban_view');
|
||||||
@@ -114,16 +114,16 @@ QUnit.module('ActionManager', {
|
|||||||
actionManager.destroy();
|
actionManager.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test('view switcher button should be displayed in dropdown on mobile screens', function (assert) {
|
QUnit.test('view switcher button should be displayed in dropdown on mobile screens', async function (assert) {
|
||||||
assert.expect(3);
|
assert.expect(3);
|
||||||
|
|
||||||
var actionManager = createActionManager({
|
var actionManager = await createActionManager({
|
||||||
actions: this.actions,
|
actions: this.actions,
|
||||||
archs: this.archs,
|
archs: this.archs,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
actionManager.doAction(1);
|
await actionManager.doAction(1);
|
||||||
|
|
||||||
assert.containsOnce(actionManager.$('.o_control_panel'), '.o_cp_switch_buttons button[data-toggle="dropdown"]');
|
assert.containsOnce(actionManager.$('.o_control_panel'), '.o_cp_switch_buttons button[data-toggle="dropdown"]');
|
||||||
assert.hasClass(actionManager.$('.o_cp_switch_buttons .o_cp_switch_kanban'), 'active');
|
assert.hasClass(actionManager.$('.o_cp_switch_buttons .o_cp_switch_kanban'), 'active');
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,14 @@ var testUtils = require('web.test_utils');
|
|||||||
var Widget = require('web.Widget');
|
var Widget = require('web.Widget');
|
||||||
|
|
||||||
QUnit.module('KeyboardNavigationMixin', function () {
|
QUnit.module('KeyboardNavigationMixin', function () {
|
||||||
QUnit.test('aria-keyshortcuts is added on elements with accesskey', function (assert) {
|
QUnit.test('aria-keyshortcuts is added on elements with accesskey', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
var $target = $('#qunit-fixture');
|
var $target = $('#qunit-fixture');
|
||||||
var KeyboardWidget = Widget.extend(KeyboardNavigationMixin, {
|
var KeyboardWidget = Widget.extend(KeyboardNavigationMixin, {
|
||||||
|
init: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
KeyboardNavigationMixin.init.call(this);
|
||||||
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
var $button = $('<button>').text('Click Me!').attr('accesskey', 'o');
|
var $button = $('<button>').text('Click Me!').attr('accesskey', 'o');
|
||||||
// we need to define the accesskey because it will not be assigned on invisible buttons
|
// we need to define the accesskey because it will not be assigned on invisible buttons
|
||||||
@@ -19,7 +23,7 @@ QUnit.module('KeyboardNavigationMixin', function () {
|
|||||||
});
|
});
|
||||||
var parent = testUtils.createParent({});
|
var parent = testUtils.createParent({});
|
||||||
var w = new KeyboardWidget(parent);
|
var w = new KeyboardWidget(parent);
|
||||||
w.appendTo($target);
|
await w.appendTo($target);
|
||||||
|
|
||||||
// minimum set of attribute to generate a native event that works with the mixin
|
// minimum set of attribute to generate a native event that works with the mixin
|
||||||
var e = new Event("keydown");
|
var e = new Event("keydown");
|
||||||
@@ -32,7 +36,7 @@ QUnit.module('KeyboardNavigationMixin', function () {
|
|||||||
parent.destroy();
|
parent.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test('keep CSS position absolute for parent of overlay', function (assert) {
|
QUnit.test('keep CSS position absolute for parent of overlay', async function (assert) {
|
||||||
// If we change the CSS position of an 'absolute' element to 'relative',
|
// If we change the CSS position of an 'absolute' element to 'relative',
|
||||||
// we may likely change its position on the document. Since the overlay
|
// we may likely change its position on the document. Since the overlay
|
||||||
// CSS position is 'absolute', it will match the size and cover the
|
// CSS position is 'absolute', it will match the size and cover the
|
||||||
@@ -42,6 +46,10 @@ QUnit.module('KeyboardNavigationMixin', function () {
|
|||||||
var $target = $('#qunit-fixture');
|
var $target = $('#qunit-fixture');
|
||||||
var $button;
|
var $button;
|
||||||
var KeyboardWidget = Widget.extend(KeyboardNavigationMixin, {
|
var KeyboardWidget = Widget.extend(KeyboardNavigationMixin, {
|
||||||
|
init: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
KeyboardNavigationMixin.init.call(this);
|
||||||
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
$button = $('<button>').text('Click Me!').attr('accesskey', 'o');
|
$button = $('<button>').text('Click Me!').attr('accesskey', 'o');
|
||||||
// we need to define the accesskey because it will not be assigned on invisible buttons
|
// we need to define the accesskey because it will not be assigned on invisible buttons
|
||||||
@@ -51,7 +59,7 @@ QUnit.module('KeyboardNavigationMixin', function () {
|
|||||||
});
|
});
|
||||||
var parent = testUtils.createParent({});
|
var parent = testUtils.createParent({});
|
||||||
var w = new KeyboardWidget(parent);
|
var w = new KeyboardWidget(parent);
|
||||||
w.appendTo($target);
|
await w.appendTo($target);
|
||||||
|
|
||||||
$button.css('position', 'absolute');
|
$button.css('position', 'absolute');
|
||||||
|
|
||||||
|
|||||||
@@ -9,40 +9,41 @@ QUnit.module('chrome', {}, function () {
|
|||||||
|
|
||||||
QUnit.module('Pager');
|
QUnit.module('Pager');
|
||||||
|
|
||||||
QUnit.test('basic stuff', function (assert) {
|
QUnit.test('basic stuff', async function (assert) {
|
||||||
assert.expect(2);
|
assert.expect(2);
|
||||||
|
|
||||||
var pager = new Pager(null, 10, 1, 4);
|
var pager = new Pager(null, 10, 1, 4);
|
||||||
pager.appendTo($('#qunit-fixture'));
|
pager.appendTo($('#qunit-fixture'));
|
||||||
|
await testUtils.nextTick();
|
||||||
|
|
||||||
assert.strictEqual(pager.state.current_min, 1,
|
assert.strictEqual(pager.state.current_min, 1,
|
||||||
"current_min should be set to 1");
|
"current_min should be set to 1");
|
||||||
|
|
||||||
// click on next
|
// click on next
|
||||||
testUtils.dom.click(pager.$('.o_pager_next'));
|
await testUtils.dom.click(pager.$('.o_pager_next'));
|
||||||
assert.strictEqual(pager.state.current_min, 5,
|
assert.strictEqual(pager.state.current_min, 5,
|
||||||
"current_min should now be 5");
|
"current_min should now be 5");
|
||||||
|
|
||||||
pager.destroy();
|
pager.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test('edit the pager', function (assert) {
|
QUnit.test('edit the pager', async function (assert) {
|
||||||
assert.expect(5);
|
assert.expect(5);
|
||||||
|
|
||||||
var pager = new Pager(null, 10, 1, 4);
|
var pager = new Pager(null, 10, 1, 4);
|
||||||
pager.appendTo($('#qunit-fixture'));
|
pager.appendTo($('#qunit-fixture'));
|
||||||
|
await testUtils.nextTick();
|
||||||
|
|
||||||
// enter edition
|
// enter edition
|
||||||
testUtils.dom.click(pager.$('.o_pager_value'));
|
await testUtils.dom.click(pager.$('.o_pager_value'));
|
||||||
assert.containsOnce(pager, 'input',
|
assert.containsOnce(pager, 'input',
|
||||||
"the pager should contain an input");
|
"the pager should contain an input");
|
||||||
assert.strictEqual(pager.$('input').val(), '1-4',
|
assert.strictEqual(pager.$('input').val(), '1-4',
|
||||||
"the input should have correct value");
|
"the input should have correct value");
|
||||||
|
|
||||||
// change the limit
|
// change the limit
|
||||||
pager.$('input')
|
await testUtils.fields.editInput(pager.$('input'), '1-6');
|
||||||
.val('1-6')
|
await testUtils.fields.triggerKeydown(pager.$('input'), 'enter');
|
||||||
.trigger($.Event('keydown', {which: $.ui.keyCode.ENTER})); // trigger the change
|
|
||||||
assert.strictEqual(pager.state.limit, 6,
|
assert.strictEqual(pager.state.limit, 6,
|
||||||
"the limit should have been updated");
|
"the limit should have been updated");
|
||||||
assert.strictEqual(pager.state.current_min, 1,
|
assert.strictEqual(pager.state.current_min, 1,
|
||||||
@@ -53,42 +54,39 @@ QUnit.module('chrome', {}, function () {
|
|||||||
pager.destroy();
|
pager.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test('disabling the pager', function (assert) {
|
QUnit.test('disabling the pager', async function (assert) {
|
||||||
var done = assert.async();
|
|
||||||
assert.expect(4);
|
assert.expect(4);
|
||||||
|
|
||||||
var pager = new Pager(null, 10, 1, 4);
|
var pager = new Pager(null, 10, 1, 4);
|
||||||
pager.appendTo($('#qunit-fixture'));
|
pager.appendTo($('#qunit-fixture'));
|
||||||
|
await testUtils.nextTick();
|
||||||
pager.disable();
|
await pager.disable();
|
||||||
|
|
||||||
// try to go to the next or previous pages
|
// try to go to the next or previous pages
|
||||||
testUtils.dom.click(pager.$('.o_pager_next'));
|
await testUtils.dom.click(pager.$('.o_pager_next'));
|
||||||
assert.strictEqual(pager.state.current_min, 1,
|
assert.strictEqual(pager.state.current_min, 1,
|
||||||
"current_min should still be 1");
|
"current_min should still be 1");
|
||||||
testUtils.dom.click(pager.$('.o_pager_previous'));
|
await testUtils.dom.click(pager.$('.o_pager_previous'));
|
||||||
assert.strictEqual(pager.state.current_min, 1,
|
assert.strictEqual(pager.state.current_min, 1,
|
||||||
"current_min should still be 1");
|
"current_min should still be 1");
|
||||||
|
|
||||||
// try to change the limit
|
// try to change the limit
|
||||||
testUtils.dom.click(pager.$('.o_pager_value'));
|
await testUtils.dom.click(pager.$('.o_pager_value'));
|
||||||
assert.containsNone(pager, 'input',
|
assert.containsNone(pager, 'input',
|
||||||
"the pager should not contain an input");
|
"the pager should not contain an input");
|
||||||
|
|
||||||
// a common use is to disable the pager before reloading the data, and
|
// a common use is to disable the pager before reloading the data, and
|
||||||
// re-enable it once they have been loaded
|
// re-enable it once they have been loaded
|
||||||
// the following imulates this situation
|
// the following emulates this situation
|
||||||
pager.on('pager_changed', null, function () {
|
pager.on('pager_changed', null, function () {
|
||||||
pager.disable();
|
pager.disable();
|
||||||
concurrency.delay(0).then(function () {
|
concurrency.delay(0).then(function () {
|
||||||
assert.ok(pager.disabled, "pager should still be disabled");
|
assert.ok(pager.disabled, "pager should still be disabled");
|
||||||
pager.destroy();
|
pager.destroy();
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
pager.enable();
|
pager.enable();
|
||||||
testUtils.dom.click(pager.$('.o_pager_next'));
|
await testUtils.dom.click(pager.$('.o_pager_next'));
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user