Skip to content
Closed
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
1 change: 1 addition & 0 deletions mail_ux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
# directory
##############################################################################
from . import models
from . import wizard
3 changes: 3 additions & 0 deletions mail_ux/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"mail",
],
"data": [
"security/ir.model.access.csv",
"wizard/mail_server_test_wizard_views.xml",
"views/ir_mail_server_views.xml",
"views/res_users_views.xml",
],
"demo": [],
Expand Down
3 changes: 2 additions & 1 deletion mail_ux/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from . import ir_http
from . import ir_mail_server
from . import mail_compose_message
from . import res_users
from . import ir_http
29 changes: 29 additions & 0 deletions mail_ux/models/ir_mail_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
##############################################################################
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from odoo import _, models


class IrMailServer(models.Model):
_inherit = "ir.mail_server"

def action_send_test_mail(self):
"""Test the SMTP connection and, if successful, open the test mail wizard.

Raises the native UserError if the connection test fails so the user
sees the standard Odoo connection-error message.
"""
self.ensure_one()
# Test connection first; any UserError propagates natively to the UI
self.test_smtp_connection()
return {
"type": "ir.actions.act_window",
"name": _("Enviar mail de prueba"),
"res_model": "mail.server.test.wizard",
"view_mode": "form",
"target": "new",
"context": {
"default_mail_server_id": self.id,
},
}
2 changes: 2 additions & 0 deletions mail_ux/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_server_test_wizard_user,mail.server.test.wizard user,model_mail_server_test_wizard,base.group_system,1,1,1,0
18 changes: 18 additions & 0 deletions mail_ux/views/ir_mail_server_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="ir_mail_server_form_send_test" model="ir.ui.view">
<field name="name">ir.mail_server.form.send.test</field>
<field name="model">ir.mail_server</field>
<field name="inherit_id" ref="base.ir_mail_server_form" />
<field name="arch" type="xml">
<button name="test_smtp_connection" position="after">
<button
string="Enviar mail de prueba"
type="object"
name="action_send_test_mail"
class="btn-secondary"
/>
</button>
</field>
</record>
</odoo>
5 changes: 5 additions & 0 deletions mail_ux/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
##############################################################################
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from . import mail_server_test_wizard
104 changes: 104 additions & 0 deletions mail_ux/wizard/mail_server_test_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import smtplib
import ssl
from socket import gaierror

from odoo import _, fields, models
from odoo.exceptions import UserError


class MailServerTestWizard(models.TransientModel):
_name = "mail.server.test.wizard"
_description = "Wizard de prueba de servidor de correo saliente"

mail_server_id = fields.Many2one(
comodel_name="ir.mail_server",
string="Servidor de correo saliente",
required=True,
context={"active_test": False},
)
email_to = fields.Char(
string="Correo destinatario",
required=True,
)

def action_send_test_mail(self):
"""Build and send a test email directly via SMTP, bypassing all
Python-level sending guards (server_mode, mail neutralization, etc.).

The bypass is achieved by calling ``ir.mail_server.connect()`` with
``allow_archived=True`` and then invoking ``smtp.send_message()``
directly — never going through ``send_email()``, which is the layer
where restrictions such as ``server_mode.allow_send_mail`` operate.
"""
self.ensure_one()
IrMailServer = self.env["ir.mail_server"]

# Verify the user has read access on ir.mail_server before elevating.
IrMailServer.check_access_rights("read")

# Browse with active_test=False so archived servers (neutralized DBs)
# are accessible, but without bypassing ACLs / record rules.
mail_server = IrMailServer.with_context(active_test=False).browse(self.mail_server_id.id)
if not mail_server.exists():
raise UserError(
_("No se encontró el servidor de correo. " "Por favor, recargue la página e intente de nuevo.")
)
Comment thread
JrAdhoc marked this conversation as resolved.
mail_server.check_access_rule("read")
mail_server = mail_server.sudo()

email_from = mail_server._get_test_email_from()
message = IrMailServer.build_email(
email_from=email_from,
email_to=[self.email_to],
subject=_("Prueba de mail desde Odoo"),
body=_("Mail de prueba enviado desde mi base de Odoo. " "Por favor no responder."),
subtype="plain",
)

smtp = None
try:
# allow_archived=True is critical for neutralized databases where
# all real servers are deactivated by the neutralize process.
smtp = IrMailServer.connect(
mail_server_id=mail_server.id,
allow_archived=True,
)
if smtp:
smtp.send_message(message)
except (gaierror, TimeoutError) as e:
raise UserError(
_(
"No se pudo enviar el mail: Sin respuesta del servidor. " "Verifique la dirección y el puerto.\n%s",
e,
)
) from e
except smtplib.SMTPRecipientsRefused as e:
raise UserError(
_(
"No se pudo enviar el mail: El servidor rechazó la dirección destinataria.\n%s",
e,
)
) from e
except smtplib.SMTPException as e:
raise UserError(_("No se pudo enviar el mail: %s", e)) from e
except ssl.SSLError as e:
raise UserError(_("No se pudo enviar el mail: Error de SSL.\n%s", e)) from e
except Exception as e:
raise UserError(_("No se pudo enviar el mail: %s", e)) from e
finally:
if smtp:
try:
smtp.quit()
except Exception:
pass

return {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"message": _("¡Mail de prueba enviado con éxito! " "Por favor, revise su casilla de correo."),
"type": "success",
"sticky": False,
"next": {"type": "ir.actions.act_window_close"},
},
}
29 changes: 29 additions & 0 deletions mail_ux/wizard/mail_server_test_wizard_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_mail_server_test_wizard_form" model="ir.ui.view">
<field name="name">mail.server.test.wizard.form</field>
<field name="model">mail.server.test.wizard</field>
<field name="arch" type="xml">
<form string="Enviar mail de prueba">
<group>
<field name="mail_server_id" invisible="1" />
<field
name="email_to"
placeholder="ej. usuario@empresa.com"
autofocus="autofocus"
/>
</group>
<footer>
<button
string="Enviar prueba"
type="object"
name="action_send_test_mail"
class="btn-primary"
data-hotkey="v"
/>
<button string="Cancelar" special="cancel" data-hotkey="z" />
</footer>
</form>
</field>
</record>
</odoo>
Loading