Odoo18-Base/addons/mail/models/ir_ui_menu.py
2025-01-06 10:57:38 +07:00

63 lines
3.1 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import contextlib
from odoo import api, models
from odoo.exceptions import AccessError
class IrUiMenu(models.Model):
_inherit = 'ir.ui.menu'
@api.model
def _get_best_backend_root_menu_id_for_model(self, res_model):
"""Get the best menu root id for the given res_model and the access
rights of the user.
When a link to a model was sent to a user it was targeting a page without
menu, so it was hard for the user to act on it.
The goal of this method is to find the best suited menu to display on a
page of a given model.
Technically, the method tries to find a menu root which has a sub menu
visible to the user that has an action linked to the given model.
If there is more than one possibility, it chooses the preferred one based
on the following preference function that determine the sub-menu from which
the root menu is extracted:
- favor the sub-menu linked to an action having a path as it probably indicates
a "major" action
- then favor the sub-menu with the smallest menu id as it probably indicates
that it belongs to the main module of the model and not a sub-one.
:param str res_model: the model name for which we want to find the best
menu root id
:return (int): the best menu root id or None if not found
"""
with contextlib.suppress(AccessError): # if no access to the menu, return None
visible_menu_ids = self._visible_menu_ids()
# Try first to get a menu root from the model implementation (take the less specialized i.e. the first one)
menu_root_candidates = self.env[res_model]._get_backend_root_menu_ids()
menu_root_id = next((m_id for m_id in menu_root_candidates if m_id in visible_menu_ids), None)
if menu_root_id:
return menu_root_id
# No menu root could be found by interrogating the model so fall back to a simple heuristic
# Prefetch menu fields and all menu's actions of type act_window
menus = self.env['ir.ui.menu'].browse(visible_menu_ids)
self.env['ir.actions.act_window'].sudo().browse([
int(menu['action'].split(',')[1])
for menu in menus.read(['action', 'parent_path'])
if menu['action'] and menu['action'].startswith('ir.actions.act_window,')
]).filtered('res_model')
def _menu_sort_key(menu_action):
menu, action = menu_action
return 1 if action.path else 0, -menu.id
menu_sudo = max((
(menu, action) for menu in menus.sudo() for action in (menu.action,)
if action and action.type == 'ir.actions.act_window' and action.res_model == res_model
and all(int(menu_id) in visible_menu_ids for menu_id in menu.parent_path.split('/') if menu_id)
), key=_menu_sort_key, default=(None, None))[0]
return int(menu_sudo.parent_path[:menu_sudo.parent_path.index('/')]) if menu_sudo else None