From 40dd121938552d3f88289d2e5fcd1d57a0dd01bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Thu, 6 Dec 2018 12:39:06 +0000 Subject: [REF] web: move ControlPanel inside controllers/actions This branch introduces a large-scale reorganization of the component tree generated by the web client. The short version is that now, the control panel is a child of the view controller and no longer a sibling. Graphically (and simplified), we go from this: webClient / | \ ... ... actionManager / \ controlPanel viewController / \ to this: webClient / | \ ... ... actionManager | viewController / | \ ControlPanelController / \ CPRenderer CPModel The motivation is that this work moves the code where it should be. Before this commit, it was kind of weird to have code in the controllers to render buttons outside of their root node (in renderButtons). Also, the action manager had to take care of coordinating search view states between view transitions. So, this work simplifies the code. It also makes it easier to extend. We see day after day that Odoo needs to take care of more complex UI needs, and in many cases, these needs were quite difficult to implement (we prefer spaghettis in our plates, not in our code). The changes in this branch should open the way to implement these features. For example, - it will now be easy to add the possibility of views (for example, the search view or a new ControlPanel view) to add custom buttons (of type action or object) in the control panel. - Another need will be to serialize/ restore the state of the search view across action boundaries (needed by the dashboard). - Another example is the possibility for views to customize easily the presence/absence of sub menus (filters/groupbys/favorites/ time range/...) - Another need is an easier way for views/client action to customize their control panel. This commit also contains a large rewrite of the search view (so it is more inline with our architecture and easier to maintain). Part of task 1893568 Co-authored-by: Aaron Bohy Co-authored-by: Mathieu Duckerts-Antoine Co-authored-by: Martin Geubelle --- addons/web/static/src/js/views/view_dialogs.js | 169 +++++++------------------ 1 file changed, 48 insertions(+), 121 deletions(-) (limited to 'addons/web/static/src/js/views/view_dialogs.js') diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index ee11afbc3bd..fb9cd5afa0c 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -3,13 +3,10 @@ odoo.define('web.view_dialogs', function (require) { var config = require('web.config'); var core = require('web.core'); -var data = require('web.data'); var Dialog = require('web.Dialog'); var dom = require('web.dom'); var ListController = require('web.ListController'); var ListView = require('web.ListView'); -var pyUtils = require('web.py_utils'); -var SearchView = require('web.SearchView'); var view_registry = require('web.view_registry'); var _t = core._t; @@ -21,9 +18,6 @@ var _t = core._t; var ViewDialog = Dialog.extend({ custom_events: _.extend({}, Dialog.prototype.custom_events, { push_state: '_onPushState', - env_updated: function (event) { - event.stopPropagation(); - }, }), /** * @constructor @@ -45,10 +39,6 @@ var ViewDialog = Dialog.extend({ this.domain = options.domain || []; this.context = options.context || {}; this.options = _.extend(this.options || {}, options || {}); - - // FIXME: remove this once a dataset won't be necessary anymore to interact - // with data_manager and instantiate views - this.dataset = new data.DataSet(this, this.res_model, this.context); }, //-------------------------------------------------------------------------- @@ -182,7 +172,7 @@ var FormViewDialog = ViewDialog.extend({ if (this.options.fields_view) { fields_view_def = $.when(this.options.fields_view); } else { - fields_view_def = this.loadFieldView(this.dataset, this.options.view_id, 'form'); + fields_view_def = this.loadFieldView(this.res_model, this.context, this.options.view_id, 'form'); } fields_view_def.then(function (viewInfo) { @@ -271,17 +261,23 @@ var FormViewDialog = ViewDialog.extend({ }); var SelectCreateListController = ListController.extend({ - // Override the ListView to handle the custom events 'open_record' (triggered when clicking on a - // row of the list) such that it triggers up 'select_record' with its res_id. - custom_events: _.extend({}, ListController.prototype.custom_events, { - open_record: function (event) { - var selectedRecord = this.model.get(event.data.id); - this.trigger_up('select_record', { - id: selectedRecord.res_id, - display_name: selectedRecord.data.display_name, - }); - }, - }), + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Override to select the clicked record instead of opening it + * + * @override + * @private + */ + _onOpenRecord: function (ev) { + var selectedRecord = this.model.get(ev.data.id); + this.trigger_up('select_record', { + id: selectedRecord.res_id, + display_name: selectedRecord.data.display_name, + }); + }, }); /** @@ -299,13 +295,6 @@ var SelectCreateDialog = ViewDialog.extend({ event.stopPropagation(); this.$footer.find(".o_select_button").prop('disabled', !event.data.selection.length); }, - search: function (event) { - event.stopPropagation(); // prevent this event from bubbling up to the action manager - var d = event.data; - var searchData = this._process_search_data(d.domains, d.contexts, d.groupbys); - this.list_controller.reload(searchData); - }, - get_controller_context: '_onGetControllerContext', }), /** @@ -320,7 +309,7 @@ var SelectCreateDialog = ViewDialog.extend({ this._super.apply(this, arguments); _.defaults(this.options, { initial_view: 'search' }); this.on_selected = this.options.on_selected || (function () {}); - this.initial_ids = this.options.initial_ids; + this.initialIDs = this.options.initial_ids; }, open: function () { @@ -328,26 +317,13 @@ var SelectCreateDialog = ViewDialog.extend({ return this.create_edit_record(); } var self = this; - var user_context = this.getSession().user_context; - var _super = this._super.bind(this); - var context = pyUtils.eval_domains_and_contexts({ - domains: [], - contexts: [user_context, this.context] - }).context; - var search_defaults = {}; - _.each(context, function (value_, key) { - var match = /^search_default_(.*)$/.exec(key); - if (match) { - search_defaults[match[1]] = value_; - } - }); - this.loadViews(this.dataset.model, this.dataset.get_context().eval(), [[false, 'list'], [false, 'search']], {}) - .then(this.setup.bind(this, search_defaults)) + this.loadViews(this.res_model, this.context, [[false, 'list'], [false, 'search']], {}) + .then(this.setup.bind(this)) .then(function (fragment) { self.opened().then(function () { dom.append(self.$el, fragment, { - callbacks: [{widget: self.list_controller}], + callbacks: [{widget: self.listController}], in_DOM: true, }); self.set_buttons(self.__buttons); @@ -357,65 +333,50 @@ var SelectCreateDialog = ViewDialog.extend({ return this; }, - setup: function (search_defaults, fields_views) { + setup: function (fieldsViews) { var self = this; var fragment = document.createDocumentFragment(); - var searchDef = $.Deferred(); - - // Set the dialog's header and its search view - var $header = $('
').addClass('o_modal_header').appendTo(fragment); - var $pager = $('
').addClass('o_pager').appendTo($header); - var options = { - $buttons: $('
').addClass('o_search_options').appendTo($header), - search_defaults: search_defaults, - }; - var searchview = new SearchView(this, this.dataset, fields_views.search, options); - searchview.prependTo($header).done(function () { - var d = searchview.build_search_data(); - if (self.initial_ids) { - d.domains.push([["id", "in", self.initial_ids]]); - self.initial_ids = undefined; - } - var searchData = self._process_search_data(d.domains, d.contexts, d.groupbys); - searchDef.resolve(searchData); - }); - - return $.when(searchDef).then(function (searchResult) { - // Set the list view - var listView = new ListView(fields_views.list, _.extend({ - context: searchResult.context, - domain: searchResult.domain, - groupBy: searchResult.groupBy, - modelName: self.dataset.model, - hasSelectors: !self.options.disable_multiple_selection, - readonly: true, - }, self.options.list_view_options)); - listView.setController(SelectCreateListController); - return listView.getController(self); - }).then(function (controller) { - self.list_controller = controller; - // Set the dialog's buttons + var domain = this.domain; + if (this.initialIDs) { + domain = domain.concat([['id', 'in', this.initialIDs]]); + } + var listView = new ListView(fieldsViews.list, _.extend({ + action: { + controlPanelFieldsView: fieldsViews.search, + }, + action_buttons: false, + context: this.context, + domain: domain, + hasSelectors: !this.options.disable_multiple_selection, + modelName: this.res_model, + readonly: true, + withBreadcrumbs: false, + }, this.options.list_view_options)); + listView.setController(SelectCreateListController); + return listView.getController(this).then(function (controller) { + self.listController = controller; + // render the footer buttons self.__buttons = [{ text: _t("Cancel"), - classes: "btn-secondary o_form_button_cancel", + classes: 'btn-secondary o_form_button_cancel', close: true, }]; if (!self.options.no_create) { self.__buttons.unshift({ text: _t("Create"), - classes: "btn-primary", + classes: 'btn-primary', click: self.create_edit_record.bind(self) }); } if (!self.options.disable_multiple_selection) { self.__buttons.unshift({ text: _t("Select"), - classes: "btn-primary o_select_button", + classes: 'btn-primary o_select_button', disabled: true, close: true, click: function () { - var records = self.list_controller.getSelectedRecords(); + var records = self.listController.getSelectedRecords(); var values = _.map(records, function (record) { return { id: record.res_id, @@ -426,28 +387,11 @@ var SelectCreateDialog = ViewDialog.extend({ }, }); } - return self.list_controller.appendTo(fragment); + return self.listController.appendTo(fragment); }).then(function () { - searchview.toggle_visibility(true); - self.list_controller.do_show(); - self.list_controller.renderPager($pager); return fragment; }); }, - _process_search_data: function (domains, contexts, groupbys) { - var results = pyUtils.eval_domains_and_contexts({ - domains: [this.domain].concat(domains), - contexts: [this.context].concat(contexts), - group_by_seq: groupbys || [], - eval_context: this.getSession().user_context, - }); - var context = _.omit(results.context, function (value, key) { return key.indexOf('search_default_') === 0; }); - return { - context: context, - domain: results.domain, - groupBy: results.group_by, - }; - }, create_edit_record: function () { var self = this; var dialog = new FormViewDialog(this, _.extend({}, this.options, { @@ -469,23 +413,6 @@ var SelectCreateDialog = ViewDialog.extend({ this.trigger_up('form_dialog_discarded'); return true; }, - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Handles a context request: provides to the caller the context of the - * list controller. - * - * @private - * @param {OdooEvent} ev - * @param {function} ev.data.callback used to send the requested context - */ - _onGetControllerContext: function (ev) { - ev.stopPropagation(); - var context = this.list_controller && this.list_controller.getContext(); - ev.data.callback(context || {}); - } }); return { -- cgit v1.2.3