Skip to content

Commit 8234f86

Browse files
committed
[IMP]mail_ux: Test email server connection from the UI
1 parent f5a5ffc commit 8234f86

9 files changed

Lines changed: 188 additions & 1 deletion

File tree

mail_ux/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
# directory
44
##############################################################################
55
from . import models
6+
from . import wizard

mail_ux/__manifest__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
"mail",
3737
],
3838
"data": [
39+
"security/ir.model.access.csv",
40+
"wizard/mail_server_test_wizard_views.xml",
41+
"views/ir_mail_server_views.xml",
3942
"views/res_users_views.xml",
4043
],
4144
"demo": [],

mail_ux/models/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# For copyright and license notices, see __manifest__.py file in module root
33
# directory
44
##############################################################################
5+
from . import ir_http
6+
from . import ir_mail_server
57
from . import mail_compose_message
68
from . import res_users
7-
from . import ir_http

mail_ux/models/ir_mail_server.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
##############################################################################
2+
# For copyright and license notices, see __manifest__.py file in module root
3+
# directory
4+
##############################################################################
5+
from odoo import _, models
6+
7+
8+
class IrMailServer(models.Model):
9+
_inherit = "ir.mail_server"
10+
11+
def action_send_test_mail(self):
12+
"""Test the SMTP connection and, if successful, open the test mail wizard.
13+
14+
Raises the native UserError if the connection test fails so the user
15+
sees the standard Odoo connection-error message.
16+
"""
17+
self.ensure_one()
18+
# Test connection first; any UserError propagates natively to the UI
19+
self.test_smtp_connection()
20+
return {
21+
"type": "ir.actions.act_window",
22+
"name": _("Enviar mail de prueba"),
23+
"res_model": "mail.server.test.wizard",
24+
"view_mode": "form",
25+
"target": "new",
26+
"context": {
27+
"default_mail_server_id": self.id,
28+
},
29+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
access_mail_server_test_wizard_user,mail.server.test.wizard user,model_mail_server_test_wizard,base.group_user,1,1,1,1
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo>
3+
<record id="ir_mail_server_form_send_test" model="ir.ui.view">
4+
<field name="name">ir.mail_server.form.send.test</field>
5+
<field name="model">ir.mail_server</field>
6+
<field name="inherit_id" ref="base.ir_mail_server_form" />
7+
<field name="arch" type="xml">
8+
<button name="test_smtp_connection" position="after">
9+
<button
10+
string="Enviar mail de prueba"
11+
type="object"
12+
name="action_send_test_mail"
13+
class="btn-secondary"
14+
/>
15+
</button>
16+
</field>
17+
</record>
18+
</odoo>

mail_ux/wizard/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
##############################################################################
2+
# For copyright and license notices, see __manifest__.py file in module root
3+
# directory
4+
##############################################################################
5+
from . import mail_server_test_wizard
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import smtplib
2+
import ssl
3+
from socket import gaierror
4+
5+
from odoo import _, fields, models
6+
from odoo.exceptions import UserError
7+
8+
9+
class MailServerTestWizard(models.TransientModel):
10+
_name = "mail.server.test.wizard"
11+
_description = "Wizard de prueba de servidor de correo saliente"
12+
13+
# Stored as Integer to reliably reference servers that may be archived
14+
# (neutralized databases deactivate all ir.mail_server records).
15+
mail_server_id = fields.Integer(
16+
string="ID del servidor de correo saliente",
17+
required=True,
18+
)
19+
email_to = fields.Char(
20+
string="Correo destinatario",
21+
required=True,
22+
)
23+
24+
def action_send_test_mail(self):
25+
"""Build and send a test email directly via SMTP, bypassing all
26+
Python-level sending guards (server_mode, mail neutralization, etc.).
27+
28+
The bypass is achieved by calling ``ir.mail_server.connect()`` with
29+
``allow_archived=True`` and then invoking ``smtp.send_message()``
30+
directly — never going through ``send_email()``, which is the layer
31+
where restrictions such as ``server_mode.allow_send_mail`` operate.
32+
"""
33+
self.ensure_one()
34+
IrMailServer = self.env["ir.mail_server"]
35+
36+
# Browse with active_test=False so archived servers (neutralized DBs)
37+
# are accessible.
38+
mail_server = IrMailServer.sudo().with_context(active_test=False).browse(self.mail_server_id)
39+
if not mail_server._ids:
40+
raise UserError(
41+
_("No se encontró el servidor de correo. " "Por favor, recargue la página e intente de nuevo.")
42+
)
43+
44+
email_from = mail_server._get_test_email_from()
45+
message = IrMailServer.build_email(
46+
email_from=email_from,
47+
email_to=[self.email_to],
48+
subject=_("Prueba de mail desde Odoo"),
49+
body=_("Mail de prueba enviado desde mi base de Odoo. " "Por favor no responder."),
50+
subtype="plain",
51+
)
52+
53+
smtp = None
54+
try:
55+
# allow_archived=True is critical for neutralized databases where
56+
# all real servers are deactivated by the neutralize process.
57+
smtp = IrMailServer.connect(
58+
mail_server_id=mail_server.id,
59+
allow_archived=True,
60+
)
61+
if smtp:
62+
smtp.send_message(message)
63+
except (gaierror, TimeoutError) as e:
64+
raise UserError(
65+
_(
66+
"No se pudo enviar el mail: Sin respuesta del servidor. " "Verifique la dirección y el puerto.\n%s",
67+
e,
68+
)
69+
) from e
70+
except smtplib.SMTPRecipientsRefused as e:
71+
raise UserError(
72+
_(
73+
"No se pudo enviar el mail: El servidor rechazó la dirección destinataria.\n%s",
74+
e,
75+
)
76+
) from e
77+
except smtplib.SMTPException as e:
78+
raise UserError(_("No se pudo enviar el mail: %s", e)) from e
79+
except ssl.SSLError as e:
80+
raise UserError(_("No se pudo enviar el mail: Error de SSL.\n%s", e)) from e
81+
except Exception as e:
82+
raise UserError(_("No se pudo enviar el mail: %s", e)) from e
83+
finally:
84+
if smtp:
85+
try:
86+
smtp.quit()
87+
except Exception:
88+
pass
89+
90+
return {
91+
"type": "ir.actions.client",
92+
"tag": "display_notification",
93+
"params": {
94+
"message": _("¡Mail de prueba enviado con éxito! " "Por favor, revise su casilla de correo."),
95+
"type": "success",
96+
"sticky": False,
97+
"next": {"type": "ir.actions.act_window_close"},
98+
},
99+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo>
3+
<record id="view_mail_server_test_wizard_form" model="ir.ui.view">
4+
<field name="name">mail.server.test.wizard.form</field>
5+
<field name="model">mail.server.test.wizard</field>
6+
<field name="arch" type="xml">
7+
<form string="Enviar mail de prueba">
8+
<group>
9+
<field name="mail_server_id" invisible="1" />
10+
<field
11+
name="email_to"
12+
placeholder="ej. usuario@empresa.com"
13+
autofocus="autofocus"
14+
/>
15+
</group>
16+
<footer>
17+
<button
18+
string="Enviar prueba"
19+
type="object"
20+
name="action_send_test_mail"
21+
class="btn-primary"
22+
data-hotkey="v"
23+
/>
24+
<button string="Cancelar" special="cancel" data-hotkey="z" />
25+
</footer>
26+
</form>
27+
</field>
28+
</record>
29+
</odoo>

0 commit comments

Comments
 (0)