Skip to content

Commit 1f27a2c

Browse files
committed
Merge PR #782 into 18.0
Signed-off-by bguillot
2 parents 530ca74 + 0ed37ec commit 1f27a2c

File tree

18 files changed

+1247
-0
lines changed

18 files changed

+1247
-0
lines changed

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

0 commit comments

Comments
 (0)