summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Bohy <aab@odoo.com>2018-03-01 11:19:23 +0100
committerAaron Bohy <aab@odoo.com>2018-03-06 09:12:21 +0100
commitcdb01d52ea0a7b3b02594f2bd98d1a7bdf2fa2a3 (patch)
treed2969057a9670a18b5c0f1ca5e9de5a1e4b91c15
parentbc66da2dfaf399f642836256c67a65a5468cd47a (diff)
[FIX] web: create favorite filters in dialogs
This is a manual forwardport of rev. b466b3bc608. Consider the following scenario: open in edit mode a form view containing a many2many field displayed with widget many2many_tags, on the many2many field dropdown, click on 'Search More' (it opens a dialog), and in the dialog, try to save the current search in the searchview's favorite menu. Before this rev., it crashed because it tried to retrieve the action's context (to add to the saved filter), whereas there is no action in this case. Actually, the action's context should never been used in this case. What must be used is the controller's context (this mainly makes sense for Pivot and Graph views). Fixes #23293
-rw-r--r--addons/web/static/src/js/chrome/abstract_web_client.js13
-rw-r--r--addons/web/static/src/js/chrome/action_manager_act_window.js15
-rw-r--r--addons/web/static/src/js/chrome/search_menus.js31
-rw-r--r--addons/web/static/src/js/views/view_dialogs.js19
-rw-r--r--addons/web/static/tests/chrome/action_manager_tests.js65
-rw-r--r--addons/web/static/tests/views/view_dialogs_tests.js60
6 files changed, 199 insertions, 4 deletions
diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js
index b4b192c1ea9..9fe38adbde6 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -68,6 +68,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
.load_filters(event.data.dataset, event.data.action_id)
.then(event.data.on_success);
},
+ create_filter: '_onCreateFilter',
push_state: '_onPushState',
show_effect: '_onShowEffect',
// session
@@ -338,6 +339,18 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
//--------------------------------------------------------------------------
/**
+ * @private
+ * @param {OdooEvent} e
+ * @param {Object} e.data.filter the filter description
+ * @param {function} e.data.on_success called when the RPC succeeds with its
+ * returned value as argument
+ */
+ _onCreateFilter: function (e) {
+ data_manager
+ .create_filter(e.data.filter)
+ .then(e.data.on_success);
+ },
+ /**
* Displays a warning in a dialog of with the NotificationManager
*
* @private
diff --git a/addons/web/static/src/js/chrome/action_manager_act_window.js b/addons/web/static/src/js/chrome/action_manager_act_window.js
index 9cfa99a9e7d..6cc05ebc5d2 100644
--- a/addons/web/static/src/js/chrome/action_manager_act_window.js
+++ b/addons/web/static/src/js/chrome/action_manager_act_window.js
@@ -21,6 +21,7 @@ ActionManager.include({
custom_events: _.extend({}, ActionManager.prototype.custom_events, {
env_updated: '_onEnvUpdated',
execute_action: '_onExecuteAction',
+ get_controller_context: '_onGetControllerContext',
search: '_onSearch',
switch_view: '_onSwitchView',
}),
@@ -716,6 +717,20 @@ ActionManager.include({
});
},
/**
+ * Handles a context request: provides to the caller the context of the
+ * current controller.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ * @param {function} ev.data.callback used to send the requested context
+ */
+ _onGetControllerContext: function (ev) {
+ ev.stopPropagation();
+ var currentController = this.getCurrentController();
+ var context = currentController && currentController.widget.getContext();
+ ev.data.callback(context || {});
+ },
+ /**
* Called when there is a change in the search view, so the current action's
* environment needs to be updated with the new domain, context and groupby.
*
diff --git a/addons/web/static/src/js/chrome/search_menus.js b/addons/web/static/src/js/chrome/search_menus.js
index 38f2bb7374c..d33cdce6500 100644
--- a/addons/web/static/src/js/chrome/search_menus.js
+++ b/addons/web/static/src/js/chrome/search_menus.js
@@ -97,10 +97,15 @@ return Widget.extend({
}
var user_context = this.getSession().user_context;
var search = this.searchview.build_search_data();
- var view_context = this.action.env.context;
+ var controllerContext;
+ this.trigger_up('get_controller_context', {
+ callback: function (ctx) {
+ controllerContext = ctx;
+ },
+ });
var results = pyeval.eval_domains_and_contexts({
domains: search.domains,
- contexts: [user_context].concat(search.contexts.concat(view_context || [])),
+ contexts: [user_context].concat(search.contexts.concat(controllerContext || [])),
group_by_seq: search.groupbys || [],
});
if (!_.isEmpty(results.group_by)) {
@@ -123,7 +128,7 @@ return Widget.extend({
is_default: default_filter,
action_id: this.action_id,
};
- return data_manager.create_filter(filter).done(function (id) {
+ return this._createFilter(filter).then(function (id) {
filter.id = id;
self.toggle_save_menu(false);
self.$save_name.find('input').val('').prop('checked', false);
@@ -273,6 +278,26 @@ return Widget.extend({
}
});
},
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Creates a new search view filter.
+ *
+ * @private
+ * @param {Object} filter the filter description
+ * @returns {$.Deferred} resolved with the RPC's result when it succeeds
+ */
+ _createFilter: function (filter) {
+ var def = $.Deferred();
+ this.trigger_up('create_filter', {
+ filter: filter,
+ on_success: def.resolve.bind(def),
+ });
+ return def;
+ },
});
});
diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js
index 4be84c23b71..1b74068d44d 100644
--- a/addons/web/static/src/js/views/view_dialogs.js
+++ b/addons/web/static/src/js/views/view_dialogs.js
@@ -260,6 +260,7 @@ var SelectCreateDialog = ViewDialog.extend({
var searchData = this._process_search_data(d.domains, d.contexts, d.groupbys);
this.list_controller.reload(searchData);
},
+ get_controller_context: '_onGetControllerContext',
}),
/**
@@ -416,6 +417,24 @@ var SelectCreateDialog = ViewDialog.extend({
dialog.on('closed', this, this.close);
return dialog;
},
+
+ //--------------------------------------------------------------------------
+ // 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 {
diff --git a/addons/web/static/tests/chrome/action_manager_tests.js b/addons/web/static/tests/chrome/action_manager_tests.js
index 4d682b8994c..7fdaa138754 100644
--- a/addons/web/static/tests/chrome/action_manager_tests.js
+++ b/addons/web/static/tests/chrome/action_manager_tests.js
@@ -5,6 +5,7 @@ var ReportClientAction = require('report.client_action');
var ControlPanelMixin = require('web.ControlPanelMixin');
var core = require('web.core');
+var ListController = require('web.ListController');
var ReportService = require('web.ReportService');
var testUtils = require('web.test_utils');
var Widget = require('web.Widget');
@@ -2347,6 +2348,70 @@ QUnit.module('ActionManager', {
actionManager.destroy();
});
+ QUnit.test('save current search', function (assert) {
+ assert.expect(4);
+
+ testUtils.patch(ListController, {
+ getContext: function () {
+ return {
+ shouldBeInFilterContext: true,
+ };
+ },
+ });
+
+ _.extend(this.archs, {
+ 'partner,7,search':
+ '<search>' +
+ '<filter name="bar" help="Bar" domain="[(\'bar\', \'=\', 1)]"/>' +
+ '</search>',
+ });
+
+ this.actions.push({
+ id: 33,
+ context: {
+ shouldNotBeInFilterContext: false,
+ },
+ name: 'Partners',
+ res_model: 'partner',
+ search_view_id: [7, 'a custom search view'],
+ type: 'ir.actions.act_window',
+ views: [[false, 'list']],
+ });
+
+ var actionManager = createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ intercepts: {
+ create_filter: function (event) {
+ var filter = event.data.filter;
+ assert.deepEqual(filter.domain, [['bar', '=', 1]],
+ "should save the correct domain");
+ assert.deepEqual(filter.context, {shouldBeInFilterContext: true},
+ "should save the correct context");
+ },
+ },
+ });
+ actionManager.doAction(33);
+
+ assert.strictEqual(actionManager.$('.o_data_row').length, 5,
+ "should contain 5 records");
+
+ // filter on bar
+ $('.o_control_panel .o_filters_menu a:contains(Bar)').click();
+
+ assert.strictEqual(actionManager.$('.o_data_row').length, 2,
+ "should contain 2 records");
+
+ // save filter
+ $('.o_control_panel .o_save_search a').click(); // toggle 'Save current search'
+ $('.o_control_panel .o_save_name input[type=text]').val('some name'); // name the filter
+ $('.o_control_panel .o_save_name button').click(); // click on 'Save'
+
+ testUtils.unpatch(ListController);
+ actionManager.destroy();
+ });
+
QUnit.module('Actions in target="new"');
QUnit.test('can execute act_window actions in target="new"', function (assert) {
diff --git a/addons/web/static/tests/views/view_dialogs_tests.js b/addons/web/static/tests/views/view_dialogs_tests.js
index 1f958fb38bc..6ec4f50ab4b 100644
--- a/addons/web/static/tests/views/view_dialogs_tests.js
+++ b/addons/web/static/tests/views/view_dialogs_tests.js
@@ -1,8 +1,9 @@
odoo.define('web.view_dialogs_tests', function (require) {
"use strict";
-var testUtils = require('web.test_utils');
var dialogs = require('web.view_dialogs');
+var ListController = require('web.ListController');
+var testUtils = require('web.test_utils');
var Widget = require('web.Widget');
QUnit.module('Views', {
@@ -241,6 +242,63 @@ QUnit.module('Views', {
parent.destroy();
});
+ QUnit.test('SelectCreateDialog: save current search', function (assert) {
+ assert.expect(4);
+
+ testUtils.patch(ListController, {
+ getContext: function () {
+ return {
+ shouldBeInFilterContext: true,
+ };
+ },
+ });
+
+ var parent = createParent({
+ data: this.data,
+ archs: {
+ 'partner,false,list':
+ '<tree>' +
+ '<field name="display_name"/>' +
+ '</tree>',
+ 'partner,false,search':
+ '<search>' +
+ '<filter name="bar" help="Bar" domain="[(\'bar\', \'=\', True)]"/>' +
+ '</search>',
+
+ },
+ intercepts: {
+ create_filter: function (event) {
+ var filter = event.data.filter;
+ assert.deepEqual(filter.domain, [['bar', '=', true]],
+ "should save the correct domain");
+ assert.deepEqual(filter.context, {shouldBeInFilterContext: true},
+ "should save the correct context");
+ },
+ },
+ });
+
+ var dialog = new dialogs.SelectCreateDialog(parent, {
+ context: {shouldNotBeInFilterContext: false},
+ res_model: 'partner',
+ }).open();
+
+ assert.strictEqual(dialog.$('.o_data_row').length, 3,
+ "should contain 3 records");
+
+ // filter on bar
+ dialog.$('.o_filters_menu a:contains(Bar)').click();
+
+ assert.strictEqual(dialog.$('.o_data_row').length, 2,
+ "should contain 2 records");
+
+ // save filter
+ dialog.$('.o_save_search a').click(); // toggle 'Save current search'
+ dialog.$('.o_save_name input[type=text]').val('some name'); // name the filter
+ dialog.$('.o_save_name button').click(); // click on 'Save'
+
+ testUtils.unpatch(ListController);
+ parent.destroy();
+ });
});
});