summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/framework
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/framework')
-rw-r--r--addons/web/static/src/js/framework/data_manager.js226
-rw-r--r--addons/web/static/src/js/framework/model.js38
-rw-r--r--addons/web/static/src/js/framework/view.js10
3 files changed, 232 insertions, 42 deletions
diff --git a/addons/web/static/src/js/framework/data_manager.js b/addons/web/static/src/js/framework/data_manager.js
new file mode 100644
index 00000000000..8065b48b4e1
--- /dev/null
+++ b/addons/web/static/src/js/framework/data_manager.js
@@ -0,0 +1,226 @@
+odoo.define('web.DataManager', function (require) {
+"use strict";
+
+var core = require('web.core');
+var Model = require('web.DataModel');
+var session = require('web.session');
+var utils = require('web.utils');
+
+return core.Class.extend({
+ init: function () {
+ this.Filters = new Model('ir.filters');
+ this._cache = {
+ actions: {},
+ fields_views: {},
+ fields: {},
+ filters: {},
+ views: {},
+ };
+ },
+
+ /**
+ * Loads an action from its id or xmlid.
+ *
+ * @param {int|string} [action_id] the action id or xmlid
+ * @param {Object} [additional_context] used to load the action
+ * @return {Deferred} resolved with the action whose id or xmlid is action_id
+ */
+ load_action: function (action_id, additional_context) {
+ var self = this;
+ var key = this._gen_key(action_id, additional_context || {});
+
+ if (!this._cache.actions[key]) {
+ this._cache.actions[key] = session.rpc("/web/action/load", {
+ action_id: action_id,
+ additional_context : additional_context,
+ }).then(function (action) {
+ self._cache.actions[key] = action.no_cache ? null : self._cache.actions[key];
+ return action;
+ });
+ }
+
+ return this._cache.actions[key];
+ },
+
+ /**
+ * Loads various information concerning views: fields_view for each view,
+ * and optionally filters and fields.
+ *
+ * @param {Object} [dataset] the dataset for which the views are loaded
+ * @param {Array} [views_descr] array of [view_id, view_type]
+ * @param {Object} [options] dictionnary of various options:
+ * - options.load_filters: whether or not to load the filters,
+ * - options.action_id: the action_id (required to load filters),
+ * - options.load_fields: whether or not to load the fields,
+ * - options.toolbar: whether or not a toolbar will be displayed,
+ * @return {Deferred} resolved with the requested views information
+ */
+ load_views: function (dataset, views_descr, options) {
+ var self = this;
+ var model = dataset.model;
+ var context = dataset.get_context();
+ var key = this._gen_key(model, views_descr, options || {}, context);
+
+ if (!this._cache.views[key]) {
+ // Don't load fields or filters if already in cache
+ options.load_fields = options.load_fields && !this._cache.fields[model];
+ var filters_key;
+ if (options.load_filters) {
+ filters_key = this._gen_key(model, options.action_id);
+ options.load_filters = !this._cache.filters[filters_key];
+ }
+
+ this._cache.views[key] = dataset.call('load_views', {
+ views: views_descr,
+ options: options,
+ context: context,
+ }).then(function (result) {
+ // Postprocess fields_views and insert them into the fields_views cache
+ result.fields_views = _.mapObject(result.fields_views, self._postprocess_fvg.bind(self)); // FIXME: lazy postprocess?
+ _.each(views_descr, function (view_descr) {
+ var toolbar = options.toolbar && view_descr[1] !== 'search';
+ var fv_key = self._gen_key(model, view_descr[0], view_descr[1], toolbar, context);
+ self._cache.fields_views[fv_key] = $.when(result.fields_views[view_descr[1]]);
+ });
+
+ // Insert filters, if any, into the filters cache
+ if (result.filters) {
+ self._cache.filters[filters_key] = $.when(result.filters);
+ }
+
+ // Insert fields, if any, into the fields cache
+ if (result.fields) {
+ self._cache.fields[model] = $.when(result.fields);
+ }
+
+ return result.fields_views;
+ });
+ }
+
+ return this._cache.views[key];
+ },
+
+ /**
+ * Loads the fields_view of a given model, view_type and optional view_id.
+ *
+ * @param {Object} [dataset] the dataset for which the fields_view is loaded
+ * @param {int} [view_id] the id of the view (optional)
+ * @param {string} [view_type] the type of the view
+ * @param {Boolean} [toolbar] whether or not a toolbar will be displayed
+ * @return {Deferred} resolved with the requested fields_view
+ */
+ load_fields_view: function (dataset, view_id, view_type, toolbar) {
+ var key = this._gen_key(dataset.model, view_id, view_type, toolbar, dataset.get_context());
+ if (!this._cache.fields_views[key]) {
+ this._cache.fields_views[key] = dataset.call('fields_view_get', {
+ view_id: view_id,
+ view_type: view_type,
+ toolbar: toolbar,
+ context: dataset.get_context(),
+ }).then(this._postprocess_fvg.bind(this));
+ }
+ return this._cache.fields_views[key];
+ },
+
+ /**
+ * Loads the fields of a given model.
+ *
+ * @param {Object} [dataset] the dataset for which the fields are loaded
+ * @return {Deferred} resolved with the requested fields
+ */
+ load_fields: function (dataset) {
+ if (!this._cache.fields[dataset.model]) {
+ this._cache.fields[dataset.model] = dataset.call('fields_get', {
+ context: dataset.get_context(),
+ });
+ }
+ return this._cache.fields[dataset.model];
+ },
+
+ /**
+ * Loads the filters of a given model and optional action id.
+ *
+ * @param {Object} [dataset] the dataset for which the filters are loaded
+ * @param {int} [action_id] the id of the action (optional)
+ * @return {Deferred} resolved with the requested filters
+ */
+ load_filters: function (dataset, action_id) {
+ var key = this._gen_key(dataset.model, action_id);
+ if (!this._cache.filters[key]) {
+ this._cache.filters[key] = this.Filters.call('get_filters', [dataset.model, action_id], {
+ context: dataset.get_context(),
+ });
+ }
+ return this._cache.filters[key];
+ },
+
+ /**
+ * Calls 'create_or_replace' on 'ir_filters'.
+ *
+ * @param {Object} [filter] the filter description
+ * @return {Deferred} resolved with the id of the created or replaced filter
+ */
+ create_filter: function (filter) {
+ var self = this;
+ return this.Filters
+ .call('create_or_replace', [filter])
+ .then(function (filter_id) {
+ var key = [
+ filter.model_id,
+ filter.action_id || false,
+ ].join(',');
+ self._cache.filters[key] = null; // invalidate cache
+ return filter_id;
+ });
+ },
+
+ /**
+ * Calls 'unlink' on 'ir_filters'.
+ *
+ * @param {Object} [filter] the description of the filter to remove
+ * @return {Deferred}
+ */
+ delete_filter: function (filter) {
+ var self = this;
+ return this.Filters
+ .call('unlink', [filter.id])
+ .then(function () {
+ self._cache.filters = {}; // invalidate cache
+ });
+ },
+
+ /**
+ * Private function that postprocesses fields_view (mainly parses the arch attribute)
+ */
+ _postprocess_fvg: function (fields_view) {
+ var self = this;
+ var doc = $.parseXML(fields_view.arch).documentElement;
+ fields_view.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
+ if ('id' in fields_view.fields) {
+ // Special case for id's
+ var id_field = fields_view.fields.id;
+ id_field.original_type = id_field.type;
+ id_field.type = 'id';
+ }
+ _.each(fields_view.fields, function(field) {
+ _.each(field.views || {}, function(view) {
+ self._postprocess_fvg(view);
+ });
+ });
+ return fields_view;
+ },
+
+ /**
+ * Private function that generates a cache key from its arguments
+ */
+ _gen_key: function () {
+ return _.map(Array.prototype.slice.call(arguments), function (arg) {
+ if (!arg) {
+ return false;
+ }
+ return _.isObject(arg) ? JSON.stringify(arg) : arg;
+ }).join(',');
+ },
+});
+
+});
diff --git a/addons/web/static/src/js/framework/model.js b/addons/web/static/src/js/framework/model.js
index 8e37f14218e..fc1422d799a 100644
--- a/addons/web/static/src/js/framework/model.js
+++ b/addons/web/static/src/js/framework/model.js
@@ -3,7 +3,6 @@ odoo.define('web.Model', function (require) {
var Class = require('web.Class');
var session = require('web.session');
-var utils = require('web.utils');
var Model = Class.extend({
/**
@@ -70,43 +69,6 @@ var Model = Class.extend({
signal: signal
});
},
- /**
- * Performs a fields_view_get and apply postprocessing.
- * return a {$.Deferred} resolved with the fvg
- *
- * @param {Object} args
- * @param {String|Object} args.model instance.web.Model instance or string repr of the model
- * @param {Object} [args.context] context if args.model is a string
- * @param {Number} [args.view_id] id of the view to be loaded, default view if null
- * @param {String} [args.view_type] type of view to be loaded if view_id is null
- * @param {Boolean} [args.toolbar=false] get the toolbar definition
- */
- fields_view_get: function (options) {
- return this.call('fields_view_get', {
- view_id: options.view_id,
- view_type: options.view_type,
- context: options.context,
- toolbar: options.toolbar || false
- }).then(this.postprocess_fvg.bind(this));
- },
-
- postprocess_fvg: function (fvg) {
- var self = this;
- var doc = $.parseXML(fvg.arch).documentElement;
- fvg.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
- if ('id' in fvg.fields) {
- // Special case for id's
- var id_field = fvg.fields.id;
- id_field.original_type = id_field.type;
- id_field.type = 'id';
- }
- _.each(fvg.fields, function(field) {
- _.each(field.views || {}, function(view) {
- self.postprocess_fvg(view);
- });
- });
- return fvg;
- },
});
return Model;
diff --git a/addons/web/static/src/js/framework/view.js b/addons/web/static/src/js/framework/view.js
index b6153475b0b..fbbede916e5 100644
--- a/addons/web/static/src/js/framework/view.js
+++ b/addons/web/static/src/js/framework/view.js
@@ -3,6 +3,7 @@ odoo.define('web.View', function (require) {
var core = require('web.core');
var data = require('web.data');
+var data_manager = require('web.data_manager');
var pyeval = require('web.pyeval');
var Widget = require('web.Widget');
@@ -122,10 +123,11 @@ var View = Widget.extend({
core.bus.trigger('do_reload_needaction');
});
} else if (action_data.type=="action") {
- return this.rpc('/web/action/load', {
- action_id: action_data.name,
- context: _.extend(pyeval.eval('context', context), {'active_model': dataset.model, 'active_ids': dataset.ids, 'active_id': record_id}),
- }).then(handler);
+ return data_manager.load_action(action_data.name, _.extend(pyeval.eval('context', context), {
+ active_model: dataset.model,
+ active_ids: dataset.ids,
+ active_id: record_id
+ })).then(handler);
} else {
return dataset.exec_workflow(record_id, action_data.name).then(handler);
}