diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index 3fce58fe0bb..4b00f6f4e01 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -1710,11 +1710,15 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
self.clear_selection();
})
.on('reset', this.proxy('clear_selection'));
- return this.model.call('get_filters', [this.view.model])
+ return this.model.call('get_filters', [this.view.model, this.get_action_id()])
.then(this.proxy('set_filters'))
.done(function () { self.is_ready.resolve(); })
.fail(function () { self.is_ready.reject.apply(self.is_ready, arguments); });
},
+ get_action_id: function(){
+ var action = instance.client.action_manager.inner_action;
+ if (action) return action.id;
+ },
/**
* Special implementation delaying defaults until CustomFilters is loaded
*/
@@ -1734,9 +1738,11 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
* @return {String} mapping key corresponding to the filter
*/
key_for: function (filter) {
- var user_id = filter.user_id;
+ var user_id = filter.user_id,
+ action_id = filter.action_id;
var uid = (user_id instanceof Array) ? user_id[0] : user_id;
- return _.str.sprintf('(%s)%s', uid, filter.name);
+ var act_id = (action_id instanceof Array) ? action_id[0] : action_id;
+ return _.str.sprintf('(%s)(%s)%s', uid, act_id, filter.name);
},
/**
* Generates a :js:class:`~instance.web.search.Facet` descriptor from a
diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js
index 5faf13b2599..d93cb131e05 100644
--- a/addons/web/static/test/search.js
+++ b/addons/web/static/test/search.js
@@ -171,6 +171,7 @@ var makeSearchView = function (instance, dummy_widget_attributes, defaults) {
dummy: {type: 'char', string: 'Dummy'}
};
};
+ instance.client = { action_manager: { inner_action: undefined } };
var dataset = new instance.web.DataSet(null, 'dummy.model');
var mock_parent = {getParent: function () {return null;}};
diff --git a/openerp/addons/base/ir/ir_filters.py b/openerp/addons/base/ir/ir_filters.py
index 6364a2492be..f179a8169f0 100644
--- a/openerp/addons/base/ir/ir_filters.py
+++ b/openerp/addons/base/ir/ir_filters.py
@@ -36,15 +36,30 @@ class ir_filters(osv.osv):
default.update({'name':_('%s (copy)') % name})
return super(ir_filters, self).copy(cr, uid, id, default, context)
- def get_filters(self, cr, uid, model):
+ def _get_action_domain(self, cr, uid, action_id=None):
+ """Return a domain component for matching filters that are visible in the
+ same context (menu/view) as the given action."""
+ if action_id:
+ # filters specific to this menu + global ones
+ return [('action_id', 'in' , [action_id, False])]
+ # only global ones
+ return [('action_id', '=', False)]
+
+ def get_filters(self, cr, uid, model, action_id=None):
"""Obtain the list of filters available for the user on the given model.
+ :param action_id: optional ID of action to restrict filters to this action
+ plus global filters. If missing only global filters are returned.
+ The action does not have to correspond to the model, it may only be
+ a contextual action.
:return: list of :meth:`~osv.read`-like dicts containing the
- ``name``, ``is_default``, ``domain``, ``user_id`` (m2o tuple) and
- ``context`` of the matching ``ir.filters``.
+ ``name``, ``is_default``, ``domain``, ``user_id`` (m2o tuple),
+ ``action_id`` (m2o tuple) and ``context`` of the matching ``ir.filters``.
"""
- # available filters: private filters (user_id=uid) and public filters (uid=NULL)
- filter_ids = self.search(cr, uid,
+ # available filters: private filters (user_id=uid) and public filters (uid=NULL),
+ # and filters for the action (action_id=action_id) or global (action_id=NULL)
+ action_domain = self._get_action_domain(cr, uid, action_id)
+ filter_ids = self.search(cr, uid, action_domain +
[('model_id','=',model),('user_id','in',[uid, False])])
my_filters = self.read(cr, uid, filter_ids,
['name', 'is_default', 'domain', 'context', 'user_id'])
@@ -66,7 +81,8 @@ class ir_filters(osv.osv):
:raises openerp.exceptions.Warning: if there is an existing default and
we're not updating it
"""
- existing_default = self.search(cr, uid, [
+ action_domain = self._get_action_domain(cr, uid, vals.get('action_id'))
+ existing_default = self.search(cr, uid, action_domain + [
('model_id', '=', vals['model_id']),
('user_id', '=', False),
('is_default', '=', True)], context=context)
@@ -83,7 +99,9 @@ class ir_filters(osv.osv):
def create_or_replace(self, cr, uid, vals, context=None):
lower_name = vals['name'].lower()
- matching_filters = [f for f in self.get_filters(cr, uid, vals['model_id'])
+ action_id = vals.get('action_id')
+ current_filters = self.get_filters(cr, uid, vals['model_id'], action_id)
+ matching_filters = [f for f in current_filters
if f['name'].lower() == lower_name
# next line looks for matching user_ids (specific or global), i.e.
# f.user_id is False and vals.user_id is False or missing,
@@ -92,18 +110,22 @@ class ir_filters(osv.osv):
if vals.get('is_default'):
if vals.get('user_id'):
- act_ids = self.search(cr, uid, [
+ # Setting new default: any other default that belongs to the user
+ # should be turned off
+ action_domain = self._get_action_domain(cr, uid, action_id)
+ act_ids = self.search(cr, uid, action_domain + [
('model_id', '=', vals['model_id']),
('user_id', '=', vals['user_id']),
('is_default', '=', True),
], context=context)
- self.write(cr, uid, act_ids, {'is_default': False}, context=context)
+ if act_ids:
+ self.write(cr, uid, act_ids, {'is_default': False}, context=context)
else:
self._check_global_default(
cr, uid, vals, matching_filters, context=None)
# When a filter exists for the same (name, model, user) triple, we simply
- # replace its definition.
+ # replace its definition (considering action_id irrelevant here)
if matching_filters:
self.write(cr, uid, matching_filters[0]['id'], vals, context)
return matching_filters[0]['id']
@@ -114,16 +136,17 @@ class ir_filters(osv.osv):
# Partial constraint, complemented by unique index (see below)
# Still useful to keep because it provides a proper error message when a violation
# occurs, as it shares the same prefix as the unique index.
- ('name_model_uid_unique', 'unique (name, model_id, user_id)', 'Filter names must be unique'),
+ ('name_model_uid_unique', 'unique (name, model_id, user_id, action_id)', 'Filter names must be unique'),
]
def _auto_init(self, cr, context=None):
super(ir_filters, self)._auto_init(cr, context)
# Use unique index to implement unique constraint on the lowercase name (not possible using a constraint)
- cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = 'ir_filters_name_model_uid_unique_index'")
+ cr.execute("DROP INDEX IF EXISTS ir_filters_name_model_uid_unique_index") # drop old index w/o action
+ cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = 'ir_filters_name_model_uid_unique_action_index'")
if not cr.fetchone():
- cr.execute("""CREATE UNIQUE INDEX "ir_filters_name_model_uid_unique_index" ON ir_filters
- (lower(name), model_id, COALESCE(user_id,-1))""")
+ cr.execute("""CREATE UNIQUE INDEX "ir_filters_name_model_uid_unique_action_index" ON ir_filters
+ (lower(name), model_id, COALESCE(user_id,-1), COALESCE(action_id,-1))""")
_columns = {
'name': fields.char('Filter Name', translate=True, required=True),
@@ -133,7 +156,11 @@ class ir_filters(osv.osv):
'domain': fields.text('Domain', required=True),
'context': fields.text('Context', required=True),
'model_id': fields.selection(_list_all_models, 'Model', required=True),
- 'is_default': fields.boolean('Default filter')
+ 'is_default': fields.boolean('Default filter'),
+ 'action_id': fields.many2one('ir.actions.actions', 'Action', ondelete='cascade',
+ help="The menu action this filter applies to. "
+ "When left empty the filter applies to all menus "
+ "for this model.")
}
_defaults = {
'domain': '[]',
@@ -141,5 +168,6 @@ class ir_filters(osv.osv):
'user_id': lambda self,cr,uid,context=None: uid,
'is_default': False
}
+ _order = 'model_id, name, id desc'
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/ir/ir_filters.xml b/openerp/addons/base/ir/ir_filters.xml
index 46242b67ab4..acc16e172b4 100644
--- a/openerp/addons/base/ir/ir_filters.xml
+++ b/openerp/addons/base/ir/ir_filters.xml
@@ -20,6 +20,7 @@
+
@@ -37,6 +38,7 @@
+
diff --git a/openerp/addons/base/tests/test_ir_filters.py b/openerp/addons/base/tests/test_ir_filters.py
index 04ecca74525..9ca093d77af 100644
--- a/openerp/addons/base/tests/test_ir_filters.py
+++ b/openerp/addons/base/tests/test_ir_filters.py
@@ -5,10 +5,9 @@ from openerp import exceptions
from openerp.tests import common
def noid(d):
- """ Removes `id` key from a dict so we don't have to keep these things
- around when trying to match
- """
- if 'id' in d: del d['id']
+ """ Removes values that are not relevant for the test comparisons """
+ d.pop('id', None)
+ d.pop('action_id', None)
return d
class FiltersCase(common.TransactionCase):