From 692b5339dfe1ca2f72144af985b6fce25963e53b Mon Sep 17 00:00:00 2001 From: George Daramouskas Date: Mon, 1 Jul 2019 11:12:40 +0200 Subject: [PATCH] fixup! fixup! fixup! fixup! fixup! fixup! [ADD] mail_embed_image module --- mail_embed_image/models/ir_mail_server.py | 118 ++++++++++++------ .../tests/test_mail_embed_image.py | 5 +- 2 files changed, 82 insertions(+), 41 deletions(-) diff --git a/mail_embed_image/models/ir_mail_server.py b/mail_embed_image/models/ir_mail_server.py index 7f95adf0..5cf98a3f 100644 --- a/mail_embed_image/models/ir_mail_server.py +++ b/mail_embed_image/models/ir_mail_server.py @@ -2,12 +2,15 @@ # Copyright 2019 Therp BV # 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 diff --git a/mail_embed_image/tests/test_mail_embed_image.py b/mail_embed_image/tests/test_mail_embed_image.py index 002ee211..81f3edac 100644 --- a/mail_embed_image/tests/test_mail_embed_image.py +++ b/mail_embed_image/tests/test_mail_embed_image.py @@ -34,7 +34,7 @@ class TestMailEmbedImage(TransactionCase): body1 = '
this is an email
' email1 = model_mail_mail.create({ 'body_html': body1, - 'body': body1, + 'email_from': 'test@example.com', 'email_to': 'test@example.com', }) email1.send() @@ -52,9 +52,12 @@ class TestMailEmbedImage(TransactionCase): ) email2 = model_mail_mail.create({ 'body_html': body2, + 'email_from': 'test@example.com', 'email_to': 'test@example.com', }) email2.send() + # TODO the below will not work, assert build_email called and return + # value (remove the tests above as well, they are foolish) body2final = fromstring(email2.body_html) self.assertEquals(len(body2final.xpath('//img')), 3) srcs = [x.get('src') for x in body2final.xpath('//img')]