Skip to content

Commit

Permalink
[IMP] mass_mailing: add unsubscribe headers in mailing emails
Browse files Browse the repository at this point in the history
Globally a backport of odoo/odoo@c0775de where mail sent through
mass mailing now have unsubscribe headers set. This requires a small update
in the send method to allow email-specific headers; previously to this commit
only generic headers from the mail.mail were considered.

With some fixes, notably csrf is disabled on unsubscribe route as it is now
called directly as one-click, without form and csrf. This is necessary notably
to allow famous email readers to include links directly in their UI.

Task-3932001 (Mail: Unsubscribe Headers Everywhere)
  • Loading branch information
tde-banana-odoo authored and benwillig committed May 29, 2024
1 parent 4aa2355 commit 1ce0139
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 7 deletions.
12 changes: 11 additions & 1 deletion addons/mail/models/mail_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,16 @@ def _send(self, auto_commit=False, raise_exception=False, smtp_session=None):
# build an RFC2822 email.message.Message object and send it without queuing
res = None
for email in email_list:
# support headers specific to the specific outgoing email
if email.get('headers'):
email_headers = headers.copy()
try:
email_headers.update(email.get('headers'))
except Exception:
pass
else:
email_headers = headers

msg = IrMailServer.build_email(
email_from=mail.email_from,
email_to=email.get('email_to'),
Expand All @@ -356,7 +366,7 @@ def _send(self, auto_commit=False, raise_exception=False, smtp_session=None):
object_id=mail.res_id and ('%s-%s' % (mail.res_id, mail.model)),
subtype='html',
subtype_alternative='plain',
headers=headers)
headers=email_headers)
processing_pid = email.pop("partner_id", None)
try:
res = IrMailServer.send_email(
Expand Down
4 changes: 3 additions & 1 deletion addons/mass_mailing/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def unsubscribe_placeholder_link(self, **post):
"""Dummy route so placeholder is not prefixed by language, MUST have multilang=False"""
raise werkzeug.exceptions.NotFound()

@http.route(['/mail/mailing/<int:mailing_id>/unsubscribe'], type='http', website=True, auth='public')
# csrf is disabled here because it will be called by the MUA with unpredictable session at that time
@http.route(['/mail/mailing/<int:mailing_id>/unsubscribe'], type='http', website=True, auth='public',
csrf=False)
def mailing(self, mailing_id, email=None, res_id=None, token="", **post):
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
if mailing.exists():
Expand Down
23 changes: 18 additions & 5 deletions addons/mass_mailing/models/mail_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,27 @@ def _send_prepare_body(self):
def _send_prepare_values(self, partner=None):
# TDE: temporary addition (mail was parameter) due to semi-new-API
res = super(MailMail, self)._send_prepare_values(partner)
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url').rstrip('/')
if self.mailing_id and res.get('body') and res.get('email_to'):
if self.mailing_id and res.get('email_to'):
base_url = self.mailing_id.get_base_url()
emails = tools.email_split(res.get('email_to')[0])
email_to = emails and emails[0] or False

unsubscribe_url = self._get_unsubscribe_url(email_to)
link_to_replace = base_url + '/unsubscribe_from_list'
if link_to_replace in res['body']:
res['body'] = res['body'].replace(link_to_replace, unsubscribe_url if unsubscribe_url else '#')

# replace links in body
if f'{base_url}/unsubscribe_from_list' in res['body']:
res['body'] = res['body'].replace(
f'{base_url}/unsubscribe_from_list',
unsubscribe_url,
)

# add headers
res.setdefault("headers", {}).update({
'List-Unsubscribe': f'<{unsubscribe_url}>',
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
'Precedence': 'list',
'X-Auto-Response-Suppress': 'OOF', # avoid out-of-office replies from MS Exchange
})
return res

def _postprocess_sent_message(self, success_pids, failure_reason=False, failure_type=None):
Expand Down

0 comments on commit 1ce0139

Please sign in to comment.