[FIX] mail: replace local links in notification emails
Since notification emails are not mail templates anymore they do not go through the mail_template.render_post_process() method that converts local links to absolute links. This means that buttons and images were not correctly displayed in notification emails. Indeed those still had local links. This commit fixes that by moving the method directly in mail.thread and calling it accordingly when rendering the notification email. Note that the method has been changed in order to use regex instead of html parser. It is done based on work already done in mass mailing application. Indeed using parsers is complex and time consuming and may create changes in the html structure that are not intended. Next step in master is to go through various link modifications in mass mailing module to see how to handle them properly. Maybe.
This commit is contained in:
@@ -313,46 +313,9 @@ class MailTemplate(models.Model):
|
||||
# RENDERING
|
||||
# ----------------------------------------
|
||||
|
||||
@api.model
|
||||
def _replace_local_links(self, html):
|
||||
""" Post-processing of html content to replace local links to absolute
|
||||
links, using web.base.url as base url. """
|
||||
if not html:
|
||||
return html
|
||||
|
||||
# form a tree
|
||||
root = lxml.html.fromstring(html)
|
||||
if not len(root) and root.text is None and root.tail is None:
|
||||
html = u'<div>%s</div>' % html
|
||||
root = lxml.html.fromstring(html, encoding='unicode')
|
||||
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
base = urls.url_parse(base_url)
|
||||
|
||||
def _process_link(url):
|
||||
new_url = urls.url_parse(url)
|
||||
if new_url.scheme and (new_url.netloc or new_url.scheme == 'mailto'):
|
||||
return url
|
||||
return new_url.replace(scheme=base.scheme, netloc=base.netloc).to_url()
|
||||
|
||||
# check all nodes, replace :
|
||||
# - img src -> check URL
|
||||
# - a href -> check URL
|
||||
for node in root.iter():
|
||||
if node.tag == 'a' and node.get('href'):
|
||||
node.set('href', _process_link(node.get('href')))
|
||||
elif node.tag == 'img' and not node.get('src', 'data').startswith(u'data'):
|
||||
node.set('src', _process_link(node.get('src')))
|
||||
|
||||
html = lxml.html.tostring(root, pretty_print=False, method='html', encoding='unicode')
|
||||
# this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that
|
||||
if html.startswith(u'<div>') and html.endswith(u'</div>'):
|
||||
html = html[5:-6]
|
||||
return html
|
||||
|
||||
@api.model
|
||||
def render_post_process(self, html):
|
||||
html = self._replace_local_links(html)
|
||||
html = self.env['mail.thread']._replace_local_links(html)
|
||||
return html
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -23,9 +23,10 @@ from email.message import Message
|
||||
from email.utils import formataddr
|
||||
from lxml import etree
|
||||
from werkzeug import url_encode
|
||||
from werkzeug import urls
|
||||
|
||||
from odoo import _, api, exceptions, fields, models, tools
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools import pycompat, ustr
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
@@ -374,6 +375,38 @@ class MailThread(models.AbstractModel):
|
||||
# Technical methods / wrappers / tools
|
||||
# ------------------------------------------------------
|
||||
|
||||
def _replace_local_links(self, html, base_url=None):
|
||||
""" Replace local links by absolute links. It is required in various
|
||||
cases, for example when sending emails on chatter or sending mass
|
||||
mailings. It replaces
|
||||
|
||||
* href of links (mailto will not match the regex)
|
||||
* src of images (base64 hardcoded data will not match the regex)
|
||||
* styling using url like background-image: url
|
||||
|
||||
It is done using regex because it is shorten than using an html parser
|
||||
to create a potentially complex soupe and hope to have a result that
|
||||
has not been harmed.
|
||||
"""
|
||||
if not html:
|
||||
return html
|
||||
|
||||
html = ustr(html)
|
||||
|
||||
def _sub_relative2absolute(match):
|
||||
# compute here to do it only if really necessary + cache will ensure it is done only once
|
||||
# if not base_url
|
||||
if not _sub_relative2absolute.base_url:
|
||||
_sub_relative2absolute.base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url")
|
||||
return match.group(1) + urls.url_join(_sub_relative2absolute.base_url, match.group(2))
|
||||
|
||||
_sub_relative2absolute.base_url = base_url
|
||||
html = re.sub(r"""(<img(?=\s)[^>]*\ssrc=")(/[^/][^"]+)""", _sub_relative2absolute, html)
|
||||
html = re.sub(r"""(<a(?=\s)[^>]*\shref=")(/[^/][^"]+)""", _sub_relative2absolute, html)
|
||||
html = re.sub(r"""(<[^>]+\bstyle="[^"]+\burl\('?)(/[^/'][^'")]+)""", _sub_relative2absolute, html)
|
||||
|
||||
return html
|
||||
|
||||
@api.model
|
||||
def _garbage_collect_attachments(self):
|
||||
""" Garbage collect lost mail attachments. Those are attachments
|
||||
|
||||
@@ -185,6 +185,7 @@ class Partner(models.Model):
|
||||
'subject': message.subject or (message.record_name and 'Re: %s' % message.record_name),
|
||||
'body': base_template.render(template_ctx, engine='ir.qweb'),
|
||||
}
|
||||
fol_values['body'] = self.env['mail.thread']._replace_local_links(fol_values['body'])
|
||||
# send email
|
||||
new_emails, new_recipients_nbr = self._notify_send(fol_values['body'], fol_values['subject'], recipient_template_values['recipients'], **base_mail_values)
|
||||
# update notifications
|
||||
|
||||
@@ -742,7 +742,7 @@ class MassMailing(models.Model):
|
||||
raise UserError(_('Please select recipients.'))
|
||||
|
||||
# Convert links in absolute URLs before the application of the shortener
|
||||
mailing.body_html = self.env['mail.template']._replace_local_links(mailing.body_html)
|
||||
mailing.body_html = self.env['mail.thread']._replace_local_links(mailing.body_html)
|
||||
|
||||
composer_values = {
|
||||
'author_id': author_id,
|
||||
|
||||
@@ -20,7 +20,7 @@ class TestMassMailing(models.TransientModel):
|
||||
test_emails = tools.email_split(self.email_to)
|
||||
for test_mail in test_emails:
|
||||
# Convert links in absolute URLs before the application of the shortener
|
||||
mailing.write({'body_html': self.env['mail.template']._replace_local_links(mailing.body_html)})
|
||||
mailing.write({'body_html': self.env['mail.thread']._replace_local_links(mailing.body_html)})
|
||||
mail_values = {
|
||||
'email_from': mailing.email_from,
|
||||
'reply_to': mailing.reply_to,
|
||||
|
||||
Reference in New Issue
Block a user