odoo.define('web.ViewManager', function (require) { "use strict"; var ControlPanelMixin = require('web.ControlPanelMixin'); var core = require('web.core'); var data = require('web.data'); var framework = require('web.framework'); var Model = require('web.DataModel'); var pyeval = require('web.pyeval'); var SearchView = require('web.SearchView'); var Widget = require('web.Widget'); var QWeb = core.qweb; var _t = core._t; var ViewManager = Widget.extend(ControlPanelMixin, { template: "ViewManager", /** * @param {Object} [dataset] null object (... historical reasons) * @param {Array} [views] List of [view_id, view_type] * @param {Object} [flags] various boolean describing UI state */ init: function(parent, dataset, views, flags, action, options) { if (action) { flags = action.flags || {}; if (!('auto_search' in flags)) { flags.auto_search = action.auto_search !== false; } this.action = action; this.action_manager = parent; dataset = new data.DataSetSearch(this, action.res_model, action.context, action.domain); if (action.res_id) { dataset.ids.push(action.res_id); dataset.index = 0; } views = action.views; } var self = this; this._super(parent); this.flags = flags || {}; this.dataset = dataset; this.view_order = []; this.views = {}; this.view_stack = []; // used for breadcrumbs this.active_view = null; this.registry = core.view_registry; this.title = this.action && this.action.name; this.is_in_DOM = false; // used to know if the view manager is attached in the DOM _.each(views, function (view) { var view_type = view[1] || view.view_type; var View = core.view_registry.get(view_type, true); var view_label = View ? View.prototype.display_name: (void 'nope'); var view_descr = { controller: null, options: view.options || {}, view_id: view[0] || view.view_id, type: view_type, label: view_label, embedded_view: view.embedded_view, title: self.title, button_label: View ? _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}) : (void 'nope'), multi_record: View ? View.prototype.multi_record : undefined, accesskey: View ? View.prototype.accesskey : undefined, }; self.view_order.push(view_descr); self.views[view_type] = view_descr; }); if (options && options.state && options.state.view_type) { var view_type = options.state.view_type; var view_descr = this.views[view_type]; this.default_view = view_descr && view_descr.multi_record ? view_type : undefined; } // Listen to event 'switch_view' indicating that the VM must now display view wiew_type this.on('switch_view', this, function(view_type) { if (view_type === 'form' && this.active_view && this.active_view.type === 'form') { this._display_view(view_type); } else { this.switch_mode(view_type); } }); }, /** * @returns {jQuery.Deferred} initial view loading promise */ start: function() { var self = this; var default_view = this.get_default_view(); var default_options = this.flags[default_view] && this.flags[default_view].options; this._super(); var views_ids = {}; _.each(this.views, function (view) { views_ids[view.type] = view.view_id; view.options = _.extend({ action : self.action, action_views_ids : views_ids, }, self.flags, self.flags[view.type], view.options); view.$container = self.$(".oe-view-manager-view-" + view.type); }); this.$el.addClass("oe_view_manager_" + ((this.action && this.action.target) || 'current')); this.control_elements = {}; if (this.flags.search_view) { this.search_view_loaded = this.setup_search_view(); } if (this.flags.views_switcher) { this.render_switch_buttons(); } // Switch to the default_view to load it var main_view_loaded = this.switch_mode(default_view, null, default_options); return $.when(main_view_loaded, this.search_view_loaded); }, get_default_view: function() { return this.default_view || this.flags.default_view || this.view_order[0].type; }, switch_mode: function(view_type, no_store, view_options) { var self = this; var view = this.views[view_type]; var old_view = this.active_view; var switched = $.Deferred(); if (!view) { return $.Deferred().reject(); } if (view.multi_record) { this.view_stack = []; } else if (this.view_stack.length > 0 && !(_.last(this.view_stack).multi_record)) { // Replace the last view by the new one if both are mono_record this.view_stack.pop(); } this.view_stack.push(view); // Hide active view (at first rendering, there is no view to hide) if (this.active_view && this.active_view !== view) { if (this.active_view.controller) this.active_view.controller.do_hide(); if (this.active_view.$container) this.active_view.$container.hide(); } this.active_view = view; if (!view.created) { view.created = this.create_view.bind(this)(view, view_options); } // Call do_search on the searchview to compute domains, contexts and groupbys if (this.search_view_loaded && this.flags.auto_search && view.controller.searchable !== false) { this.active_search = $.Deferred(); $.when(this.search_view_loaded, view.created).done(function() { self.searchview.do_search(); }); } $.when(view.created, this.active_search).done(function () { self._display_view(view_options, old_view).done(function() { self.trigger('switch_mode', view_type, no_store, view_options); switched.resolve(); }); }); return switched; }, _display_view: function (view_options, old_view) { var self = this; var view_controller = this.active_view.controller; var view_fragment = this.active_view.$fragment; var view_control_elements = this.render_view_control_elements(); // Show the view this.active_view.$container.show(); return $.when(view_controller.do_show(view_options)).done(function () { // Prepare the ControlPanel content and update it var cp_status = { active_view_selector: '.oe-cp-switch-' + self.active_view.type, breadcrumbs: self.action_manager && self.action_manager.get_breadcrumbs(), cp_content: _.extend({}, self.control_elements, view_control_elements), hidden: self.flags.headless, searchview: self.searchview, search_view_hidden: view_controller.searchable === false, }; self.update_control_panel(cp_status); // Detach the old view but not ui-autocomplete elements to let // jquery-ui garbage-collect them if (old_view) { old_view.$container.contents().not('.ui-autocomplete').detach(); } // Append the view fragment to its $container framework.append(self.active_view.$container, view_fragment, self.is_in_DOM); }); }, create_view: function(view, view_options) { var self = this; var View = this.registry.get(view.type); var options = _.clone(view.options); var view_loaded = $.Deferred(); if (view.type === "form" && ((this.action && (this.action.target === 'new' || this.action.target === 'inline')) || (view_options && view_options.mode === 'edit'))) { options.initial_mode = options.initial_mode || 'edit'; } var controller = new View(this, this.dataset, view.view_id, options); view.controller = controller; view.$fragment = $('