diff options
| -rw-r--r-- | addons/hr/data/hr_data.xml | 38 | ||||
| -rw-r--r-- | addons/hr/models/mail_alias.py | 19 | ||||
| -rw-r--r-- | addons/mail/data/mail_data.xml | 6 | ||||
| -rw-r--r-- | addons/mail/models/mail_alias.py | 51 | ||||
| -rw-r--r-- | addons/mail/models/mail_channel.py | 4 | ||||
| -rw-r--r-- | addons/mail/models/mail_thread.py | 24 | ||||
| -rw-r--r-- | addons/mail/views/mail_alias_views.xml | 1 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_mail_gateway.py | 26 | ||||
| -rw-r--r-- | odoo/tools/mail.py | 1 |
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&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 |
