Skip to content

Commit 56a424c

Browse files
committed
Merge PR #735 into 16.0
Signed-off-by sebastienbeau
2 parents 1433907 + 7e18e52 commit 56a424c

19 files changed

Lines changed: 1249 additions & 0 deletions

File tree

cross_connect_client/README.rst

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
====================
2+
Cross Connect Client
3+
====================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:f10ceaed1b91df49c3a9b4e8acdef3d412d7ce50105f6f1ca752630fc1559d8e
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github
20+
:target: https://github.com/OCA/server-auth/tree/16.0/cross_connect_client
21+
:alt: OCA/server-auth
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-cross_connect_client
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-auth&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module allows this odoo instance users to connect directly on
32+
another odoo instance where the module ``cross_connect_server`` is
33+
installed.
34+
35+
**Table of contents**
36+
37+
.. contents::
38+
:local:
39+
40+
Usage
41+
=====
42+
43+
First of all after installing the module, you need to configure the
44+
server connection.
45+
46+
In order to do that, you need to go to the menu
47+
``Settings > Technical > Cross Connect > Cross Connect Servers`` and
48+
create a new server to connect to.
49+
50+
Fill the fields with the server's information :
51+
52+
- Url: The api root path (e.g. ``https://my-remote-odoo.com/api``)
53+
- Api Key: The api-key from the ``cross_connect_server`` configuration
54+
55+
Then click on the ``Sync Cross Connection`` button to check if the
56+
connection is working and to sync the remote server's groups.
57+
58+
After that, you will have to affect the remote groups to the local users
59+
in order for them to be able to connect to the remote server.
60+
61+
Once an user has a remote group, a new top level menu will appear in the
62+
menu bar with the Cross Connect Server's name. Clicking on it will
63+
redirect the user to the remote server logged in as the user.
64+
65+
You can change each menu icon (for use with ``web_responsive`` for
66+
instance) by setting the ``Web Icon Data`` in the server configuration.
67+
68+
Bug Tracker
69+
===========
70+
71+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-auth/issues>`_.
72+
In case of trouble, please check there if your issue has already been reported.
73+
If you spotted it first, help us to smash it by providing a detailed and welcomed
74+
`feedback <https://github.com/OCA/server-auth/issues/new?body=module:%20cross_connect_client%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
75+
76+
Do not contact contributors directly about support or help with technical issues.
77+
78+
Credits
79+
=======
80+
81+
Authors
82+
-------
83+
84+
* Akretion
85+
86+
Contributors
87+
------------
88+
89+
- Florian Mounier florian.mounier@akretion.com
90+
91+
Maintainers
92+
-----------
93+
94+
This module is maintained by the OCA.
95+
96+
.. image:: https://odoo-community.org/logo.png
97+
:alt: Odoo Community Association
98+
:target: https://odoo-community.org
99+
100+
OCA, or the Odoo Community Association, is a nonprofit organization whose
101+
mission is to support the collaborative development of Odoo features and
102+
promote its widespread use.
103+
104+
.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px
105+
:target: https://github.com/paradoxxxzero
106+
:alt: paradoxxxzero
107+
108+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
109+
110+
|maintainer-paradoxxxzero|
111+
112+
This module is part of the `OCA/server-auth <https://github.com/OCA/server-auth/tree/16.0/cross_connect_client>`_ project on GitHub.
113+
114+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

cross_connect_client/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import controllers
2+
from . import models
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
{
6+
"name": "Cross Connect Client",
7+
"version": "16.0.1.0.0",
8+
"author": "Akretion, Odoo Community Association (OCA)",
9+
"summary": "Cross Connect Client allows to connect to a "
10+
"Cross Connect Server enabled odoo instance.",
11+
"category": "Tools",
12+
"depends": ["server_environment"],
13+
"website": "https://github.com/OCA/server-auth",
14+
"data": [
15+
"security/ir_model_access.xml",
16+
"views/cross_connect_server_views.xml",
17+
],
18+
"maintainers": ["paradoxxxzero"],
19+
"demo": [],
20+
"installable": True,
21+
"license": "AGPL-3",
22+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import cross_connect
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
from odoo import _
5+
from odoo.exceptions import UserError
6+
from odoo.http import Controller, request, route
7+
8+
9+
class CrossConnectController(Controller):
10+
@route(
11+
["/cross_connect_server/<int:server_id>"],
12+
methods=["GET"],
13+
type="http",
14+
auth="public",
15+
)
16+
def cross_connect(
17+
self,
18+
server_id,
19+
**params,
20+
):
21+
server = request.env["cross.connect.server"].sudo().browse(server_id)
22+
if not server:
23+
raise UserError(_("Server not found"))
24+
25+
url = server._get_cross_connect_url(request.params.get("redirect_url"))
26+
return request.redirect(url, local=False)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import cross_connect_server
2+
from . import res_groups
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
import requests
6+
7+
from odoo import _, api, fields, models
8+
from odoo.exceptions import UserError
9+
10+
11+
class CrossConnectServer(models.Model):
12+
_name = "cross.connect.server"
13+
_description = "Cross Connect Server"
14+
_inherit = "server.env.mixin"
15+
16+
name = fields.Char(
17+
required=True,
18+
help="This name will be used for the new created app",
19+
)
20+
server_url = fields.Char(required=True)
21+
api_key = fields.Char(
22+
required=True,
23+
)
24+
group_ids = fields.One2many(
25+
"res.groups",
26+
inverse_name="cross_connect_server_id",
27+
string="Cross Connect Server Groups",
28+
readonly=True,
29+
)
30+
menu_id = fields.Many2one(
31+
"ir.ui.menu",
32+
string="Menu",
33+
help="Menu to display the Cross Connect Server in the menu",
34+
compute="_compute_menu_id",
35+
store=True,
36+
)
37+
web_icon_data = fields.Binary(
38+
compute="_compute_web_icon_data", inverse="_inverse_web_icon_data"
39+
)
40+
41+
@api.depends("name", "group_ids")
42+
def _compute_menu_id(self):
43+
for record in self:
44+
if not record.group_ids:
45+
if record.menu_id:
46+
if record.menu_id.action:
47+
record.menu_id.action.unlink()
48+
record.menu_id.unlink()
49+
record.menu_id = False
50+
continue
51+
52+
menu_groups = record.group_ids
53+
54+
if not record.menu_id:
55+
action = self.env["ir.actions.act_url"].create(
56+
{
57+
"name": record.name,
58+
"url": f"/cross_connect_server/{record.id}",
59+
"target": "new",
60+
}
61+
)
62+
63+
record.menu_id = self.env["ir.ui.menu"].create(
64+
{
65+
"name": record.name,
66+
"action": f"ir.actions.act_url,{action.id}", # noqa
67+
"web_icon": "cross_connect_client,static/description/web_icon_data.png",
68+
"groups_id": [(6, 0, menu_groups.ids)],
69+
"sequence": 100,
70+
}
71+
)
72+
else:
73+
record.menu_id.name = record.name
74+
record.menu_id.groups_id = [(6, 0, menu_groups.ids)]
75+
76+
@api.depends("menu_id")
77+
def _compute_web_icon_data(self):
78+
for record in self:
79+
record.web_icon_data = record.menu_id.web_icon_data
80+
81+
def _inverse_web_icon_data(self):
82+
for record in self:
83+
record.menu_id.web_icon_data = record.web_icon_data
84+
85+
def _absolute_url_for(self, path):
86+
return f"{self.server_url.rstrip('/')}/cross_connect/{path.lstrip('/')}"
87+
88+
def _request(self, method, url, headers=None, data=None):
89+
headers = headers or {}
90+
headers["api-key"] = self.api_key
91+
response = requests.request(
92+
method,
93+
self._absolute_url_for(url),
94+
headers=headers,
95+
json=data,
96+
timeout=10,
97+
)
98+
response.raise_for_status()
99+
return response.json()
100+
101+
def _get_cross_connect_url(self, redirect_url=None):
102+
self.ensure_one()
103+
groups = self.env.user.groups_id & self.group_ids
104+
if not groups:
105+
raise UserError(_("You are not allowed to access this server"))
106+
107+
if not self.env.user.email:
108+
raise UserError(_("User email is required"))
109+
110+
data = {
111+
"id": self.env.user.id,
112+
"name": self.env.user.name,
113+
"login": self.env.user.login,
114+
"email": self.env.user.email,
115+
"lang": self.env.user.lang,
116+
"groups": [group.cross_connect_server_group_id for group in groups],
117+
}
118+
if redirect_url:
119+
data["redirect_url"] = redirect_url
120+
121+
response = self._request("POST", "/access", data=data)
122+
client_id = response.get("client_id")
123+
token = response.get("token")
124+
if not token:
125+
raise UserError(_("Missing token"))
126+
127+
return self._absolute_url_for(f"login/{client_id}/{token}")
128+
129+
def _sync_groups(self):
130+
self.ensure_one()
131+
response = self._request("GET", "/sync")
132+
remote_groups = response.get("groups", [])
133+
# Removing groups that are not on the remote server
134+
remote_groups_ids = {remote_group["id"] for remote_group in remote_groups}
135+
self.group_ids.filtered(
136+
lambda group: group.cross_connect_server_group_id not in remote_groups_ids
137+
).write({"cross_connect_server_id": False})
138+
139+
# Create or Update existing groups
140+
for remote_group in remote_groups:
141+
existing_group = self.env["res.groups"].search(
142+
[("cross_connect_server_group_id", "=", remote_group["id"])]
143+
)
144+
if existing_group and not existing_group.cross_connect_server_id:
145+
existing_group.write({"cross_connect_server_id": self.id})
146+
if existing_group:
147+
existing_group.sudo().write(
148+
{
149+
"name": f"{self.name}: {remote_group['name']}",
150+
"comment": remote_group["comment"],
151+
}
152+
)
153+
else:
154+
self.env["res.groups"].sudo().create(
155+
{
156+
"cross_connect_server_id": self.id,
157+
"cross_connect_server_group_id": remote_group["id"],
158+
"name": f"{self.name}: {remote_group['name']}",
159+
"comment": remote_group["comment"],
160+
}
161+
)
162+
163+
def action_sync(self):
164+
for record in self:
165+
record._sync_groups()
166+
167+
def action_disable(self):
168+
for record in self:
169+
record.group_ids.write({"cross_connect_server_id": False})
170+
171+
@property
172+
def _server_env_fields(self):
173+
return {"api_key": {}}
174+
175+
def unlink(self):
176+
for rec in self:
177+
# deleting the groups will delete the menu and related action.
178+
rec.group_ids.unlink()
179+
return super().unlink()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from odoo import fields, models
6+
7+
8+
class ResGroups(models.Model):
9+
_inherit = "res.groups"
10+
11+
cross_connect_server_id = fields.Many2one(
12+
"cross.connect.server", string="Originating Cross Connect Server"
13+
)
14+
cross_connect_server_group_id = fields.Integer(
15+
string="Originating Cross Connect Server Group ID"
16+
)
17+
18+
_sql_constraints = [
19+
(
20+
"cross_connect_server_group_id_cross_connect_server_id_unique",
21+
"unique (cross_connect_server_group_id, cross_connect_server_id)",
22+
"Cross Connect Server Group ID must be unique per Cross Connect Server",
23+
)
24+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Florian Mounier <florian.mounier@akretion.com>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This module allows this odoo instance users to connect directly on another odoo instance
2+
where the module `cross_connect_server` is installed.

0 commit comments

Comments
 (0)