From de27eaa97161abb06c717a36b7ed68d6f36e4e73 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 21 Dec 2017 11:15:44 +0100 Subject: [PATCH 1/6] [FIX] web: config.debug and session.debug equivalence Before this rev., config.debug was always a boolean, whereas it should also encode the special value 'assets', as done in session. --- addons/web/static/src/js/core/session.js | 4 ++-- addons/web/static/src/js/services/config.js | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index c3181a7abe6..e17b1b0c7fc 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -3,6 +3,7 @@ odoo.define('web.Session', function (require) { var ajax = require('web.ajax'); var concurrency = require('web.concurrency'); +var config = require('web.config'); var core = require('web.core'); var mixins = require('web.mixins'); var utils = require('web.utils'); @@ -37,8 +38,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { this.avoid_recursion = false; this.use_cors = options.use_cors || false; this.setup(origin); - var debug_param = $.deparam($.param.querystring()).debug; - this.debug = (debug_param !== undefined ? debug_param || 1 : false); + this.debug = config.debug; // for historic reasons, the session requires a name to properly work // (see the methods get_cookie and set_cookie). We should perhaps diff --git a/addons/web/static/src/js/services/config.js b/addons/web/static/src/js/services/config.js index 04002b1b381..8a42e755322 100644 --- a/addons/web/static/src/js/services/config.js +++ b/addons/web/static/src/js/services/config.js @@ -10,14 +10,19 @@ odoo.define('web.config', function (require) { * this file someday. */ +var debugParam = $.deparam($.param.querystring()).debug; +var debug = false; +if (debugParam !== undefined) { + debug = debugParam === 'assets' ? 'assets' : true; +} + var config = { /** - * debug is a boolean flag. It is only considered true if the flag is set - * in the url + * debug can be either a boolean, or the special value 'assets' * - * @type Boolean + * @type boolean|string */ - debug: ($.deparam($.param.querystring()).debug !== undefined), + debug: debug, device: { /** * touch is a boolean, true if the device supports touch interaction From 9bee25a9df65f657fef0f2f32a7ad823fdc7d4e1 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 29 Nov 2017 14:39:42 +0100 Subject: [PATCH 2/6] [REF] account,web(_tour): close rainbowman on click This rev. makes the Rainbowman stop listening to event 'clear_uncommitted_changes' on core.bus, as this event is about to be removed (in a next commit). It was using it to destroy itself when such an event was triggered (basically, when the current view was left). Instead, we removed the option 'click_close' as, when set to true, which was the default, the rainbowman was destroyed as soon as the user clicked outside the raibowman. --- .../js/reconciliation/reconciliation_renderer.js | 1 - addons/web/static/src/js/widgets/rainbow_man.js | 16 +++++----------- .../static/tests/widgets/rainbow_man_tests.js | 1 - addons/web_tour/static/src/js/tour_manager.js | 6 +++--- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index 91139aa0926..adda440df79 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -126,7 +126,6 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, { type: 'rainbow_man', fadeout: 'no', message: $done, - click_close: false, }); this.$el.css('min-height', '450px'); } diff --git a/addons/web/static/src/js/widgets/rainbow_man.js b/addons/web/static/src/js/widgets/rainbow_man.js index 16d4d9a1e3d..e46201584bc 100644 --- a/addons/web/static/src/js/widgets/rainbow_man.js +++ b/addons/web/static/src/js/widgets/rainbow_man.js @@ -33,8 +33,6 @@ var RainbowMan = Widget.extend({ * [options.fadeout='no'] will keep rainbowman on screen until user clicks * anywhere outside rainbowman * @param {string} [options.img_url] URL of the image to be displayed - * @param {boolean} [options.click_close=true] If true, destroys rainbowman on - * click outside */ init: function (options) { this._super.apply(this, arguments); @@ -43,23 +41,19 @@ var RainbowMan = Widget.extend({ fadeout: 'medium', img_url: '/web/static/src/img/smile.svg', message: _t('Well Done!'), - click_close: true, }); this.delay = rainbowDelay[this.options.fadeout]; - core.bus.on('clear_uncommitted_changes', this, this.destroy); }, /** * @override */ start: function () { var self = this; - if (this.options.click_close) { - core.bus.on('click', this, function (ev) { - if (ev.originalEvent && ev.target.className.indexOf('o_reward') === -1) { - this.destroy(); - } - }); - } + core.bus.on('click', this, function (ev) { + if (ev.originalEvent && ev.target.className.indexOf('o_reward') === -1) { + this.destroy(); + } + }); if (this.delay) { setTimeout(function () { self.$el.addClass('o_reward_fading'); diff --git a/addons/web/static/tests/widgets/rainbow_man_tests.js b/addons/web/static/tests/widgets/rainbow_man_tests.js index b9ccef662c9..ad265416f3c 100644 --- a/addons/web/static/tests/widgets/rainbow_man_tests.js +++ b/addons/web/static/tests/widgets/rainbow_man_tests.js @@ -9,7 +9,6 @@ QUnit.module('RainbowMan', { beforeEach: function () { this.data = { message: 'Congrats!', - click_close: false }; }, }, function () { diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index 54f51f0bbc6..599e6c6b2f5 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -416,13 +416,13 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { _consume_tour: function (tour_name, error) { delete this.active_tooltips[tour_name]; //display rainbow at the end of any tour - if (this.tours[tour_name].rainbowMan && this.running_tour !== tour_name - && this.tours[tour_name].current_step === this.tours[tour_name].steps.length) { + if (this.tours[tour_name].rainbowMan && this.running_tour !== tour_name && + this.tours[tour_name].current_step === this.tours[tour_name].steps.length) { var $rainbow_message = $('' + 'Good job!' + ' You went through all steps of this tour.' + ''); - new RainbowMan({message: $rainbow_message, click_close: false}).appendTo(this.$body); + new RainbowMan({message: $rainbow_message}).appendTo(this.$body); } this.tours[tour_name].current_step = 0; local_storage.removeItem(get_step_key(tour_name)); From 32b8cec5361174c92c45a4975d4e02f1fb448aa2 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 17 Nov 2017 13:14:02 +0100 Subject: [PATCH 3/6] [REF] web,*: ActionManager/ViewManager refactoring This rev. is the first part (out of 3) of the refactoring of the ActionManager and ViewManager layer of the webclient. The main changes are: - there is no ViewManager anymore, its work is now handled by the ActionManager itself, but isolated in a specific file ; this eases a lot of things, as the breadcrumbs handling for example. - the ActionManager code is converted to the new coding principles and guidelines ; mainly, children widgets communicate with it by triggering events up, and not by function calls anymore. - the ActionManager layer is now testable, and a lot of tests have already been written. - the code in other addons has been adapted consequently. What's coming next: - introduce the 'AbstractAction' Widget, and make client actions and view controllers inherit from it ; this widget will implement a common API that could be used uniformly by the ActionManager (e.g. restore(), canBeLeft(), renderButtons(), getTitle()...). - move the ControlPanel handling from the ActionManager to the AbstractAction. --- .../reconciliation/reconciliation_action.js | 14 +- .../static/src/js/import_action.js | 6 +- .../src/js/action_manager_board_action.js | 33 + addons/board/static/src/js/dashboard.js | 20 +- addons/board/static/src/js/favorite_menu.js | 17 +- addons/board/views/board_templates.xml | 1 + .../document/static/tests/document_tests.js | 2 +- .../static/src/js/search.js | 17 +- addons/mail/static/src/js/chat_mixin.js | 2 +- addons/mail/static/src/js/client_action.js | 2 +- .../static/src/js/timesheet_plan.js | 1 - .../js/stock_traceability_report_backend.js | 1 - .../src/js/chrome/abstract_web_client.js | 55 +- .../static/src/js/chrome/action_manager.js | 1669 ++++++++--------- .../js/chrome/action_manager_act_window.js | 756 ++++++++ .../web/static/src/js/chrome/control_panel.js | 27 +- addons/web/static/src/js/chrome/menu.js | 15 + .../web/static/src/js/chrome/search_menus.js | 11 +- .../web/static/src/js/chrome/search_view.js | 6 +- .../web/static/src/js/chrome/view_manager.js | 671 ------- addons/web/static/src/js/chrome/web_client.js | 15 +- addons/web/static/src/js/core/dialog.js | 4 +- addons/web/static/src/js/core/misc.js | 11 - .../web/static/src/js/report/client_action.js | 1 - .../src/js/views/abstract_controller.js | 170 +- .../web/static/src/js/views/abstract_view.js | 25 +- .../src/js/views/basic/basic_controller.js | 22 +- .../src/js/views/form/form_controller.js | 4 +- .../web/static/src/js/views/form/form_view.js | 10 +- .../src/js/views/list/list_controller.js | 2 +- .../web/static/src/js/views/list/list_view.js | 4 +- .../web/static/src/js/views/view_dialogs.js | 9 +- .../static/src/js/widgets/debug_manager.js | 145 +- addons/web/static/src/less/fields.less | 2 +- addons/web/static/src/less/form_view.less | 2 +- addons/web/static/src/less/graph_view.less | 2 - addons/web/static/src/less/modal.less | 4 - addons/web/static/src/less/pivot_view.less | 1 - addons/web/static/src/less/views.less | 3 - .../web/static/src/less/webclient_layout.less | 7 +- addons/web/static/src/xml/base.xml | 12 +- .../chrome/action_manager_mobile_tests.js | 85 + .../tests/chrome/action_manager_tests.js | 1667 ++++++++++++++++ .../web/static/tests/helpers/mock_server.js | 21 +- addons/web/static/tests/helpers/test_utils.js | 89 +- addons/web/static/tests/views/form_tests.js | 41 +- addons/web/static/tests/views/kanban_tests.js | 14 +- addons/web/static/tests/views/list_tests.js | 31 +- .../static/tests/views/view_dialogs_tests.js | 2 +- .../static/tests/widgets/data_export_tests.js | 4 +- addons/web/views/webclient_templates.xml | 4 +- .../static/src/js/backend/dashboard.js | 1 - 52 files changed, 3883 insertions(+), 1857 deletions(-) create mode 100644 addons/board/static/src/js/action_manager_board_action.js create mode 100644 addons/web/static/src/js/chrome/action_manager_act_window.js delete mode 100644 addons/web/static/src/js/chrome/view_manager.js create mode 100644 addons/web/static/tests/chrome/action_manager_mobile_tests.js create mode 100644 addons/web/static/tests/chrome/action_manager_tests.js diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js index 79724846cbc..4102780c5c0 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_action.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js @@ -106,8 +106,7 @@ var StatementAction = Widget.extend(ControlPanelMixin, { var self = this; this.set("title", this.title); - var breadcrumbs = this.action_manager && this.action_manager.get_breadcrumbs() || [{ title: this.title, action: this }]; - this.update_control_panel({breadcrumbs: breadcrumbs, search_view_hidden: true}, {clear: true}); + this.update_control_panel({search_view_hidden: true}, {clear: true}); this.renderer.prependTo(self.$('.o_form_sheet')); this._renderLines(); @@ -122,18 +121,11 @@ var StatementAction = Widget.extend(ControlPanelMixin, { do_show: function () { this._super.apply(this, arguments); if (this.action_manager) { - var breadcrumbs = this.action_manager && this.action_manager.get_breadcrumbs() || [{ title: this.title, action: this }]; - while (breadcrumbs.length) { - if (breadcrumbs[breadcrumbs.length-1].action.widget === this) { - break; - } - breadcrumbs.pop(); - } - this.update_control_panel({breadcrumbs: breadcrumbs, search_view_hidden: true}, {clear: true}); + this.update_control_panel({search_view_hidden: true}, {clear: true}); this.action_manager.do_push_state({ action: this.params.tag, active_id: this.params.res_id, - }); + }); } }, diff --git a/addons/base_import/static/src/js/import_action.js b/addons/base_import/static/src/js/import_action.js index ee9e95fa5a0..513a663cfc2 100644 --- a/addons/base_import/static/src/js/import_action.js +++ b/addons/base_import/static/src/js/import_action.js @@ -126,7 +126,6 @@ var DataImport = Widget.extend(ControlPanelMixin, { self.renderButtons(); self.renderImportLink(); var status = { - breadcrumbs: self.action_manager.get_breadcrumbs(), cp_content: {$buttons: self.$buttons}, }; self.update_control_panel(status); @@ -505,10 +504,7 @@ var DataImport = Widget.extend(ControlPanelMixin, { this.exit(); }, exit: function () { - this.do_action({ - type: 'ir.actions.client', - tag: 'history_back' - }); + this.trigger_up('history_back'); }, onresults: function (event, from, to, message) { var no_messages = _.isEmpty(message); diff --git a/addons/board/static/src/js/action_manager_board_action.js b/addons/board/static/src/js/action_manager_board_action.js new file mode 100644 index 00000000000..3036ac0f328 --- /dev/null +++ b/addons/board/static/src/js/action_manager_board_action.js @@ -0,0 +1,33 @@ +odoo.define('board.ActionManager', function (require) { +"use strict"; + +/** + * The purpose of this file is to patch the ActionManager to properly generate + * the flags for the 'ir.actions.act_window' of model 'board.board'. + */ + +var ActionManager = require('web.ActionManager'); + +ActionManager.include({ + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + * @private + */ + _executeWindowAction: function (action) { + if (action.res_model === 'board.board' && action.view_mode === 'form') { + action.target = 'inline'; + _.extend(action.flags, { + hasSearchView: false, + hasSidebar: false, + headless: true, + }); + } + return this._super.apply(this, arguments); + }, +}); + +}); diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 00b66c06312..e84529be83b 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -104,10 +104,9 @@ FormController.include({ }, /** - * We need to intercept switch_view event coming from sub views, because - * there is no view manager doing the job. Also, we don't actually want to - * switch view in dashboard, we want to do a do_action (which will open the - * record in a different breadcrumb) + * 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 + * do_action (which will open the record in a different breadcrumb). * * @private * @param {OdooEvent} event @@ -126,6 +125,9 @@ FormController.include({ }); FormRenderer.include({ + custom_events: _.extend({}, FormRenderer.prototype.custom_events, { + env_updated: '_onEnvUpdated', + }), events: _.extend({}, FormRenderer.prototype.events, { 'click .oe_dashboard_column .oe_fold': '_onFoldClick', 'click .oe_dashboard_link_change_layout': '_onChangeLayout', @@ -339,6 +341,16 @@ FormRenderer.include({ }, }); }, + /** + * Stops the propagation of 'update_env' events triggered by the controllers + * instantiated by the dashboard. + * + * @override + * @private + */ + _onEnvUpdated: function (event) { + event.stopPropagation(); + }, /** * @private * @param {MouseEvent} event diff --git a/addons/board/static/src/js/favorite_menu.js b/addons/board/static/src/js/favorite_menu.js index a4d7aa11022..77cd3df4967 100644 --- a/addons/board/static/src/js/favorite_menu.js +++ b/addons/board/static/src/js/favorite_menu.js @@ -7,7 +7,6 @@ var core = require('web.core'); var Domain = require('web.Domain'); var FavoriteMenu = require('web.FavoriteMenu'); var pyeval = require('web.pyeval'); -var ViewManager = require('web.ViewManager'); var _t = core._t; var QWeb = core.qweb; @@ -23,11 +22,7 @@ FavoriteMenu.include({ if(this.action_id === undefined) { return this._super(); } - var am = this.findAncestor(function (a) { - return a instanceof ActionManager; - }); - if (am && am.get_inner_widget() instanceof ViewManager) { - this.view_manager = am.get_inner_widget(); + if (this.action.type === 'ir.actions.act_window') { this.add_to_dashboard_available = true; this.$('.o_favorites_menu').append(QWeb.render('SearchView.addtodashboard')); this.$add_to_dashboard = this.$('.o_add_to_dashboard'); @@ -71,7 +66,13 @@ FavoriteMenu.include({ context.add({ group_by: pyeval.eval('groupbys', search_data.groupbys || []) }); - context.add(this.view_manager.active_view.controller.getContext()); + // 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 am = this.findAncestor(function (a) { + return a instanceof ActionManager; + }); + var controller = am.getCurrentController(); + context.add(controller.widget.getContext()); var c = pyeval.eval('context', context); for (var k in c) { if (c.hasOwnProperty(k) && /^search_default_/.test(k)) { @@ -88,7 +89,7 @@ FavoriteMenu.include({ action_id: self.action_id || false, context_to_save: c, domain: domain, - view_mode: self.view_manager.active_view.type, + view_mode: controller.viewType, name: name, }, }) diff --git a/addons/board/views/board_templates.xml b/addons/board/views/board_templates.xml index 7e8b8d9c4a1..ac87af8ef26 100644 --- a/addons/board/views/board_templates.xml +++ b/addons/board/views/board_templates.xml @@ -3,6 +3,7 @@