summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/hr/data/hr_data.xml38
-rw-r--r--addons/hr/models/mail_alias.py19
-rw-r--r--addons/mail/data/mail_data.xml6
-rw-r--r--addons/mail/models/mail_alias.py51
-rw-r--r--addons/mail/models/mail_channel.py4
-rw-r--r--addons/mail/models/mail_thread.py24
-rw-r--r--addons/mail/views/mail_alias_views.xml1
-rw-r--r--addons/test_mail/tests/test_mail_gateway.py26
-rw-r--r--odoo/tools/mail.py1
9 files changed, 94 insertions, 76 deletions
diff --git a/addons/hr/data/hr_data.xml b/addons/hr/data/hr_data.xml
index 60601583cd1..2e7fca012b7 100644
--- a/addons/hr/data/hr_data.xml
+++ b/addons/hr/data/hr_data.xml
@@ -60,44 +60,6 @@
])]"/>
</record>
- <record id="mail_template_data_unknown_employee_email_address" model="mail.template">
- <field name="name">HR: mailgateway unknown employee bounce</field>
- <field name="model_id" ref="base.model_ir_module_module"/>
- <field name="subject">Your document has not been created</field>
- <field name="email_from">${user.email_formatted | safe}</field>
- <field name="email_to">${ctx['email_to']|safe}</field>
- <field name="body_html" type="html">
-<table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial,sans-serif; color: #454748; width: 100%; border-collapse:separate;"><tr><td align="center">
-<table border="0" cellpadding="0" cellspacing="0" width="590" style="padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
-<tbody>
- <tr>
- <td align="center" style="min-width: 590px;">
- <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
- <tr><td valign="top" style="font-size: 13px;">
- <div>
- Hi,<br/>
- Your document has not been created because your email address is not recognized. Please send emails with the email address recorded on your employee information, or contact your HR manager.
- </div>
- </td></tr>
- </table>
- </td></tr>
-</tbody>
-</table>
-</td></tr>
-<!-- POWERED BY -->
-<tr><td align="center" style="min-width: 590px;">
- <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: #F1F1F1; color: #454748; padding: 8px; border-collapse:separate;">
- <tr><td style="text-align: center; font-size: 13px;">
- Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=hr" style="color: #875A7B;">Odoo</a>
- </td></tr>
- </table>
-</td></tr>
-</table>
- </field>
- <field name="lang">${user.lang}</field>
- <field name="auto_delete" eval="True"/>
- </record>
-
<record model="ir.config_parameter" id="hr_presence_control_login" forcecreate="False">
<field name="key">hr.hr_presence_control_login</field>
<field name="value">True</field>
diff --git a/addons/hr/models/mail_alias.py b/addons/hr/models/mail_alias.py
index c224d21df07..6a363221943 100644
--- a/addons/hr/models/mail_alias.py
+++ b/addons/hr/models/mail_alias.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import fields, models, tools
+from odoo import fields, models, tools, _
class Alias(models.Model):
@@ -9,8 +9,16 @@ class Alias(models.Model):
alias_contact = fields.Selection(selection_add=[('employees', 'Authenticated Employees')])
+ def _get_alias_bounced_body_fallback(self, message_dict):
+ if self.alias_contact == 'employees':
+ return _("""Hi,<br/>
+Your document has not been created because your email address is not recognized.<br/>
+Please send emails with the email address recorded on your employee information, or contact your HR manager.""")
+ else:
+ return super(Alias, self)._get_alias_bounced_body_fallback(message_dict)
-class MailAlias(models.AbstractModel):
+
+class AliasMixin(models.AbstractModel):
_inherit = 'mail.alias.mixin'
def _alias_check_contact_on_record(self, record, message, message_dict, alias):
@@ -21,9 +29,6 @@ class MailAlias(models.AbstractModel):
if not employee:
employee = self.env['hr.employee'].search([('user_id.email', 'ilike', email_address)], limit=1)
if not employee:
- return {
- 'error_message': 'restricted to employees',
- 'error_template': self.env.ref('hr.mail_template_data_unknown_employee_email_address').body_html,
- }
+ return _('restricted to employees')
return True
- return super(MailAlias, self)._alias_check_contact_on_record(record, message, message_dict, alias)
+ return super(AliasMixin, self)._alias_check_contact_on_record(record, message, message_dict, alias)
diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml
index a83cafbbc8d..3e0c6ca6b37 100644
--- a/addons/mail/data/mail_data.xml
+++ b/addons/mail/data/mail_data.xml
@@ -363,6 +363,12 @@
<blockquote><t t-esc="message['body']"/></blockquote>
</template>
+ <!-- Mail bounce alias mail template -->
+ <template id="mail_bounce_alias_security">
+<div><t t-raw="body"/></div>
+<blockquote><t t-raw="message['body']"/></blockquote>
+ </template>
+
<!-- Channel and moderation related data -->
<template id="mail_channel_notify_moderation">
<div>
diff --git a/addons/mail/models/mail_alias.py b/addons/mail/models/mail_alias.py
index 839123896c8..43c2350fdb3 100644
--- a/addons/mail/models/mail_alias.py
+++ b/addons/mail/models/mail_alias.py
@@ -7,7 +7,7 @@ import re
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
-from odoo.tools import remove_accents
+from odoo.tools import remove_accents, is_html_empty
_logger = logging.getLogger(__name__)
@@ -68,6 +68,9 @@ class Alias(models.Model):
"- everyone: everyone can post\n"
"- partners: only authenticated partners\n"
"- followers: only followers of the related document or members of following channels\n")
+ alias_bounced_content = fields.Html(
+ "Custom Bounced Message", translate=True,
+ help="If set, this content will automatically be sent out to unauthorized users instead of the default message.")
_sql_constraints = [
('alias_unique', 'UNIQUE(alias_name)', 'Unfortunately this email alias is already used, please choose a unique one')
@@ -171,6 +174,36 @@ class Alias(models.Model):
'type': 'ir.actions.act_window',
}
+ def _get_alias_bounced_body_fallback(self, message_dict):
+ return _("""Hi,<br/>
+The following email sent to %s cannot be accepted because this is a private email address.
+Only allowed people can contact us at this address.""" % message_dict.get('to'))
+
+ def _get_alias_bounced_body(self, message_dict):
+ """Get the body of the email return in case of bounced email.
+
+ :param message_dict: dictionary of mail values
+ """
+ lang_author = False
+ if message_dict.get('author_id'):
+ try:
+ lang_author = self.env['res.partner'].browse(message_dict['author_id']).lang
+ except:
+ pass
+
+ if lang_author:
+ self = self.with_context(lang=lang_author)
+
+ if not is_html_empty(self.alias_bounced_content):
+ body = self.alias_bounced_content
+ else:
+ body = self._get_alias_bounced_body_fallback(message_dict)
+ template = self.env.ref('mail.mail_bounce_alias_security', raise_if_not_found=True)
+ return template.render({
+ 'body': body,
+ 'message': message_dict
+ }, engine='ir.qweb', minimal_qcontext=True)
+
class AliasMixin(models.AbstractModel):
""" A mixin for models that inherits mail.alias. This mixin initializes the
@@ -255,20 +288,12 @@ class AliasMixin(models.AbstractModel):
author = self.env['res.partner'].browse(message_dict.get('author_id', False))
if alias.alias_contact == 'followers':
if not record.ids:
- return {
- 'error_message': _('incorrectly configured alias (unknown reference record)'),
- }
+ return _('incorrectly configured alias (unknown reference record)')
if not hasattr(record, "message_partner_ids") or not hasattr(record, "message_channel_ids"):
- return {
- 'error_message': _('incorrectly configured alias'),
- }
+ return _('incorrectly configured alias')
accepted_partner_ids = record.message_partner_ids | record.message_channel_ids.mapped('channel_partner_ids')
if not author or author not in accepted_partner_ids:
- return {
- 'error_message': _('restricted to followers'),
- }
+ return _('restricted to followers')
elif alias.alias_contact == 'partners' and not author:
- return {
- 'error_message': _('restricted to known authors')
- }
+ return _('restricted to known authors')
return True
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index 72779279b68..04642b70126 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -383,9 +383,7 @@ class Channel(models.Model):
if alias.alias_contact == 'followers' and self.ids:
author = self.env['res.partner'].browse(message_dict.get('author_id', False))
if not author or author not in self.channel_partner_ids:
- return {
- 'error_message': _('restricted to channel members'),
- }
+ return _('restricted to channel members')
return True
return super(Channel, self)._alias_check_contact(message, message_dict, alias)
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 748cf08ec71..90aebbff672 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -30,7 +30,7 @@ from odoo import _, api, exceptions, fields, models, tools, registry, SUPERUSER_
from odoo.exceptions import MissingError
from odoo.osv import expression
-from odoo.tools import pycompat, ustr
+from odoo.tools import ustr
from odoo.tools.misc import clean_context, split_every
_logger = logging.getLogger(__name__)
@@ -820,7 +820,6 @@ class MailThread(models.AbstractModel):
fallback on a message_new by resetting thread_id
2 - check that message_update exists if thread_id is set; or at least
that message_new exist
- [ - find author_id if udpate_author is set]
3 - if there is an alias, check alias_contact:
'followers' and thread_id:
check on target document that the author is in the followers
@@ -848,12 +847,6 @@ class MailThread(models.AbstractModel):
model, thread_id, alias = route[0], route[1], route[4]
record_set = None
- _generic_bounce_body_html = """<div>
-<p>Hello,</p>
-<p>The following email sent to %s cannot be accepted because this is a private email address.
- Only allowed people can contact us at this address.</p>
-</div><blockquote>%s</blockquote>""" % (message_dict.get('to'), message_dict.get('body'))
-
# Wrong model
if not model:
self._routing_warn(_('target model unspecified'), message_id, route, raise_exception)
@@ -877,7 +870,7 @@ class MailThread(models.AbstractModel):
self._routing_warn(_('model %s does not accept document creation') % model, message_id, route, raise_exception)
return ()
- # Update message author if asked. We do it now because we need it for aliases (contact settings)
+ # Update message author. We do it now because we need it for aliases (contact settings)
if not author_id:
if record_set:
authors = self._mail_find_partner_from_emails([email_from], records=record_set)
@@ -898,12 +891,13 @@ class MailThread(models.AbstractModel):
else:
obj = self.env[model]
if hasattr(obj, '_alias_check_contact'):
- check_result = obj._alias_check_contact(message, message_dict, alias)
+ error_code = obj._alias_check_contact(message, message_dict, alias)
else:
- check_result = self.env['mail.alias.mixin']._alias_check_contact_on_record(obj, message, message_dict, alias)
- if check_result is not True:
- self._routing_warn(_('alias %s: %s') % (alias.alias_name, check_result.get('error_message', _('unknown error'))), message_id, route, False)
- self._routing_create_bounce_email(email_from, check_result.get('error_template', _generic_bounce_body_html), message)
+ error_code = self.env['mail.alias.mixin']._alias_check_contact_on_record(obj, message, message_dict, alias)
+ if error_code is not True:
+ self._routing_warn(_('alias %s: %s') % (alias.alias_name, error_code or _('unknown error')), message_id, route, False)
+ body = alias._get_alias_bounced_body(message_dict)
+ self._routing_create_bounce_email(email_from, body, message, references=message_id)
return False
return (model, thread_id, route[2], route[3], route[4])
@@ -1055,7 +1049,7 @@ class MailThread(models.AbstractModel):
body = self.env.ref('mail.mail_bounce_catchall').render({
'message': message,
}, engine='ir.qweb')
- self._routing_create_bounce_email(email_from, body, message, reply_to=self.env.company.email)
+ self._routing_create_bounce_email(email_from, body, message, references=message_id, reply_to=self.env.company.email)
return []
dest_aliases = self.env['mail.alias'].search([('alias_name', 'in', rcpt_tos_valid_localparts)])
diff --git a/addons/mail/views/mail_alias_views.xml b/addons/mail/views/mail_alias_views.xml
index 5bfe53265b4..3584bac53e7 100644
--- a/addons/mail/views/mail_alias_views.xml
+++ b/addons/mail/views/mail_alias_views.xml
@@ -26,6 +26,7 @@
<field name="alias_user_id"/>
<field name="alias_parent_model_id"/>
<field name="alias_parent_thread_id"/>
+ <field name="alias_bounced_content" attrs="{'invisible':[('alias_contact', '=', 'everyone')]}"/>
</group>
</sheet>
</form>
diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py
index 698ba38264a..5a63940756b 100644
--- a/addons/test_mail/tests/test_mail_gateway.py
+++ b/addons/test_mail/tests/test_mail_gateway.py
@@ -235,6 +235,32 @@ class TestMailgateway(TestMailCommon):
# Alias configuration
# --------------------------------------------------
+ @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_message_process_alias_config_bounced_content(self):
+ """ Custom bounced message for the alias => Received this custom message """
+ self.alias.write({
+ 'alias_contact': 'partners',
+ 'alias_bounced_content': '<p>What Is Dead May Never Die</p>'
+ })
+
+ # Test: custom bounced content
+ with self.mock_mail_gateway():
+ record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce')
+ self.assertFalse(record, 'message_process: should have bounced')
+ self.assertSentEmail('"MAILER-DAEMON" <bounce.test@test.com>', ['whatever-2a840@postmaster.twitter.com'], body_content='<p>What Is Dead May Never Die</p>')
+
+ self.alias.write({
+ 'alias_contact': 'partners',
+ 'alias_bounced_content': '<p></br></p>'
+ })
+
+ # Test: with "empty" bounced content (simulate view, putting always '<p></br></p>' in html field)
+ with self.mock_mail_gateway():
+ record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce')
+ self.assertFalse(record, 'message_process: should have bounced')
+ # Check if default (hardcoded) value is in the mail content
+ self.assertSentEmail('"MAILER-DAEMON" <bounce.test@test.com>', ['whatever-2a840@postmaster.twitter.com'], body_content='The following email sent to')
+
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models')
def test_message_process_alias_user_id(self):
""" Test alias ownership """
diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py
index b6640da1240..21756fe0640 100644
--- a/odoo/tools/mail.py
+++ b/odoo/tools/mail.py
@@ -254,6 +254,7 @@ def html_sanitize(src, silent=True, sanitize_tags=True, sanitize_attributes=Fals
# HTML/Text management
#----------------------------------------------------------
+
def is_html_empty(html_content):
"""Check if a html content is empty. If there are only formatting tags or
a void content return True. Famous use case if a '<p><br></p>' added by