diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index 2773e27d29a..1fe8b61a1f1 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -333,6 +333,7 @@ class ir_model_access(osv.osv): # Check rights on actions # def write(self, cr, uid, *args, **argv): + self.pool.get('ir.ui.menu').clear_cache() res = super(ir_model_access, self).write(cr, uid, *args, **argv) self.check() return res @@ -341,6 +342,7 @@ class ir_model_access(osv.osv): self.check() return res def unlink(self, cr, uid, *args, **argv): + self.pool.get('ir.ui.menu').clear_cache() res = super(ir_model_access, self).unlink(cr, uid, *args, **argv) self.check() return res diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index bc3693be85f..f787db2e5d2 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -58,33 +58,74 @@ class many2many_unique(fields.many2many): class ir_ui_menu(osv.osv): _name = 'ir.ui.menu' + + def __init__(self, *args, **kwargs): + self._cache = {} + return super(ir_ui_menu, self).__init__(*args, **kwargs) + + def clear_cache(self): + # radical but this doesn't frequently happen + self._cache = {} + def search(self, cr, uid, args, offset=0, limit=2000, order=None, context=None, count=False): if context is None: context = {} - ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, - context=context) + ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1)) if uid==1: return ids - user_groups = set(self.pool.get('res.users').browse(cr, uid, uid)['groups_id'] or []) + + if not ids: + if count: + return 0 + return [] + + modelaccess = self.pool.get('ir.model.access') + user_groups = set(self.pool.get('res.users').read(cr, 1, uid)['groups_id']) result = [] for menu in self.browse(cr, uid, ids): - restrict_to_groups = menu.groups_id - if not restrict_to_groups: - if menu.action: - # if the menu itself has no restrictions, we get the groups of - # the action of the menu - try: - m, oid = menu.action.split(',', 1) - data = self.pool.get(m).browse(cr, uid, int(oid), context=context) - if data and 'groups_id' in data: - restrict_to_groups = data['groups_id'] - except: - pass - - if restrict_to_groups and not user_groups.intersection(restrict_to_groups): + # this key works because user access rights are all based on user's groups (cfr ir_model_access.check) + key = (cr.dbname, menu.id, tuple(user_groups)) + + if key in self._cache: + if self._cache[key]: + result.append(menu.id) continue + + self._cache[key] = False + if menu.groups_id: + restrict_to_groups = [g.id for g in menu.groups_id] + if not user_groups.intersection(restrict_to_groups): + continue + + if menu.action: # FIXME elif ? + # we check if the user has access to the action of the menu + m, oid = menu.action.split(',', 1) + data = self.pool.get(m).browse(cr, 1, int(oid)) + + if data: + model_field = { 'ir.actions.act_window': 'res_model', + 'ir.actions.report.custom': 'model', + 'ir.actions.report.xml': 'model', + 'ir.actions.wizard': 'model', + 'ir.actions.server': 'model_id', + } + + field = model_field.get(m) + if field and data[field]: + if not modelaccess.check(cr, uid, data[field], raise_exception=False): + continue + else: + # if there is no action, it's a 'folder' menu + if not menu.child_id: + # not displayed if there is no children + continue + + self._cache[key] = True result.append(menu.id) + + if count: + return len(result) return result def _get_full_name(self, cr, uid, ids, name, args, context): @@ -102,6 +143,14 @@ class ir_ui_menu(osv.osv): parent_path = '' return parent_path + menu.name + def write(self, *args, **kwargs): + self.clear_cache() + return super(ir_ui_menu, self).write(*args, **kwargs) + + def unlink(self, *args, **kwargs): + self.clear_cache() + return super(ir_ui_menu, self).unlink(*args, **kwargs) + def copy(self, cr, uid, id, default=None, context=None): ir_values_obj = self.pool.get('ir.values') res = super(ir_ui_menu, self).copy(cr, uid, id, context=context)