From e68d3bb09d1f844387ceb20aa76022ace48507d9 Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Wed, 11 Mar 2020 09:38:29 +0000 Subject: [PATCH] [REF] *: Adapt all modules to new control panel Adaptation of the code throughout all modules to properly instantiate the newly refactored control panel and update it accordingly. Part of task 2196029 Co-authored-by: Mathieu Duckerts-Antoine --- .../static/src/js/barcode_form_view.js | 79 +++-- .../src/js/action_manager_board_action.js | 2 +- .../board/static/src/js/add_to_board_menu.js | 315 +++++++----------- addons/board/static/src/js/board_view.js | 40 ++- addons/board/static/src/scss/dashboard.scss | 3 +- addons/board/static/src/xml/board.xml | 30 +- addons/google_drive/__manifest__.py | 1 + addons/google_drive/static/src/js/gdrive.js | 174 +++++----- addons/google_drive/static/src/xml/gdrive.xml | 14 + .../src/js/add_to_google_spreadsheet_menu.js | 147 +++----- .../static/src/xml/addtospreadsheet.xml | 8 +- .../mail/static/src/js/custom_filter_item.js | 22 ++ addons/mail/static/src/js/discuss.js | 83 ++--- addons/mail/static/src/js/filter_menu.js | 16 - .../js/views/activity/activity_controller.js | 9 +- addons/mail/views/mail_templates.xml | 2 +- addons/mrp/static/src/js/mrp_bom_report.js | 4 +- .../static/src/js/product_pricelist_report.js | 30 +- .../js/inventory_report_list_controller.js | 3 +- .../static/src/js/theme_preview_form.js | 30 +- .../static/src/js/theme_preview_kanban.js | 27 +- 21 files changed, 475 insertions(+), 564 deletions(-) create mode 100644 addons/google_drive/static/src/xml/gdrive.xml create mode 100644 addons/mail/static/src/js/custom_filter_item.js delete mode 100644 addons/mail/static/src/js/filter_menu.js diff --git a/addons/barcodes/static/src/js/barcode_form_view.js b/addons/barcodes/static/src/js/barcode_form_view.js index 08463f2b997..82ca913d752 100644 --- a/addons/barcodes/static/src/js/barcode_form_view.js +++ b/addons/barcodes/static/src/js/barcode_form_view.js @@ -88,60 +88,69 @@ FormController.include({ /** * @private */ - _barcodePagerFirst: function () { - var self = this; - return this.mutex.exec(function () {}).then(function () { - if (!self.pager) { - self.do_warn(_t('Error: Pager not available')); - return; - } - self.pager.updateState({ - current_min: 1, - }, {notifyChange: true}); - }); + _barcodePagerFirst: async function () { + return this._updatePage(() => 1); }, /** * @private */ - _barcodePagerLast: function () { - var self = this; - return this.mutex.exec(function () {}).then(function () { - if (!self.pager) { - self.do_warn(_t('Error: Pager not available')); - return; - } - var state = self.model.get(self.handle, {raw: true}); - self.pager.updateState({ - current_min: state.count, - }, {notifyChange: true}); - }); + _barcodePagerLast: async function () { + return this._updatePage((min, state) => state.count); }, /** * @private */ _barcodePagerNext: function () { - var self = this; - return this.mutex.exec(function () {}).then(function () { - if (!self.pager) { - self.do_warn(_t('Error: Pager not available')); - return; + return this._updatePage((min, state) => { + min += 1; + if (min > state.count) { + min = 1; } - self.pager.next(); + return min; }); }, /** * @private */ _barcodePagerPrevious: function () { - var self = this; - return this.mutex.exec(function () {}).then(function () { - if (!self.pager) { - self.do_warn(_t('Error: Pager not available')); - return; + return this._updatePage((min, state) => { + min -= 1; + if (min < 1) { + min = state.count; } - self.pager.previous(); + return min; }); }, + /** + * Change the current minimum value of the pager using provided function. + * This function will be given the current minimum and state and must return + * the updated value. + * + * @private + * @param {Function(currentMin: Number, state: Object)} updater + */ + _updatePage: async function (updater) { + await this.mutex.exec(() => {}); + const state = this.model.get(this.handle, { raw: true }); + const pagingInfo = this._getPagingInfo(state); + if (!pagingInfo) { + return this.do_warn(_t('Error: Pager not available')); + } + const currentMinimum = updater(pagingInfo.currentMinimum, state); + const limit = pagingInfo.limit; + const reloadParams = state.groupedBy && state.groupedBy.length ? { + groupsLimit: limit, + groupsOffset: currentMinimum - 1, + } : { + limit, + offset: currentMinimum - 1, + }; + await this.reload(reloadParams); + // reset the scroll position to the top on page changed only + if (state.limit === limit) { + this.trigger_up('scrollTo', { top: 0 }); + } + }, /** * Returns true iff the given barcode matches the given record (candidate). * diff --git a/addons/board/static/src/js/action_manager_board_action.js b/addons/board/static/src/js/action_manager_board_action.js index 3036ac0f328..05414572311 100644 --- a/addons/board/static/src/js/action_manager_board_action.js +++ b/addons/board/static/src/js/action_manager_board_action.js @@ -21,8 +21,8 @@ ActionManager.include({ if (action.res_model === 'board.board' && action.view_mode === 'form') { action.target = 'inline'; _.extend(action.flags, { + hasActionMenus: false, hasSearchView: false, - hasSidebar: false, headless: true, }); } diff --git a/addons/board/static/src/js/add_to_board_menu.js b/addons/board/static/src/js/add_to_board_menu.js index 57e98ca87d7..dd5158c6d50 100644 --- a/addons/board/static/src/js/add_to_board_menu.js +++ b/addons/board/static/src/js/add_to_board_menu.js @@ -1,211 +1,154 @@ odoo.define('board.AddToBoardMenu', function (require) { -"use strict"; + "use strict"; -var ActionManager = require('web.ActionManager'); -var Context = require('web.Context'); -var core = require('web.core'); -var Domain = require('web.Domain'); -var favorites_submenus_registry = require('web.favorites_submenus_registry'); -var pyUtils = require('web.py_utils'); -var Widget = require('web.Widget'); + const Context = require('web.Context'); + const Domain = require('web.Domain'); + const DropdownMenuItem = require('web.DropdownMenuItem'); + const FavoriteMenu = require('web.FavoriteMenu'); + const pyUtils = require('web.py_utils'); + const { sprintf } = require('web.utils'); + const { useAutofocus } = require('web.custom_hooks'); -var _t = core._t; -var QWeb = core.qweb; - -var AddToBoardMenu = Widget.extend({ - events: _.extend({}, Widget.prototype.events, { - 'click .o_add_to_board.o_menu_header': '_onMenuHeaderClick', - 'click .o_add_to_board_confirm_button': '_onAddToBoardConfirmButtonClick', - 'click .o_add_to_board_input': '_onAddToBoardInputClick', - 'keyup .o_add_to_board_input': '_onKeyUp', - }), - /** - * @override - * @param {Object} params - * @param {Object} params.action an ir.actions description - */ - init: function (parent, params) { - this._super(parent); - this.action = params.action; - this.isOpen = false; - }, - /** - * @override - */ - start: function () { - if (this.action.id && this.action.type === 'ir.actions.act_window') { - this._render(); - } - return this._super.apply(this, arguments); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- + const { useState } = owl.hooks; /** - * Closes the menu and render it. + * 'Add to board' menu * + * Component consisiting of a toggle button, a text input and an 'Add' button. + * The first button is simply used to toggle the component and will determine + * whether the other elements should be rendered. + * The input will be given the name (or title) of the view that will be added. + * Finally, the last button will send the name as well as some of the action + * properties to the server to add the current view (and its context) to the + * user's dashboard. + * This component is only available in actions of type 'ir.actions.act_window'. + * @extends DropdownMenuItem */ - closeMenu: function () { - this.isOpen = false; - this._render(); - }, + class AddToBoardMenu extends DropdownMenuItem { + constructor() { + super(...arguments); - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- + this.interactive = true; + this.state = useState({ + name: this.env.action.name || "", + open: false, + }); - /** - * This is the main function for actually saving the dashboard. This method - * is supposed to call the route /board/add_to_dashboard with proper - * information. - * - * @private - * @returns {Promise} - */ - _addToBoard: function () { - var self = this; - var searchQuery; - // TO DO: for now the domains in query are evaluated. - // This should be changed I think. - this.trigger_up('get_search_query', { - callback: function (query) { - searchQuery = query; - } - }); - // TO DO: replace direct reference to action manager, controller, and currentAction in code below - - // AAB: trigger_up an event that will be intercepted by the controller, - // as soon as the controller is the parent of the control panel - var actionManager = this.findAncestor(function (ancestor) { - return ancestor instanceof ActionManager; - }); - var controller = actionManager.getCurrentController(); - - var context = new Context(this.action.context); - context.add(searchQuery.context); - context.add({ - group_by: searchQuery.groupBy, - orderedBy: searchQuery.orderedBy, - }); - - this.trigger_up('get_controller_query_params', { - callback: function (controllerQueryParams) { - var queryContext = controllerQueryParams.context; - var allContext = _.extend( - _.omit(controllerQueryParams, ['context']), - queryContext - ); - context.add(allContext); - } - }); - - var domain = new Domain(this.action.domain || []); - domain = Domain.prototype.normalizeArray(domain.toArray().concat(searchQuery.domain)); - - var evalutatedContext = pyUtils.eval('context', context); - for (var key in evalutatedContext) { - if (evalutatedContext.hasOwnProperty(key) && /^search_default_/.test(key)) { - delete evalutatedContext[key]; - } + useAutofocus(); } - evalutatedContext.dashboard_merge_domains_contexts = false; - var name = this.$input.val(); + //--------------------------------------------------------------------- + // Private + //--------------------------------------------------------------------- - this.closeMenu(); + /** + * This is the main function for actually saving the dashboard. This method + * is supposed to call the route /board/add_to_dashboard with proper + * information. + * @private + */ + async _addToBoard() { + const searchQuery = this.env.controlPanelModel.getQuery(); + const context = new Context(this.env.action.context); + context.add(searchQuery.context); + context.add({ + group_by: searchQuery.groupBy, + orderedBy: searchQuery.orderedBy, + }); + if (searchQuery.timeRanges && searchQuery.timeRanges.hasOwnProperty('fieldName')) { + const { fieldName: field, range, comparisonRange } = searchQuery.timeRanges; + context.add({ + time_ranges : { field, range, comparisonRange }, + }); + } + let controllerQueryParams; + this.env.controlPanelModel.trigger('get-controller-query-params', params => { + controllerQueryParams = params || {}; + }); + controllerQueryParams.context = controllerQueryParams.context || {}; + const queryContext = controllerQueryParams.context; + delete controllerQueryParams.context; + context.add(Object.assign(controllerQueryParams, queryContext)); - return self._rpc({ + const domainArray = new Domain(this.env.action.domain || []); + const domain = Domain.prototype.normalizeArray(domainArray.toArray().concat(searchQuery.domain)); + + const evalutatedContext = context.eval(); + for (const key in evalutatedContext) { + if (evalutatedContext.hasOwnProperty(key) && /^search_default_/.test(key)) { + delete evalutatedContext[key]; + } + } + evalutatedContext.dashboard_merge_domains_contexts = false; + + Object.assign(this.state, { + name: this.env.action.name || "", + open: false, + }); + + const result = await this.rpc({ route: '/board/add_to_dashboard', params: { - action_id: self.action.id || false, + action_id: this.env.action.id || false, context_to_save: evalutatedContext, domain: domain, - view_mode: controller.viewType, - name: name, + view_mode: this.env.view.type, + name: this.state.name, }, - }) - .then(function (r) { - if (r) { - self.do_notify( - _.str.sprintf(_t("'%s' added to dashboard"), name), - _t('Please refresh your browser for the changes to take effect.') - ); - } else { - self.do_warn(_t("Could not add filter to dashboard")); - } }); - }, - /** - * Renders and focuses the unique input if it is visible. - * - * @private - */ - _render: function () { - var $el = QWeb.render('AddToBoardMenu', {widget: this}); - this._replaceElement($el); - if (this.isOpen) { - this.$input = this.$('.o_add_to_board_input'); - this.$input.val(this.action.name); - this.$input.focus(); + if (result) { + this.env.services.notification.notify({ + title: sprintf(this.env._t("'%s' added to dashboard"), this.state.name), + message: this.env._t("Please refresh your browser for the changes to take effect."), + type: 'warning', + }); + } else { + this.env.services.notification.notify({ + message: this.env._t("Could not add filter to dashboard"), + type: 'danger', + }); + } } - }, - /** - * Hides and displays the submenu which allows adding custom filters. - * - * @private - */ - _toggleMenu: function () { - this.isOpen = !this.isOpen; - this._render(); - }, - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- + //--------------------------------------------------------------------- + // Handlers + //--------------------------------------------------------------------- - /** - * @private - * @param {jQueryEvent} event - */ - _onAddToBoardInputClick: function (event) { - event.preventDefault(); - event.stopPropagation(); - this.$input.focus(); - }, - /** - * @private - * @param {jQueryEvent} event - */ - _onAddToBoardConfirmButtonClick: function (event) { - event.preventDefault(); - event.stopPropagation(); - this._addToBoard(); - }, - /** - * @private - * @param {jQueryEvent} event - */ - _onKeyUp: function (event) { - if (event.which === $.ui.keyCode.ENTER) { - this._addToBoard(); + /** + * @private + * @param {KeyboardEvent} ev + */ + _onInputKeydown(ev) { + switch (ev.key) { + case 'Enter': + ev.preventDefault(); + this._addToBoard(); + break; + case 'Escape': + // Gives the focus back to the component. + ev.preventDefault(); + ev.target.blur(); + break; + } } - }, - /** - * @private - * @param {jQueryEvent} event - */ - _onMenuHeaderClick: function (event) { - event.preventDefault(); - event.stopPropagation(); - this._toggleMenu(); - }, - -}); - -favorites_submenus_registry.add('add_to_board_menu', AddToBoardMenu, 10); - -return AddToBoardMenu; + //--------------------------------------------------------------------- + // Static + //--------------------------------------------------------------------- + + /** + * @param {Object} env + * @returns {boolean} + */ + static shouldBeDisplayed(env) { + return env.action.type === 'ir.actions.act_window'; + } + } + + AddToBoardMenu.props = {}; + AddToBoardMenu.template = 'AddToBoardMenu'; + + FavoriteMenu.registry.add('add-to-board-menu', AddToBoardMenu, 10); + + return AddToBoardMenu; }); diff --git a/addons/board/static/src/js/board_view.js b/addons/board/static/src/js/board_view.js index ffdcfbeb7ed..fab64783073 100644 --- a/addons/board/static/src/js/board_view.js +++ b/addons/board/static/src/js/board_view.js @@ -42,10 +42,7 @@ var BoardController = FormController.extend({ * @override */ getTitle: function () { - if (this.inDashboard) { - return _t("My Dashboard"); - } - return this._super.apply(this, arguments); + return _t("My Dashboard"); }, //-------------------------------------------------------------------------- @@ -93,12 +90,6 @@ var BoardController = FormController.extend({ }); dialog.open(); }, - /** - * @private - */ - _onEnableDashboard: function () { - this.inDashboard = true; - }, /** * We need to intercept switch_view event coming from sub views, because we * don't actually want to switch view in dashboard, we want to do a @@ -255,6 +246,7 @@ var BoardRenderer = FormRenderer.extend({ var rawContext = new Context(action.context, evalContext, {lang: session.user_context.lang}); var context = pyUtils.eval('context', rawContext, evalContext); var domain = params.domain || pyUtils.eval('domain', action.domain || '[]', action.context); + action.context = context; action.domain = domain; var viewType = params.viewType || action.views[0][1]; @@ -265,17 +257,32 @@ var BoardRenderer = FormRenderer.extend({ .then(function (viewsInfo) { var viewInfo = viewsInfo[viewType]; var View = viewRegistry.get(viewType); + + const searchQuery = { + context: context, + domain: domain, + groupBy: typeof context.group_by === 'string' && context.group_by ? + [context.group_by] : + context.group_by || [], + orderedBy: context.orderedBy || [], + }; + + if (['pivot', 'dashboard', 'graph', 'cohort'].includes(viewType)) { + if (context.time_ranges) { + const { field: fieldName, range, comparisonRange } = context.time_ranges; + searchQuery.timeRanges = { fieldName, range, comparisonRange }; + } else { + searchQuery.timeRanges = {}; + } + } + var view = new View(viewInfo, { action: action, hasSelectors: false, modelName: action.res_model, - searchQuery: { - context: context, - domain: domain, - groupBy: typeof context.group_by === 'string' && context.group_by ? [context.group_by] : context.group_by || [], - orderedBy: context.orderedBy || [], - }, + searchQuery, withControlPanel: false, + withSearchPanel: false, }); return view.getController(self).then(function (controller) { self._boardFormViewIDs[controller.handle] = _.first( @@ -300,7 +307,6 @@ var BoardRenderer = FormRenderer.extend({ // this function has a side effect. This is ok because we assume that // once we have a '' tag, we are in a special dashboard mode. this.$el.addClass('o_dashboard'); - this.trigger_up('enable_dashboard'); var hasAction = _.detect(node.children, function (column) { return _.detect(column.children,function (element){ diff --git a/addons/board/static/src/scss/dashboard.scss b/addons/board/static/src/scss/dashboard.scss index 518849cde0a..69d1511761b 100644 --- a/addons/board/static/src/scss/dashboard.scss +++ b/addons/board/static/src/scss/dashboard.scss @@ -151,7 +151,8 @@ // Favorites menu in control panel .o_add_to_dashboard { display: none; // hidden by default - @include o-search-options-dropdown-custom-item; + max-width: 250px; + width: auto; } @include media-breakpoint-down(sm) { diff --git a/addons/board/static/src/xml/board.xml b/addons/board/static/src/xml/board.xml index bd2ec9fe605..c81892aca07 100644 --- a/addons/board/static/src/xml/board.xml +++ b/addons/board/static/src/xml/board.xml @@ -80,15 +80,27 @@