[IMP] mass_mailing: add 'view in browser' link in mailings

PURPOSE

Add a block that allows recipients to check the email in a browser in case
their mail service provider does not render it well or to see it full screen.

SPECIFICATIONS

A thumbnail has been added in the snippet thumbnails list of the mail editor.
It allows to add an header snippet. This snippet contains a link allowing the
recipient to see the mail in its browser instead of its mail client.

Add a route to display a mailing content. This route should be recipient
dependent as mailings could contain jinja markers. Using the same access token
and check as unsubscribe route this public route allows to display a mailing
content directly rendered for a given customer.

_get_unsubscribe_url method is moved from mail_mail to mailing as it does
not really belong to the mail model. Indeed it is mailing dependent.

A view URL leading to the view in browser route is added. It is managed like
the unsubscribe url: magically avoid shortening, magically rewrite when
sending email.

Task ID-2239327
PR #49886
This commit is contained in:
Sébastien Mottet (oms)
2020-08-05 13:55:04 +00:00
committed by Thibault Delavallée
parent 0809601d6d
commit ac0579263b
7 changed files with 93 additions and 21 deletions
+29
View File
@@ -105,6 +105,35 @@ class MassMailController(http.Controller):
return response
@http.route(['/mailing/<int:mailing_id>/view'], type='http', website=True, auth='public')
def view(self, mailing_id, email=None, res_id=None, token=""):
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
if mailing.exists():
res_id = int(res_id) if res_id else False
if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)) and not request.env.user.has_group('mass_mailing.group_mass_mailing_user'):
raise exceptions.AccessDenied()
res = mailing.convert_links()
base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url').rstrip('/')
urls_to_replace = [
(base_url + '/unsubscribe_from_list', mailing._get_unsubscribe_url(email, res_id)),
(base_url + '/view', mailing._get_view_url(email, res_id))
]
for url_to_replace, new_url in urls_to_replace:
if url_to_replace in res[mailing_id]:
res[mailing_id] = res[mailing_id].replace(url_to_replace, new_url if new_url else '#')
res[mailing_id] = res[mailing_id].replace(
'class="o_snippet_view_in_browser"',
'class="o_snippet_view_in_browser" style="display: none;"'
)
return request.render('mass_mailing.view', {
'body': res[mailing_id],
})
return request.redirect('/web')
@http.route('/r/<string:code>/m/<int:mailing_trace_id>', type='http', auth="public")
def full_url_redirect(self, code, mailing_trace_id, **post):
# don't assume geoip is set, it is part of the website module
+9 -19
View File
@@ -29,21 +29,6 @@ class MailMail(models.Model):
token = tools.hmac(self.env(su=True), 'mass_mailing-mail_mail-open', self.id)
return werkzeug.urls.url_join(base_url, 'mail/track/%s/%s/blank.gif' % (self.id, token))
def _get_unsubscribe_url(self, email_to):
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
url = werkzeug.urls.url_join(
base_url, 'mail/mailing/%(mailing_id)s/unsubscribe?%(params)s' % {
'mailing_id': self.mailing_id.id,
'params': werkzeug.urls.url_encode({
'res_id': self.res_id,
'email': email_to,
'token': self.mailing_id._unsubscribe_token(
self.res_id, email_to),
}),
}
)
return url
def _send_prepare_body(self):
""" Override to add the tracking URL to the body and to add
trace ID in shortened urls """
@@ -81,10 +66,15 @@ class MailMail(models.Model):
if self.mailing_id and res.get('body') and res.get('email_to'):
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 '#')
urls_to_replace = [
(base_url + '/unsubscribe_from_list', self.mailing_id._get_unsubscribe_url(email_to, self.res_id)),
(base_url + '/view', self.mailing_id._get_view_url(email_to, self.res_id))
]
for url_to_replace, new_url in urls_to_replace:
if url_to_replace in res['body']:
res['body'] = res['body'].replace(url_to_replace, new_url if new_url else '#')
return res
def _postprocess_sent_message(self, success_pids, failure_reason=False, failure_type=None):
@@ -18,6 +18,6 @@ class MailRenderMixin(models.AbstractModel):
rendered[res_id] = self._shorten_links(
html,
self.env.context['post_convert_links'],
blacklist=['/unsubscribe_from_list']
blacklist=['/unsubscribe_from_list', '/view']
)
return rendered
+30 -1
View File
@@ -8,6 +8,7 @@ import lxml
import random
import re
import threading
import werkzeug.urls
from ast import literal_eval
from datetime import datetime
from dateutil.relativedelta import relativedelta
@@ -528,6 +529,34 @@ class MassMailing(models.Model):
done_res_ids = [record['res_id'] for record in already_mailed]
return [rid for rid in res_ids if rid not in done_res_ids]
def _get_unsubscribe_url(self, email_to, res_id):
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
url = werkzeug.urls.url_join(
base_url, 'mail/mailing/%(mailing_id)s/unsubscribe?%(params)s' % {
'mailing_id': self.id,
'params': werkzeug.urls.url_encode({
'res_id': res_id,
'email': email_to,
'token': self._unsubscribe_token(res_id, email_to),
}),
}
)
return url
def _get_view_url(self, email_to, res_id):
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
url = werkzeug.urls.url_join(
base_url, 'mailing/%(mailing_id)s/view?%(params)s' % {
'mailing_id': self.id,
'params': werkzeug.urls.url_encode({
'res_id': res_id,
'email': email_to,
'token': self._unsubscribe_token(res_id, email_to),
}),
}
)
return url
def action_send_mail(self, res_ids=None):
author_id = self.env.user.partner_id.id
@@ -583,7 +612,7 @@ class MassMailing(models.Model):
if mass_mailing.medium_id:
vals['medium_id'] = mass_mailing.medium_id.id
res[mass_mailing.id] = mass_mailing._shorten_links(html, vals, blacklist=['/unsubscribe_from_list'])
res[mass_mailing.id] = mass_mailing._shorten_links(html, vals, blacklist=['/unsubscribe_from_list', '/view'])
return res
Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

