from collections import defaultdict from odoo import api, models, tools from odoo.http import request class IrUiMenu(models.Model): _inherit = 'ir.ui.menu' def _get_active_master_code(self): return (request.session.get('active_master') if request else False) or self.env.context.get('active_master') @api.model @tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug', 'self.env.uid', 'self._get_active_master_code() or ""') def _visible_menu_ids(self, debug=False): context = {'ir.ui.menu.full_list': True} menus = self.with_context(context).search_fetch([], ['action', 'parent_id']).sudo() active_master_code = self._get_active_master_code() master_control = self.env['master.control'].sudo().search([('code', '=', active_master_code)], limit=1) if active_master_code else False group_ids = set(self.env.user._get_group_ids()) if not debug: 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)) ) if master_control: if master_control.user_ids and self.env.user not in master_control.user_ids: menus = self.browse() else: hidden_menu_ids = self._get_master_hidden_menu_ids(master_control) | self._get_hidden_menu_ids(master_control) if hidden_menu_ids: menus = menus.filtered(lambda menu: menu.id not in hidden_menu_ids) visible = self._process_action_menus(menus) return set(visible.ids) @api.model def load_menus_root(self): root = super().load_menus_root() visible_ids = self._visible_menu_ids(request.session.debug if request else False) root['children'] = [ child for child in root.get('children', []) if child['id'] in visible_ids ] root['all_menu_ids'] = [ menu_id for menu_id in root.get('all_menu_ids', []) if menu_id in visible_ids ] return root @api.model def load_menus(self, debug): all_menus = super().load_menus(debug) visible_ids = self._visible_menu_ids(debug) filtered_menus = {'root': dict(all_menus['root'])} for menu_id, menu_data in all_menus.items(): if menu_id == 'root': continue if menu_id in visible_ids: filtered_menus[menu_id] = dict(menu_data) filtered_menus['root']['children'] = [ child_id for child_id in filtered_menus['root'].get('children', []) if child_id in filtered_menus ] for menu_id, menu_data in list(filtered_menus.items()): if menu_id == 'root': continue menu_data['children'] = [ child_id for child_id in menu_data.get('children', []) if child_id in filtered_menus ] return filtered_menus def _get_hidden_menu_ids(self, master_control): access_controls = self.env['menu.access.control'].sudo().search([ ('master_control_id', '=', master_control.id), ('user_ids', 'in', self.env.user.id), ]) hidden_lines = (access_controls.access_menu_line_ids | access_controls.access_sub_menu_line_ids).filtered( lambda line: not line.show_menu ) hidden_menu_ids = hidden_lines.mapped('menu_id').ids if not hidden_menu_ids: return set() return set( self.env['ir.ui.menu'] .sudo() .with_context({'ir.ui.menu.full_list': True}) .search([('id', 'child_of', hidden_menu_ids)]).ids ) def _get_master_hidden_menu_ids(self, master_control): hidden_lines = (master_control.menu_line_ids | master_control.submenu_line_ids).filtered( lambda line: not line.show_menu ) hidden_menu_ids = hidden_lines.mapped('menu_id').ids if not hidden_menu_ids: return set() return set( self.env['ir.ui.menu'] .sudo() .with_context({'ir.ui.menu.full_list': True}) .search([('id', 'child_of', hidden_menu_ids)]).ids ) def _process_action_menus(self, menus): 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 menu: menu.action and menu.action in existing_actions) folder_menus = menus - action_menus visible = self.browse() access_model = self.env['ir.model.access'] model_by_type = { 'ir.actions.act_window': 'res_model', 'ir.actions.report': 'model', 'ir.actions.server': 'model_name', } 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.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_model.check(model_name, 'read', False): visible += menu parent = menu.parent_id while parent and parent in folder_menus and parent not in visible: visible += parent parent = parent.parent_id return visible