Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions base_user_role_menu/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
===================
Base User Role Menu
===================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:22bad68d1ff4d67af1c3a3944b67fe6354602b370e7a3a26b1d02a210e947cbf
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github
:target: https://github.com/OCA/server-backend/tree/14.0/base_user_role_menu
:alt: OCA/server-backend
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-backend-14-0/server-backend-14-0-base_user_role_menu
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-backend&target_branch=14.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Lists all menus with roles, and adds a smart button with accessible menu list directly on each role form.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-backend/issues/new?body=module:%20base_user_role_menu%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Akretion

Contributors
~~~~~~~~~~~~

* Kévin Roche <kevin.roche@akretion.com>

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-Kev-Roche| image:: https://github.com/Kev-Roche.png?size=40px
:target: https://github.com/Kev-Roche
:alt: Kev-Roche

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-Kev-Roche|

This module is part of the `OCA/server-backend <https://github.com/OCA/server-backend/tree/14.0/base_user_role_menu>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions base_user_role_menu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
23 changes: 23 additions & 0 deletions base_user_role_menu/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Base User Role Menu",
"summary": "List roles required to access each menu item",
"version": "14.0.1.0.0",
"category": "security",
"website": "https://github.com/OCA/server-backend",
"author": "Akretion, Odoo Community Association (OCA)",
"license": "AGPL-3",
"maintainers": ["Kev-Roche"],
"application": False,
"installable": True,
"depends": [
"base_user_role",
],
"data": [
"views/ir_ui_menu.xml",
"views/res_users_role.xml",
],
}
2 changes: 2 additions & 0 deletions base_user_role_menu/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import ir_ui_menu
from . import res_users_role
72 changes: 72 additions & 0 deletions base_user_role_menu/models/ir_ui_menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class IrUiMenu(models.Model):
_inherit = "ir.ui.menu"

role_ids = fields.Many2many(
comodel_name="res.users.role",
string="Roles with Access",
compute="_compute_role_ids",
search="_search_role_ids",
)
has_no_role = fields.Boolean(
string="No Role Restriction",
compute="_compute_role_ids",
search="_search_has_no_role",
)

def _compute_role_ids(self):
all_roles = self.env["res.users.role"].search([])
group_to_roles = {}
for role in all_roles:
implied_groups = role.group_id | role.group_id.trans_implied_ids
for grp in implied_groups:
group_to_roles.setdefault(grp.id, self.env["res.users.role"])
group_to_roles[grp.id] |= role

for menu in self:
if not menu.groups_id:
menu.role_ids = self.env["res.users.role"]
menu.has_no_role = True
continue

roles = self.env["res.users.role"]
for grp in menu.groups_id:
roles |= group_to_roles.get(grp.id, self.env["res.users.role"])

menu.role_ids = roles
menu.has_no_role = not bool(roles)

def _search_role_ids(self, operator, value):
if operator not in ("in", "not in", "=", "!="):
return []

roles = self.env["res.users.role"]
if isinstance(value, (list, tuple)):
roles = roles.browse(value)
elif isinstance(value, int):
roles = roles.browse([value])

group_ids = []
for role in roles:
implied = role.group_id | role.group_id.trans_implied_ids
group_ids.extend(implied.ids)
menus = self.env["ir.ui.menu"].search([("groups_id", "in", group_ids)])
if operator in ("in", "="):
return [("id", "in", menus.ids)]
else:
return [("id", "not in", menus.ids)]

def _search_has_no_role(self, operator, value):
if operator not in ("=", "!="):
return []
menus_with_roles = self.env["ir.ui.menu"].search([("groups_id", "!=", False)])
if (operator == "=" and value) or (operator == "!=" and not value):
return [("id", "not in", menus_with_roles.ids)]
else:
return [("id", "in", menus_with_roles.ids)]
41 changes: 41 additions & 0 deletions base_user_role_menu/models/res_users_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ResUsersRole(models.Model):
_inherit = "res.users.role"

menu_ids = fields.Many2many(
comodel_name="ir.ui.menu",
string="Accessible Menus",
compute="_compute_menu_ids",
help="All menus accessible through this role.",
)
menu_count = fields.Integer(
string="Menu Count",
compute="_compute_menu_ids",
)

def _compute_menu_ids(self):
for role in self:
implied_groups = role.group_id | role.group_id.trans_implied_ids
menus = self.env["ir.ui.menu"].search([])
filtered_menus = menus.filtered(
lambda menu: any(group in menu.groups_id for group in implied_groups)
)
role.menu_ids = filtered_menus
role.menu_count = len(filtered_menus)

def action_view_menus(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"name": "Menus — %s" % self.name,
"res_model": "ir.ui.menu",
"view_mode": "tree,form",
"domain": [("id", "in", self.menu_ids.ids)],
"context": {"default_role_id": self.id},
}
1 change: 1 addition & 0 deletions base_user_role_menu/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Kévin Roche <kevin.roche@akretion.com>
1 change: 1 addition & 0 deletions base_user_role_menu/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Lists all menus with roles, and adds a smart button with accessible menu list directly on each role form.
Loading
Loading