ADD mail_outbound_static: Allow configuration of from email header
* Allow for the configuration of the FROM header for outbound emails, and move the existing from header to Sender as per RFC-2822
This commit is contained in:
parent
6ce2c6af42
commit
85d68c4f40
62
mail_outbound_static/README.rst
Normal file
62
mail_outbound_static/README.rst
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
|
||||||
|
:target: http://www.gnu.org/licenses/lgpl.html
|
||||||
|
:alt: License: LGPL-3
|
||||||
|
|
||||||
|
====================
|
||||||
|
Mail Outbound Static
|
||||||
|
====================
|
||||||
|
|
||||||
|
This module brings Odoo outbound emails in to strict compliance with RFC-2822
|
||||||
|
by allowing for a statically configured From header, with the sender's e-mail
|
||||||
|
being appended into the proper Sender header instead.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
* Navigate to an Outbound Email Server
|
||||||
|
* Set the `Email From` option to an email address
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||||
|
:alt: Try me on Runbot
|
||||||
|
:target: https://runbot.odoo-community.org/runbot/205/10.0
|
||||||
|
|
||||||
|
Road Map
|
||||||
|
========
|
||||||
|
|
||||||
|
* Allow for domain-based whitelist that will not be manipulated
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues
|
||||||
|
<https://github.com/OCA/social/issues>`_. In case of trouble, please
|
||||||
|
check there if your issue has already been reported. If you spotted it first,
|
||||||
|
help us smash it by providing detailed and welcomed feedback.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Images
|
||||||
|
------
|
||||||
|
|
||||||
|
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Dave Lasley <dave@laslabs.com>
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: https://odoo-community.org
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
To contribute to this module, please visit https://odoo-community.org.
|
5
mail_outbound_static/__init__.py
Normal file
5
mail_outbound_static/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from . import models
|
21
mail_outbound_static/__manifest__.py
Normal file
21
mail_outbound_static/__manifest__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2016-2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Mail Outbound Static',
|
||||||
|
'summary': 'Allows you to configure the from header for a mail server.',
|
||||||
|
'version': '10.0.1.0.1',
|
||||||
|
'category': 'Discuss',
|
||||||
|
'website': 'https://laslabs.com/',
|
||||||
|
'author': 'LasLabs, Odoo Community Association (OCA)',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'application': False,
|
||||||
|
'installable': True,
|
||||||
|
'depends': [
|
||||||
|
'base',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/ir_mail_server_view.xml',
|
||||||
|
],
|
||||||
|
}
|
5
mail_outbound_static/models/__init__.py
Normal file
5
mail_outbound_static/models/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from . import ir_mail_server
|
50
mail_outbound_static/models/ir_mail_server.py
Normal file
50
mail_outbound_static/models/ir_mail_server.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class IrMailServer(models.Model):
|
||||||
|
|
||||||
|
_inherit = 'ir.mail_server'
|
||||||
|
|
||||||
|
smtp_from = fields.Char(
|
||||||
|
string='Email From',
|
||||||
|
help='Set this in order to email from a specific address.'
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def send_email(self, message, mail_server_id=None, smtp_server=None,
|
||||||
|
*args, **kwargs):
|
||||||
|
|
||||||
|
# Replicate logic from core to get mail server
|
||||||
|
mail_server = None
|
||||||
|
if mail_server_id:
|
||||||
|
mail_server = self.sudo().browse(mail_server_id)
|
||||||
|
elif not smtp_server:
|
||||||
|
mail_server = self.sudo().search([], order='sequence', limit=1)
|
||||||
|
|
||||||
|
if mail_server and mail_server.smtp_from:
|
||||||
|
split_from = message['From'].rsplit(' <', 1)
|
||||||
|
if len(split_from) > 1:
|
||||||
|
email_from = '%s <%s>' % (
|
||||||
|
split_from[0], mail_server.smtp_from,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
email_from = mail_server.smtp_from
|
||||||
|
|
||||||
|
message.replace_header('From', email_from)
|
||||||
|
bounce_alias = self.env['ir.config_parameter'].get_param(
|
||||||
|
"mail.bounce.alias")
|
||||||
|
if not bounce_alias:
|
||||||
|
# then, bounce handling is disabled and we want
|
||||||
|
# Return-Path = From
|
||||||
|
if message.has_key('Return-Path'):
|
||||||
|
message.replace_header('Return-Path', email_from)
|
||||||
|
else:
|
||||||
|
message.add_header('Return-Path', email_from)
|
||||||
|
|
||||||
|
return super(IrMailServer, self).send_email(
|
||||||
|
message, mail_server_id, smtp_server, *args, **kwargs
|
||||||
|
)
|
BIN
mail_outbound_static/static/description/icon.png
Normal file
BIN
mail_outbound_static/static/description/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
5
mail_outbound_static/tests/__init__.py
Normal file
5
mail_outbound_static/tests/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from . import test_ir_mail_server
|
70
mail_outbound_static/tests/test.msg
Normal file
70
mail_outbound_static/tests/test.msg
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
Delivered-To: test@gmail.com
|
||||||
|
Received: by 10.74.138.167 with SMTP id m36csp7226976ooj;
|
||||||
|
Tue, 12 Sep 2017 10:37:56 -0700 (PDT)
|
||||||
|
X-Google-Smtp-Source: AOwi7QDKSb3BE6lIhVXub9wcPA/HxFKKpnNPconNr9f1L35SVw+EIm8itVQkbOdAW6TohImypmrF
|
||||||
|
X-Received: by 10.28.158.208 with SMTP id h199mr250060wme.47.1505237876258;
|
||||||
|
Tue, 12 Sep 2017 10:37:56 -0700 (PDT)
|
||||||
|
ARC-Seal: i=1; a=rsa-sha256; t=1505237876; cv=none;
|
||||||
|
d=google.com; s=arc-20160816;
|
||||||
|
b=E2B6KUxHOJQk1YrT12BpitEMCgkxyqEXcFlwPWKjA/i/Xyvlh+09spNOF4VPmD/ZJm
|
||||||
|
5lkY6hYyxvIH2RpRPeZVPkRIYhaEASkMIygdJu9Gd4weBdO2rd8iP/zSGHYyAmO/hLN2
|
||||||
|
64hXtKexrWnO/YNWlpfhAo1kiwgSRVnZx55EopbWP49cy7BzKfwr1kHN0T9A5Lw1w+BW
|
||||||
|
ZrXdCX6LRxHS2USKHb76PAVt0bhwsM/ZznBauR2zNKYcPxAWzdpN/vK3BDmWUdqbbSaB
|
||||||
|
BOKINjuI9EmWynogDZE7Riu+sbc5QafE3owla1/2d0Bogp9FLtJe0YyQeW2qLvZKcmlI
|
||||||
|
ftSQ==
|
||||||
|
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
|
||||||
|
h=to:list-archive:list-unsubscribe:list-subscribe:precedence
|
||||||
|
:list-post:list-id:date:reply-to:from:subject:references:message-id
|
||||||
|
:mime-version:arc-authentication-results;
|
||||||
|
bh=DwvSiw5K7ryb4S8O/8HcIaGhJqbOxcXKsnPAr63iQZ4=;
|
||||||
|
b=U0Ac9Rqvv+tfqO9fCx+F79oZknn3rOv9N9ekViEuL5DtjpJxKDDkO1xw//sV3eRILT
|
||||||
|
nqGuxd2yQXwC4U+WAwraBwoLC3ScHb/9gWtzlrLCgv6WbNE7HZi5g6L8c0LWRN24cIe9
|
||||||
|
AOdc/8fOdGoaL8yajrGEHgMz9B2KMltA9tZyxFOeKsyODxJ6iWjXcG1BSQTxERwosV3h
|
||||||
|
ch8AznQr7xLLvc/u9VTEqC5ome3RqsxKRxOGenEqIbCOr11sxwpZQdQcNR6faNRom3+2
|
||||||
|
6gz++4tVIV9cqYX1j9eEU/ufoUzBJ6Uzm0jMGZZQOHAF+YX3tZUEsPmc75PsvRCAIWby
|
||||||
|
urMg==
|
||||||
|
ARC-Authentication-Results: i=1; mx.google.com;
|
||||||
|
spf=pass (google.com: domain of postmaster-odoo@odoo-community.org designates 2a01:4f8:a0:430d::2 as permitted sender) smtp.mailfrom=postmaster-odoo@odoo-community.org;
|
||||||
|
dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=QUARANTINE) header.from=laslabs.com
|
||||||
|
Return-Path: <postmaster-odoo@odoo-community.org>
|
||||||
|
Received: from odoo-community.org (odoo-community.org. [2a01:4f8:a0:430d::2])
|
||||||
|
by mx.google.com with ESMTP id j72si1795626wmg.60.2017.09.12.10.37.55
|
||||||
|
for <test@gmail.com>;
|
||||||
|
Tue, 12 Sep 2017 10:37:56 -0700 (PDT)
|
||||||
|
Received-SPF: pass (google.com: domain of postmaster-odoo@odoo-community.org designates 2a01:4f8:a0:430d::2 as permitted sender) client-ip=2a01:4f8:a0:430d::2;
|
||||||
|
Authentication-Results: mx.google.com;
|
||||||
|
spf=pass (google.com: domain of postmaster-odoo@odoo-community.org designates 2a01:4f8:a0:430d::2 as permitted sender) smtp.mailfrom=postmaster-odoo@odoo-community.org;
|
||||||
|
dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=QUARANTINE) header.from=laslabs.com
|
||||||
|
Received: from odoo.odoo-community.org (localhost.localdomain [127.0.0.1])
|
||||||
|
by odoo-community.org (Postfix) with ESMTP id DB5DC2EC2277;
|
||||||
|
Tue, 12 Sep 2017 19:37:53 +0200 (CEST)
|
||||||
|
Content-Type: multipart/mixed; boundary="===============7439524030966430607=="
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Message-Id: <A1AAECD6-2A36-45E1-A0F8-4266F178D52C@laslabs.com>
|
||||||
|
references: <0db43737-b846-4890-6801-44ff9617e3b3@camptocamp.com>
|
||||||
|
Subject: Re: OCA Code sprint: sprint topics
|
||||||
|
From: Dave Lasley <test@laslabs.com>
|
||||||
|
Reply-To: "Odoo Community Association \(OCA\) Contributors"
|
||||||
|
<contributors@odoo-community.org>
|
||||||
|
Date: Tue, 12 Sep 2017 17:37:53 -0000
|
||||||
|
List-Id: contributors.odoo-community.org
|
||||||
|
List-Post: <mailto:contributors@odoo-community.org>
|
||||||
|
Precedence: list
|
||||||
|
X-Auto-Response-Suppress: OOF
|
||||||
|
List-Subscribe: <https://odoo-community.org/groups>
|
||||||
|
List-Unsubscribe: <https://odoo-community.org/groups?unsubscribe>
|
||||||
|
List-Archive: <https://odoo-community.org/groups/contributors-15>
|
||||||
|
To: "Contributors" <contributors@odoo-community.org>
|
||||||
|
|
||||||
|
--===============7439524030966430607==
|
||||||
|
Content-Type: multipart/alternative;
|
||||||
|
boundary="===============8317593469411551167=="
|
||||||
|
MIME-Version: 1.0
|
||||||
|
|
||||||
|
--===============8317593469411551167==
|
||||||
|
Content-Type: text/plain; charset="utf-8"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
|
VGhpcyBpcyBhIGZha2UsIHRlc3QgbWVzc2FnZQ==
|
||||||
|
--===============7439524030966430607==--
|
70
mail_outbound_static/tests/test_ir_mail_server.py
Normal file
70
mail_outbound_static/tests/test_ir_mail_server.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from mock import MagicMock
|
||||||
|
from email import message_from_string
|
||||||
|
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestIrMailServer(TransactionCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestIrMailServer, self).setUp()
|
||||||
|
self.email_from = 'derp@example.com'
|
||||||
|
self.email_from_another = 'another@example.com'
|
||||||
|
self.Model = self.env['ir.mail_server']
|
||||||
|
self.Model.search([]).write({'smtp_from': self.email_from})
|
||||||
|
message_file = os.path.join(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)), 'test.msg',
|
||||||
|
)
|
||||||
|
with open(message_file, 'r') as fh:
|
||||||
|
self.message = message_from_string(fh.read())
|
||||||
|
|
||||||
|
def _send_mail(self, message=None, mail_server_id=None, smtp_server=None):
|
||||||
|
if message is None:
|
||||||
|
message = self.message
|
||||||
|
connect = MagicMock()
|
||||||
|
thread = threading.currentThread()
|
||||||
|
setattr(thread, 'testing', False)
|
||||||
|
try:
|
||||||
|
self.Model._patch_method('connect', connect)
|
||||||
|
try:
|
||||||
|
self.Model.send_email(message, mail_server_id, smtp_server)
|
||||||
|
finally:
|
||||||
|
self.Model._revert_method('connect')
|
||||||
|
finally:
|
||||||
|
setattr(thread, 'testing', True)
|
||||||
|
send_from, send_to, message_string = connect().sendmail.call_args[0]
|
||||||
|
return message_from_string(message_string)
|
||||||
|
|
||||||
|
def test_send_email_injects_from_no_canonical(self):
|
||||||
|
"""It should inject the FROM header correctly when no canonical name.
|
||||||
|
"""
|
||||||
|
self.message.replace_header('From', 'test@example.com')
|
||||||
|
message = self._send_mail()
|
||||||
|
self.assertEqual(message['From'], self.email_from)
|
||||||
|
|
||||||
|
def test_send_email_injects_from_with_canonical(self):
|
||||||
|
"""It should inject the FROM header correctly with a canonical name.
|
||||||
|
|
||||||
|
Note that there is an extra `<` in the canonical name to test for
|
||||||
|
proper handling in the split.
|
||||||
|
"""
|
||||||
|
user = 'Test < User'
|
||||||
|
self.message.replace_header('From', '%s <test@example.com>' % user)
|
||||||
|
message = self._send_mail()
|
||||||
|
self.assertEqual(
|
||||||
|
message['From'],
|
||||||
|
'%s <%s>' % (user, self.email_from),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_send_email_injects_sender(self):
|
||||||
|
"""It should inject the Sender header into the email."""
|
||||||
|
original_from = self.message['From']
|
||||||
|
message = self._send_mail()
|
||||||
|
self.assertEqual(message['Sender'], original_from)
|
21
mail_outbound_static/views/ir_mail_server_view.xml
Normal file
21
mail_outbound_static/views/ir_mail_server_view.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright 2017 LasLabs Inc.
|
||||||
|
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="ir_mail_server_form" model="ir.ui.view">
|
||||||
|
<field name="name">IR Mail Server - From Address</field>
|
||||||
|
<field name="model">ir.mail_server</field>
|
||||||
|
<field name="inherit_id" ref="base.ir_mail_server_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='smtp_pass']" position="after">
|
||||||
|
<field name="smtp_from" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
Loading…
Reference in New Issue
Block a user