diff options
Diffstat (limited to 'addons/web/static/src/js/framework/view.js')
| -rw-r--r-- | addons/web/static/src/js/framework/view.js | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/addons/web/static/src/js/framework/view.js b/addons/web/static/src/js/framework/view.js new file mode 100644 index 00000000000..c315e53d710 --- /dev/null +++ b/addons/web/static/src/js/framework/view.js @@ -0,0 +1,239 @@ +odoo.define('web.View', ['web.core', 'web.data', 'web.pyeval', 'web.Widget'], function (require) { +"use strict"; + +var core = require('web.core'); +var data = require('web.data'); +var pyeval = require('web.pyeval'); +var Widget = require('web.Widget'); + +var View = Widget.extend({ + // name displayed in view switchers + display_name: '', + /** + * Define a view type for each view to allow automatic call to fields_view_get. + */ + view_type: undefined, + init: function(parent, dataset, view_id, options) { + this._super(parent); + this.ViewManager = parent; + this.dataset = dataset; + this.view_id = view_id; + this.$buttons = options && options.$buttons; + this.set_default_options(options); + }, + start: function () { + return this.load_view(); + }, + load_view: function() { + var self = this; + var view_loaded_def; + if (this.embedded_view) { + view_loaded_def = $.Deferred(); + $.async_when().done(function() { + view_loaded_def.resolve(self.embedded_view); + }); + } else { + if (! this.view_type) + console.warn("view_type is not defined", this); + view_loaded_def = this.dataset._model.fields_view_get({ + "view_id": this.view_id, + "view_type": this.view_type, + "toolbar": !!this.options.$sidebar, + "context": this.dataset.get_context(), + }); + } + return this.alive(view_loaded_def).then(function(r) { + self.fields_view = r; + // add css classes that reflect the (absence of) access rights + self.$el.addClass('oe_view') + .toggleClass('oe_cannot_create', !self.is_action_enabled('create')) + .toggleClass('oe_cannot_edit', !self.is_action_enabled('edit')) + .toggleClass('oe_cannot_delete', !self.is_action_enabled('delete')); + return $.when(self.view_loading(r)).then(function() { + self.trigger('view_loaded', r); + }); + }); + }, + view_loading: function(r) { + }, + set_default_options: function(options) { + this.options = options || {}; + _.defaults(this.options, { + // All possible views options should be defaulted here + $sidebar: null, + sidebar_id: null, + action: null, + action_views_ids: {} + }); + }, + /** + * Fetches and executes the action identified by ``action_data``. + * + * @param {Object} action_data the action descriptor data + * @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it + * @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``) + * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'`` + * @param {Object} [action_data.context=null] additional action context, to add to the current context + * @param {instance.web.DataSet} dataset a dataset object used to communicate with the server + * @param {Object} [record_id] the identifier of the object on which the action is to be applied + * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action) + */ + do_execute_action: function (action_data, dataset, record_id, on_closed) { + var self = this; + var result_handler = function () { + if (on_closed) { on_closed.apply(null, arguments); } + if (self.getParent() && self.getParent().on_action_executed) { + return self.getParent().on_action_executed.apply(null, arguments); + } + }; + var context = new data.CompoundContext(dataset.get_context(), action_data.context || {}); + + // response handler + var handler = function (action) { + if (action && action.constructor == Object) { + // filter out context keys that are specific to the current action. + // Wrong default_* and search_default_* values will no give the expected result + // Wrong group_by values will simply fail and forbid rendering of the destination view + var ncontext = new data.CompoundContext( + _.object(_.reject(_.pairs(dataset.get_context().eval()), function(pair) { + return pair[0].match('^(?:(?:default_|search_default_).+|.+_view_ref|group_by|group_by_no_leaf|active_id|active_ids)$') !== null; + })) + ); + ncontext.add(action_data.context || {}); + ncontext.add({active_model: dataset.model}); + if (record_id) { + ncontext.add({ + active_id: record_id, + active_ids: [record_id], + }); + } + ncontext.add(action.context || {}); + action.context = ncontext; + return self.do_action(action, { + on_close: result_handler, + }); + } else { + self.do_action({"type":"ir.actions.act_window_close"}); + return result_handler(); + } + }; + + if (action_data.special === 'cancel') { + return handler({"type":"ir.actions.act_window_close"}); + } else if (action_data.type=="object") { + var args = [[record_id]]; + if (action_data.args) { + try { + // Warning: quotes and double quotes problem due to json and xml clash + // Maybe we should force escaping in xml or do a better parse of the args array + var additional_args = JSON.parse(action_data.args.replace(/'/g, '"')); + args = args.concat(additional_args); + } catch(e) { + console.error("Could not JSON.parse arguments", action_data.args); + } + } + args.push(context); + return dataset.call_button(action_data.name, args).then(handler).then(function () { + 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}), + do_not_eval: true + }).then(handler); + } else { + return dataset.exec_workflow(record_id, action_data.name).then(handler); + } + }, + /** + * Directly set a view to use instead of calling fields_view_get. This method must + * be called before start(). When an embedded view is set, underlying implementations + * of instance.web.View must use the provided view instead of any other one. + * + * @param embedded_view A view. + */ + set_embedded_view: function(embedded_view) { + this.embedded_view = embedded_view; + }, + do_show: function () { + var self = this; + if (this.$buttons) { + this.$buttons.show(); + } + this.$el.show(); + setTimeout(function () { + self.$el.parent().addClass('in'); + }, 0); + + core.bus.trigger('view_shown', this); + }, + do_hide: function () { + if (this.$buttons) { + this.$buttons.hide(); + } + this.$el.parent().removeClass('in'); + this.$el.hide(); + }, + is_active: function () { + return this.ViewManager.active_view.controller === this; + }, /** + * Wraps fn to only call it if the current view is the active one. If the + * current view is not active, doesn't call fn. + * + * fn can not return anything, as a non-call to fn can't return anything + * either + * + * @param {Function} fn function to wrap in the active guard + */ + guard_active: function (fn) { + var self = this; + return function () { + if (self.is_active()) { + fn.apply(self, arguments); + } + }; + }, + do_push_state: function(state) { + if (this.getParent() && this.getParent().do_push_state) { + this.getParent().do_push_state(state); + } + }, + do_load_state: function (state, warm) { + + }, + /** + * Switches to a specific view type + */ + do_switch_view: function() { + this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments))); + }, + do_search: function(domain, context, group_by) { + }, + sidebar_eval_context: function () { + return $.when({}); + }, + /** + * Asks the view to reload itself, if the reloading is asynchronous should + * return a {$.Deferred} indicating when the reloading is done. + */ + reload: function () { + return $.when(); + }, + /** + * Return whether the user can perform the action ('create', 'edit', 'delete') in this view. + * An action is disabled by setting the corresponding attribute in the view's main element, + * like: <form string="" create="false" edit="false" delete="false"> + */ + is_action_enabled: function(action) { + var attrs = this.fields_view.arch.attrs; + return (action in attrs) ? JSON.parse(attrs[action]) : true; + }, + get_context: function () { + return {}; + }, +}); + +return View; + +}); |
