|
|
@ -2,12 +2,15 @@ |
|
|
|
# Copyright 2019 Therp BV <https://therp.nl> |
|
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|
|
|
import uuid |
|
|
|
import requests |
|
|
|
from werkzeug.routing import Rule, Map |
|
|
|
from odoo import models |
|
|
|
from odoo.tools.image import image_resize_image |
|
|
|
from odoo.addons.web.controllers.main import Binary |
|
|
|
from odoo.modules import get_resource_path |
|
|
|
from odoo.addons.base.ir.ir_mail_server import encode_header_param |
|
|
|
from lxml.html.soupparser import fromstring |
|
|
|
from lxml.etree import tostring |
|
|
|
from base64 import b64encode |
|
|
|
from base64 import b64encode, b64decode |
|
|
|
from email.mime.image import MIMEImage |
|
|
|
from email import Encoders |
|
|
|
|
|
|
@ -33,10 +36,10 @@ class IrMailServer(models.Model): |
|
|
|
body_alternative=None, |
|
|
|
subtype_alternative='plain'): |
|
|
|
result = super(IrMailServer, self).build_email( |
|
|
|
email_from, |
|
|
|
email_to, |
|
|
|
subject, |
|
|
|
body, |
|
|
|
email_from=email_from, |
|
|
|
email_to=email_to, |
|
|
|
subject=subject, |
|
|
|
body=body, |
|
|
|
email_cc=email_cc, |
|
|
|
email_bcc=email_bcc, |
|
|
|
reply_to=reply_to, |
|
|
@ -49,40 +52,75 @@ class IrMailServer(models.Model): |
|
|
|
body_alternative=body_alternative, |
|
|
|
subtype_alternative=subtype_alternative, |
|
|
|
) |
|
|
|
return self._build_email_replace_img_src(result, result) |
|
|
|
|
|
|
|
def _build_email_replace_img_src(self, email, email_part): |
|
|
|
""" Given a message, find it's img tags and if they |
|
|
|
are URLs, replace them with cids. |
|
|
|
|
|
|
|
def _build_email_replace_img_src(msg, attachments): |
|
|
|
""" Given a message, find it's img tags and if they |
|
|
|
are URLs, replace them with cids. |
|
|
|
:param email: The root email.message.Message, used for reference |
|
|
|
:param email_part: The current part of email being examined |
|
|
|
""" |
|
|
|
if email_part.is_multipart(): |
|
|
|
for part in email_part.get_payload(): |
|
|
|
self._build_email_replace_img_src(email, part) |
|
|
|
else: |
|
|
|
if email_part.get_content_subtype() == 'html': |
|
|
|
body = email_part.get_payload(decode=True) |
|
|
|
root = fromstring(body) |
|
|
|
for img in root.xpath( |
|
|
|
"//img[starts-with(@src, '/web/image')]"): |
|
|
|
base_url = self.env['ir.config_parameter'].get_param( |
|
|
|
'web.base.url') |
|
|
|
response = self._get_image(base_url, img.get('src')) |
|
|
|
cid = uuid.uuid4().hex |
|
|
|
filename_rfc2047 = encode_header_param(cid) |
|
|
|
part = MIMEImage(response) |
|
|
|
part.set_param('name', filename_rfc2047) |
|
|
|
part.add_header( |
|
|
|
'Content-Disposition', |
|
|
|
'inline', |
|
|
|
cid=cid, |
|
|
|
filename=filename_rfc2047, |
|
|
|
) |
|
|
|
part.set_payload(response) |
|
|
|
Encoders.encode_base64(part) |
|
|
|
# attach the image into the email as attachment |
|
|
|
email.attach(part) |
|
|
|
img.set('src', 'cid:%s' % (cid)) |
|
|
|
email_part.set_payload(b64encode(tostring(root))) |
|
|
|
return email |
|
|
|
|
|
|
|
:param msg: A email.message.Message |
|
|
|
""" |
|
|
|
if msg.is_multipart(): |
|
|
|
for part in msg.get_payload(): |
|
|
|
_build_email_replace_img_src(part, attachments) |
|
|
|
def _get_image(self, base_url, src): |
|
|
|
""" This function emulates the operations of |
|
|
|
odoo.addons.web.controllers.main.Binary.content_image() because |
|
|
|
we cannot call this from here |
|
|
|
Given an absolute url, return the raw image data. |
|
|
|
""" |
|
|
|
routes = Binary.content_image.routing['routes'] |
|
|
|
map_rules = Map([Rule(x) for x in routes]) |
|
|
|
urls = map_rules.bind(base_url) |
|
|
|
endpoint, kwargs = urls.match(src) |
|
|
|
status, headers, content = self.env['ir.http'].binary_content( |
|
|
|
env=self.env, **kwargs) |
|
|
|
height = int(kwargs.get('height', 0)) |
|
|
|
width = int(kwargs.get('width', 0)) |
|
|
|
if content and (height or width): |
|
|
|
if height > 500: |
|
|
|
height = 500 |
|
|
|
if width > 500: |
|
|
|
width = 500 |
|
|
|
content = image_resize_image( |
|
|
|
content, |
|
|
|
(width or None, height or None), |
|
|
|
filetype='PNG', |
|
|
|
) |
|
|
|
if content: |
|
|
|
content = b64decode(content) |
|
|
|
else: |
|
|
|
if msg.get_content_subtype() == 'html': |
|
|
|
body = msg.get_payload(decode=True) |
|
|
|
root = fromstring(body) |
|
|
|
for img in root.xpath( |
|
|
|
"//img[starts-with(@src, '/web/image')]"): |
|
|
|
base_url = self.env['ir.config_parameter'].get_param( |
|
|
|
'web.base.url') |
|
|
|
src = base_url + img.get('src') |
|
|
|
response = requests.get(src) |
|
|
|
cid = uuid.uuid4().hex |
|
|
|
filename_rfc2047 = encode_header_param(cid) |
|
|
|
part = MIMEImage(response.content) |
|
|
|
part.set_param('name', filename_rfc2047) |
|
|
|
part.add_header( |
|
|
|
'Content-Disposition', |
|
|
|
'inline', |
|
|
|
cid=cid, |
|
|
|
filename=filename_rfc2047, |
|
|
|
) |
|
|
|
part.set_payload(response.content) |
|
|
|
Encoders.encode_base64(part) |
|
|
|
result.attach(part) |
|
|
|
img.set('src', 'cid:%s' % (cid)) |
|
|
|
msg.set_payload(b64encode(tostring(root))) |
|
|
|
_build_email_replace_img_src(result, attachments) |
|
|
|
return result |
|
|
|
path_placeholder = '/web/static/src/img/placeholder.png' |
|
|
|
with open(get_resource_path(path_placeholder)) as f: |
|
|
|
content = f.read() |
|
|
|
else: |
|
|
|
content = b64decode(content) |
|
|
|
return content |