@@ -97,6 +97,11 @@
</div>
</template>
<template id="view" name="Browser View">
<!-- Raw body inserted here because it is a rendered mailing, therefore internal content -->
<t t-raw="body"/>
</template>
<template id="page_unsubscribe" name="Unsubscribe">
<t t-call="mass_mailing.layout">
<t t-call="mass_mailing.unsubscribe"/>
@@ -28,6 +28,7 @@
<t t-snippet="mass_mailing.s_mail_block_header_text_social" t-thumbnail="/mass_mailing/static/src/img/blocks/block_header_text_social.png"/>
<t t-snippet="mass_mailing.s_mail_block_header_logo" t-thumbnail="/mass_mailing/static/src/img/blocks/block_header_logo.png"/>
<t t-snippet="mass_mailing.s_mail_block_banner" t-thumbnail="/mass_mailing/static/src/img/blocks/block_banner.png"/>
<t t-snippet="mass_mailing.s_mail_block_header_view" t-thumbnail="/mass_mailing/static/src/img/blocks/block_header_browser.png"/>
</div>
</div>
<div id="email_designer_default_body" class="o_panel">
@@ -154,6 +155,24 @@
</div>
</template>
<template id="s_mail_block_header_view" name="View in browser">
<div class="o_snippet_view_in_browser">
<div class="o_mail_snippet_general">
<table align="center" cellspacing="0" cellpadding="0" border="0" class="o_mail_table_styles">
<tbody>
<tr>
<td width="100%" class="o_mail_h_padding o_mail_v_padding o_mail_no_colorpicker text-center">
<a href="/view">
View this email in your browser
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<template id="s_mail_block_title_text" name="Title Content">
<div class="o_mail_block_title_text">
<div class="o_mail_snippet_general">