from odoo import models, fields, api, tools, _ from collections import defaultdict class IrUiMenu(models.Model): _inherit = 'ir.ui.menu' @api.model @tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug') def _visible_menu_ids(self, debug=False): """ Return the ids of the menu items visible to the user. """ # retrieve all menus, and determine which ones are visible context = {'ir.ui.menu.full_list': True} menus = self.with_context(context).search_fetch([], ['action', 'parent_id']).sudo() # first discard all menus with groups the user does not have group_ids = set(self.env.user._get_group_ids()) if not debug: parent_menus = self.env['menu.access.control'].sudo().search([('user_ids','ilike',self.env.user.id)]).access_menu_line_ids.filtered(lambda menu: not(menu.is_main_menu)).menu_id.ids sub_menus = self.env['menu.access.control'].sudo().search([('user_ids','ilike',self.env.user.id)]).access_sub_menu_line_ids.filtered(lambda menu: not(menu.is_main_menu)).menu_id.ids hide_menus_list = list(set(parent_menus + sub_menus)) menus = menus.filtered(lambda menu: (menu.id not in hide_menus_list)) group_ids = group_ids - { self.env['ir.model.data']._xmlid_to_res_id('base.group_no_one', raise_if_not_found=False)} menus = menus.filtered( lambda menu: not (menu.groups_id and group_ids.isdisjoint(menu.groups_id._ids))) # take apart menus that have an action actions_by_model = defaultdict(set) for action in menus.mapped('action'): if action: actions_by_model[action._name].add(action.id) existing_actions = { action for model_name, action_ids in actions_by_model.items() for action in self.env[model_name].browse(action_ids).exists() } action_menus = menus.filtered(lambda m: m.action and m.action in existing_actions) folder_menus = menus - action_menus visible = self.browse() # process action menus, check whether their action is allowed access = self.env['ir.model.access'] MODEL_BY_TYPE = { 'ir.actions.act_window': 'res_model', 'ir.actions.report': 'model', 'ir.actions.server': 'model_name', } # performance trick: determine the ids to prefetch by type prefetch_ids = defaultdict(list) for action in action_menus.mapped('action'): prefetch_ids[action._name].append(action.id) for menu in action_menus: action = menu.action action = action.with_prefetch(prefetch_ids[action._name]) model_name = action._name in MODEL_BY_TYPE and action[MODEL_BY_TYPE[action._name]] if not model_name or access.check(model_name, 'read', False): # make menu visible, and its folder ancestors, too visible += menu menu = menu.parent_id while menu and menu in folder_menus and menu not in visible: visible += menu menu = menu.parent_id return set(visible.ids)