summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Morel <xmo@odoo.com>2017-05-17 10:59:41 +0200
committerOlivier Dony <odo@openerp.com>2017-08-20 23:25:54 +0200
commit200612ca1ef1a1bf75dbe52feef5a09ae5a58ca6 (patch)
treec9243ec052649007c3c0c083b71cd290bc923f1a
parentae866acf08abe91fe87a3d3f4899070962b795cc (diff)
[FIX] P3: text model in various mail bits
No formal decision there, and the stdlib API seems like a PITA as it's based around native strings, not bytes and not text. * try to use cross-model API instead of random encode/decode * unicode all the mail_template things * formataddr *requires* a text tuple now In P2, formataddr((False, False)) "worked" (generated "False <False>"), in P3 it will fail and blow up. * attachments are quite definitely bytes * remove try_coerce_ascii because it's garbage
-rw-r--r--addons/mail/models/mail_mail.py2
-rw-r--r--addons/mail/models/mail_template.py10
-rw-r--r--addons/mail/tests/test_mail_features.py2
-rw-r--r--odoo/addons/base/ir/ir_mail_server.py75
-rw-r--r--odoo/addons/base/res/res_partner.py2
-rw-r--r--odoo/addons/base/tests/test_mail.py14
-rw-r--r--odoo/addons/base/tests/test_mail_examples.py84
-rw-r--r--odoo/tools/mail.py43
8 files changed, 112 insertions, 120 deletions
diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py
index 3f206424e07..e53c24fb176 100644
--- a/addons/mail/models/mail_mail.py
+++ b/addons/mail/models/mail_mail.py
@@ -169,7 +169,7 @@ class MailMail(models.Model):
- else fallback on mail.email_to splitting """
self.ensure_one()
if partner:
- email_to = [formataddr((partner.name, partner.email))]
+ email_to = [formataddr((partner.name or 'False', partner.email or 'False'))]
else:
email_to = tools.email_split_and_format(self.email_to)
return email_to
diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py
index e9bf605c382..f44ee7be09a 100644
--- a/addons/mail/models/mail_template.py
+++ b/addons/mail/models/mail_template.py
@@ -306,8 +306,8 @@ class MailTemplate(models.Model):
# form a tree
root = lxml.html.fromstring(html)
if not len(root) and root.text is None and root.tail is None:
- html = '<div>%s</div>' % html
- root = lxml.html.fromstring(html)
+ 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)
@@ -324,12 +324,12 @@ class MailTemplate(models.Model):
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('data'):
+ 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')
+ 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('<div>') and html.endswith('</div>'):
+ if html.startswith(u'<div>') and html.endswith(u'</div>'):
html = html[5:-6]
return html
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index a6fce59c10a..8e2264ad6d3 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -131,7 +131,7 @@ class TestMessagePost(TestMail):
self.assertEqual(set(msg.attachment_ids.mapped('res_id')), set([self.test_pigs.id]),
'message_post: all atttachments should be linked to the pigs group')
self.assertEqual(set([base64.b64decode(x) for x in msg.attachment_ids.mapped('datas')]),
- set(['migration test', _attachments[0][1], _attachments[1][1]]))
+ set([b'migration test', _attachments[0][1], _attachments[1][1]]))
self.assertTrue(set([_attach_1.id, _attach_2.id]).issubset(msg.attachment_ids.ids),
'message_post: mail.message attachments duplicated')
# notifications
diff --git a/odoo/addons/base/ir/ir_mail_server.py b/odoo/addons/base/ir/ir_mail_server.py
index 91cf0e61e73..eab160b4a84 100644
--- a/odoo/addons/base/ir/ir_mail_server.py
+++ b/odoo/addons/base/ir/ir_mail_server.py
@@ -39,20 +39,8 @@ class WriteToLogger(object):
_logger.debug(s)
smtplib.stderr = WriteToLogger()
-def try_coerce_ascii(string_utf8):
- """Attempts to decode the given utf8-encoded string
- as ASCII after coercing it to UTF-8, then return
- the confirmed 7-bit ASCII string.
-
- If the process fails (because the string
- contains non-ASCII characters) returns ``None``.
- """
- try:
- string_utf8.decode('ascii')
- except UnicodeDecodeError:
- return
- return string_utf8
-
+def is_ascii(s):
+ return all(ord(cp) < 128 for cp in s)
def encode_header(header_text):
"""Returns an appropriate representation of the given header value,
@@ -70,16 +58,10 @@ def encode_header(header_text):
"""
if not header_text:
return ""
- # convert anything to utf-8, suitable for testing ASCIIness, as 7-bit chars are
- # encoded as ASCII in utf-8
- header_text_utf8 = ustr(header_text).encode('utf-8')
- header_text_ascii = try_coerce_ascii(header_text_utf8)
- # if this header contains non-ASCII characters,
- # we'll need to wrap it up in a message.header.Header
- # that will take care of RFC2047-encoding it as
- # 7-bit string.
- return header_text_ascii or Header(header_text_utf8, 'utf-8')
-
+ header_text = ustr(header_text) # FIXME: require unicode higher up?
+ if is_ascii(header_text):
+ return pycompat.to_native(header_text)
+ Header(header_text, 'utf-8')
def encode_header_param(param_text):
"""Returns an appropriate RFC2047 encoded representation of the given
@@ -97,10 +79,10 @@ def encode_header_param(param_text):
# For details see the encode_header() method that uses the same logic
if not param_text:
return ""
- param_text_utf8 = ustr(param_text).encode('utf-8')
- param_text_ascii = try_coerce_ascii(param_text_utf8)
- return param_text_ascii or Charset('utf8').header_encode(param_text_utf8)
-
+ param_text = ustr(param_text) # FIXME: require unicode higher up?
+ if is_ascii(param_text):
+ return pycompat.to_native(param_text) # TODO: is that actually necessary?
+ return Charset("utf-8").header_encode(param_text)
address_pattern = re.compile(r'([^ ,<@]+@[^> ,]+)')
@@ -111,8 +93,8 @@ def extract_rfc2822_addresses(text):
"""
if not text:
return []
- candidates = address_pattern.findall(ustr(text).encode('utf-8'))
- return [c for c in candidates if try_coerce_ascii(c)]
+ candidates = address_pattern.findall(ustr(text))
+ return [c for c in candidates if is_ascii(c)]
def encode_rfc2822_address_header(header_text):
@@ -124,11 +106,20 @@ def encode_rfc2822_address_header(header_text):
"""
def encode_addr(addr):
name, email = addr
- if not try_coerce_ascii(name):
- name = str(Header(name, 'utf-8'))
+ # If s is a <text string>, then charset is a hint specifying the
+ # character set of the characters in the string. The Unicode string
+ # will be encoded using the following charsets in order: us-ascii,
+ # the charset hint, utf-8. The first character set to not provoke a
+ # UnicodeError is used.
+ # -> always pass a text string to Header
+
+ # also Header.__str__ in Python 3 "Returns an approximation of the
+ # Header as a string, using an unlimited line length.", the old one
+ # was "A synonym for Header.encode()." so call encode() directly?
+ name = Header(pycompat.to_text(name)).encode()
return formataddr((name, email))
- addresses = getaddresses([ustr(header_text).encode('utf-8')])
+ addresses = getaddresses([pycompat.to_native(ustr(header_text))])
return COMMASPACE.join(encode_addr(a) for a in addresses)
@@ -252,8 +243,8 @@ class IrMailServer(models.Model):
# The user/password must be converted to bytestrings in order to be usable for
# certain hashing schemes, like HMAC.
# See also bug #597143 and python issue #5285
- smtp_user = ustr(smtp_user).encode('utf-8')
- smtp_password = ustr(smtp_password).encode('utf-8')
+ smtp_user = pycompat.to_native(ustr(smtp_user))
+ smtp_password = pycompat.to_native(ustr(smtp_password))
connection.login(smtp_user, smtp_password)
return connection
@@ -301,8 +292,8 @@ class IrMailServer(models.Model):
email_bcc = email_bcc or []
body = body or u''
- email_body_utf8 = ustr(body).encode('utf-8')
- email_text_part = MIMEText(email_body_utf8, _subtype=subtype, _charset='utf-8')
+ email_body = ustr(body)
+ email_text_part = MIMEText(email_body, _subtype=subtype, _charset='utf-8')
msg = MIMEMultipart()
if not message_id:
@@ -328,20 +319,20 @@ class IrMailServer(models.Model):
msg['Date'] = formatdate()
# Custom headers may override normal headers or provide additional ones
for key, value in pycompat.items(headers):
- msg[ustr(key).encode('utf-8')] = encode_header(value)
+ msg[pycompat.to_native(ustr(key))] = encode_header(value)
if subtype == 'html' and not body_alternative:
# Always provide alternative text body ourselves if possible.
- text_utf8 = html2text.html2text(email_body_utf8.decode('utf-8')).encode('utf-8')
+ text = html2text.html2text(email_body_utf8)
alternative_part = MIMEMultipart(_subtype="alternative")
- alternative_part.attach(MIMEText(text_utf8, _charset='utf-8', _subtype='plain'))
+ alternative_part.attach(MIMEText(text, _charset='utf-8', _subtype='plain'))
alternative_part.attach(email_text_part)
msg.attach(alternative_part)
elif body_alternative:
# Include both alternatives, as specified, within a multipart/alternative part
alternative_part = MIMEMultipart(_subtype="alternative")
- body_alternative_utf8 = ustr(body_alternative).encode('utf-8')
- alternative_body_part = MIMEText(body_alternative_utf8, _subtype=subtype_alternative, _charset='utf-8')
+ body_alternative_ = ustr(body_alternative)
+ alternative_body_part = MIMEText(body_alternative_, _subtype=subtype_alternative, _charset='utf-8')
alternative_part.attach(alternative_body_part)
alternative_part.attach(email_text_part)
msg.attach(alternative_part)
diff --git a/odoo/addons/base/res/res_partner.py b/odoo/addons/base/res/res_partner.py
index 972b74d7f3e..32772fa19a6 100644
--- a/odoo/addons/base/res/res_partner.py
+++ b/odoo/addons/base/res/res_partner.py
@@ -337,7 +337,7 @@ class Partner(models.Model):
@api.depends('name', 'email')
def _compute_email_formatted(self):
for partner in self:
- partner.email_formatted = formataddr((partner.name, partner.email))
+ partner.email_formatted = formataddr((partner.name or u"False", partner.email or u"False"))
@api.depends('is_company')
def _compute_company_type(self):
diff --git a/odoo/addons/base/tests/test_mail.py b/odoo/addons/base/tests/test_mail.py
index 1a30ef4690b..84cf892ea30 100644
--- a/odoo/addons/base/tests/test_mail.py
+++ b/odoo/addons/base/tests/test_mail.py
@@ -171,14 +171,14 @@ class TestSanitizer(unittest.TestCase):
for ext in test_mail_examples.QUOTE_BLOCKQUOTE_IN:
self.assertIn(ext, html)
for ext in test_mail_examples.QUOTE_BLOCKQUOTE_OUT:
- self.assertIn('<span data-o-mail-quote="1">%s' % misc.html_escape(ext.decode('utf-8')), html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s' % misc.html_escape(ext), html)
def test_quote_thunderbird(self):
html = html_sanitize(test_mail_examples.QUOTE_THUNDERBIRD_1)
for ext in test_mail_examples.QUOTE_THUNDERBIRD_1_IN:
self.assertIn(ext, html)
for ext in test_mail_examples.QUOTE_THUNDERBIRD_1_OUT:
- self.assertIn('<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext.decode('utf-8')), html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
def test_quote_hotmail_html(self):
html = html_sanitize(test_mail_examples.QUOTE_HOTMAIL_HTML)
@@ -225,7 +225,7 @@ class TestSanitizer(unittest.TestCase):
for text in in_lst:
self.assertIn(text, new_html)
for text in out_lst:
- self.assertIn('<span data-o-mail-quote="1">%s</span>' % misc.html_escape(text), new_html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s</span>' % misc.html_escape(text), new_html)
def test_quote_signature(self):
test_data = [
@@ -244,27 +244,27 @@ class TestSanitizer(unittest.TestCase):
for ext in test_mail_examples.GMAIL_1_IN:
self.assertIn(ext, html)
for ext in test_mail_examples.GMAIL_1_OUT:
- self.assertIn('<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
def test_quote_text(self):
html = html_sanitize(test_mail_examples.TEXT_1)
for ext in test_mail_examples.TEXT_1_IN:
self.assertIn(ext, html)
for ext in test_mail_examples.TEXT_1_OUT:
- self.assertIn('<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
html = html_sanitize(test_mail_examples.TEXT_2)
for ext in test_mail_examples.TEXT_2_IN:
self.assertIn(ext, html)
for ext in test_mail_examples.TEXT_2_OUT:
- self.assertIn('<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
def test_quote_bugs(self):
html = html_sanitize(test_mail_examples.BUG1)
for ext in test_mail_examples.BUG_1_IN:
self.assertIn(ext, html)
for ext in test_mail_examples.BUG_1_OUT:
- self.assertIn('<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext.decode('utf-8')), html)
+ self.assertIn(u'<span data-o-mail-quote="1">%s</span>' % misc.html_escape(ext), html)
def test_misc(self):
# False / void should not crash
diff --git a/odoo/addons/base/tests/test_mail_examples.py b/odoo/addons/base/tests/test_mail_examples.py
index dfecfe7c0d4..4f3b5b48300 100644
--- a/odoo/addons/base/tests/test_mail_examples.py
+++ b/odoo/addons/base/tests/test_mail_examples.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-MISC_HTML_SOURCE = """
+MISC_HTML_SOURCE = u"""
<font size="2" style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; ">test1</font>
<div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; font-style: normal; ">
<b>test2</b></div><div style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; font-size: 12px; ">
@@ -19,7 +19,7 @@ test12</font></div><div><font color="#1f1f1f" face="monospace" size="2"><br></fo
<a href="javascript:alert('malicious code')">test link</a>
"""
-EDI_LIKE_HTML_SOURCE = """<div style="font-family: 'Lucida Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
+EDI_LIKE_HTML_SOURCE = u"""<div style="font-family: 'Lucida Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Hello ${object.partner_id.name},</p>
<p>A new invoice is available for you: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
@@ -63,7 +63,7 @@ EDI_LIKE_HTML_SOURCE = """<div style="font-family: 'Lucida Grande', Ubuntu, Aria
# QUOTES
-QUOTE_BLOCKQUOTE = """<html>
+QUOTE_BLOCKQUOTE = u"""<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
@@ -103,14 +103,14 @@ web: <a class="moz-txt-link-freetext" href="https://opener.am">https://opener.am
</body>
</html>"""
-QUOTE_BLOCKQUOTE_IN = ["""<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com" type="cite" data-o-mail-quote-node="1" data-o-mail-quote="1">"""]
-QUOTE_BLOCKQUOTE_OUT = ["""--
+QUOTE_BLOCKQUOTE_IN = [u"""<blockquote cite="mid:CAEJSRZvWvud8c6Qp=wfNG6O1+wK3i_jb33qVrF7XyrgPNjnyUA@mail.gmail.com" type="cite" data-o-mail-quote-node="1" data-o-mail-quote="1">"""]
+QUOTE_BLOCKQUOTE_OUT = [u"""--
Opener B.V. - Business solutions driven by open source collaboration
Stefan Rijnhart - Consultant/developer"""]
-QUOTE_THUNDERBIRD_HTML = """<html>
+QUOTE_THUNDERBIRD_HTML = u"""<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
@@ -137,13 +137,13 @@ Raoul Poilvache
</html>"""
-QUOTE_THUNDERBIRD_HTML_IN = ["""<blockquote cite="mid:CAP76m_WWFH2KVrbjOxbaozvkmbzZYLWJnQ0n0sy9XpGaCWRf1g@mail.gmail.com" type="cite" data-o-mail-quote-node="1" data-o-mail-quote="1">"""]
-QUOTE_THUNDERBIRD_HTML_OUT = ["""<pre class="moz-signature" cols="72"><span data-o-mail-quote="1">--
+QUOTE_THUNDERBIRD_HTML_IN = [u"""<blockquote cite="mid:CAP76m_WWFH2KVrbjOxbaozvkmbzZYLWJnQ0n0sy9XpGaCWRf1g@mail.gmail.com" type="cite" data-o-mail-quote-node="1" data-o-mail-quote="1">"""]
+QUOTE_THUNDERBIRD_HTML_OUT = [u"""<pre class="moz-signature" cols="72"><span data-o-mail-quote="1">--
Raoul Poilvache
</span></pre>"""]
-QUOTE_HOTMAIL_HTML = """
+QUOTE_HOTMAIL_HTML = u"""
<html>
<head>
<style><!--
@@ -170,12 +170,12 @@ To: tartelette@example.com grosbedon@example.com<br><br>
</div>-- <br><div class="ecxgmail_signature">
Raoul Poilvache</div>
</div></div></div></body></html>"""
-QUOTE_HOTMAIL_HTML_IN = ["""I don't like that.<br><br>"""]
+QUOTE_HOTMAIL_HTML_IN = [u"""I don't like that.<br><br>"""]
QUOTE_HOTMAIL_HTML_OUT = [
- """<hr id="stopSpelling" data-o-mail-quote="1">""",
- """<div dir="ltr" data-o-mail-quote="1"><b data-o-mail-quote="1"><i data-o-mail-quote="1">Test reply. The suite.</i></b>"""]
+ u"""<hr id="stopSpelling" data-o-mail-quote="1">""",
+ u"""<div dir="ltr" data-o-mail-quote="1"><b data-o-mail-quote="1"><i data-o-mail-quote="1">Test reply. The suite.</i></b>"""]
-QUOTE_THUNDERBIRD_1 = """<div>On 11/08/2012 05:29 PM,
+QUOTE_THUNDERBIRD_1 = u"""<div>On 11/08/2012 05:29 PM,
<a href="mailto:dummy@example.com">dummy@example.com</a> wrote:<br></div>
<blockquote>
<div>I contact you about our meeting for tomorrow. Here is the
@@ -203,15 +203,15 @@ Raoul Grosbedonn&#233;e
</pre>"""
QUOTE_THUNDERBIRD_1_IN = [
- '<a href="mailto:dummy@example.com">dummy@example.com</a> ',
- '<blockquote data-o-mail-quote-node="1" data-o-mail-quote="1">',
- 'Ok for me. I am replying directly below your mail, using Thunderbird, with a signature.']
-QUOTE_THUNDERBIRD_1_OUT = ["""--
+ u'<a href="mailto:dummy@example.com">dummy@example.com</a> ',
+ u'<blockquote data-o-mail-quote-node="1" data-o-mail-quote="1">',
+ u'Ok for me. I am replying directly below your mail, using Thunderbird, with a signature.']
+QUOTE_THUNDERBIRD_1_OUT = [u"""--
Raoul Grosbedonnée
"""]
-TEXT_1 = """I contact you about our meeting tomorrow. Here is the schedule I propose:
+TEXT_1 = u"""I contact you about our meeting tomorrow. Here is the schedule I propose:
9 AM: brainstorming about our new amazing business app
9.45 AM: summary
10 AM: meeting with Ignasse to present our app
@@ -219,16 +219,16 @@ Is everything ok for you ?
--
MySignature"""
-TEXT_1_IN = ["""I contact you about our meeting tomorrow. Here is the schedule I propose:
+TEXT_1_IN = [u"""I contact you about our meeting tomorrow. Here is the schedule I propose:
9 AM: brainstorming about our new amazing business app
9.45 AM: summary
10 AM: meeting with Ignasse to present our app
Is everything ok for you ?"""]
-TEXT_1_OUT = ["""
+TEXT_1_OUT = [u"""
--
MySignature"""]
-TEXT_2 = """Salut Raoul!
+TEXT_2 = u"""Salut Raoul!
Le 28 oct. 2012 à 00:02, Raoul Grosbedon a écrit :
> I contact you about our meeting tomorrow. Here is the schedule I propose: (quote)
@@ -253,8 +253,8 @@ Bert TARTOPOILS
bert.tartopoils@miam.miam
"""
-TEXT_2_IN = ["Salut Raoul!", "Of course. This seems viable."]
-TEXT_2_OUT = ["""
+TEXT_2_IN = [u"Salut Raoul!", "Of course. This seems viable."]
+TEXT_2_OUT = [u"""
> I contact you about our meeting tomorrow. Here is the schedule I propose: (quote)""",
"""
> 2012/10/27 Bert Tartopoils :
@@ -272,17 +272,17 @@ TEXT_2_OUT = ["""
# MISC
-GMAIL_1 = """Hello,<div><br></div><div>Ok for me. I am replying directly in gmail, without signature.</div><div><br></div><div>Kind regards,</div><div><br></div><div>Demo.<br><br><div>On Thu, Nov 8, 2012 at 5:29 PM, <span>&lt;<a href="mailto:dummy@example.com">dummy@example.com</a>&gt;</span> wrote:<br><blockquote><div>I contact you about our meeting for tomorrow. Here is the schedule I propose:</div><div><ul><li>9 AM: brainstorming about our new amazing business app&lt;/span&gt;&lt;/li&gt;</li>
+GMAIL_1 = u"""Hello,<div><br></div><div>Ok for me. I am replying directly in gmail, without signature.</div><div><br></div><div>Kind regards,</div><div><br></div><div>Demo.<br><br><div>On Thu, Nov 8, 2012 at 5:29 PM, <span>&lt;<a href="mailto:dummy@example.com">dummy@example.com</a>&gt;</span> wrote:<br><blockquote><div>I contact you about our meeting for tomorrow. Here is the schedule I propose:</div><div><ul><li>9 AM: brainstorming about our new amazing business app&lt;/span&gt;&lt;/li&gt;</li>
<li>9.45 AM: summary</li><li>10 AM: meeting with Fabien to present our app</li></ul></div><div>Is everything ok for you ?</div>
<div><p>-- <br>Administrator</p></div>
<div><p>Log in our portal at: <a href="http://localhost:8069#action=login&amp;db=mail_1&amp;login=demo">http://localhost:8069#action=login&amp;db=mail_1&amp;login=demo</a></p></div>
</blockquote></div><br></div>"""
-GMAIL_1_IN = ['Ok for me. I am replying directly in gmail, without signature.', '<blockquote data-o-mail-quote-node="1" data-o-mail-quote="1">']
+GMAIL_1_IN = [u'Ok for me. I am replying directly in gmail, without signature.', '<blockquote data-o-mail-quote-node="1" data-o-mail-quote="1">']
GMAIL_1_OUT = []
-HOTMAIL_1 = """<div>
+HOTMAIL_1 = u"""<div>
<div dir="ltr"><br>
I have an amazing company, i'm learning OpenERP, it is a small company yet, but plannig to grow up quickly.
<br><br>Kindest regards,<br>xxx<br>
@@ -309,19 +309,19 @@ HOTMAIL_1 = """<div>
</div>
</div>
</div>"""
-HOTMAIL_1_IN = ["""<div dir="ltr"><br>
+HOTMAIL_1_IN = [u"""<div dir="ltr"><br>
I have an amazing company, i'm learning OpenERP, it is a small company yet, but plannig to grow up quickly.
<br><br>Kindest regards,<br>xxx<br>"""]
HOTMAIL_1_OUT = [
- """<hr id="stopSpelling" data-o-mail-quote="1">""",
- """<pre data-o-mail-quote="1">
+ u"""<hr id="stopSpelling" data-o-mail-quote="1">""",
+ u"""<pre data-o-mail-quote="1">
<a href="http://openerp.com" target="_blank" data-o-mail-quote="1">http://openerp.com</a>
Belgium: +32.81.81.37.00
U.S.: +1 (650) 307-6736
India: +91 (79) 40 500 100
</pre>"""]
-MSOFFICE_1 = """
+MSOFFICE_1 = u"""
<div>
<div class="WordSection1">
<p class="MsoNormal">
@@ -423,8 +423,8 @@ MSOFFICE_1 = """
</div>
</div>"""
-MSOFFICE_1_IN = ['Our requirements are simple. Just looking to replace some spreadsheets for tracking quotes and possibly using the timecard module.']
-MSOFFICE_1_OUT = ['I noticed you recently downloaded OpenERP.', 'Uou mentioned you wish to use OpenERP in your own company.', 'Belgium: +32.81.81.37.00']
+MSOFFICE_1_IN = [u'Our requirements are simple. Just looking to replace some spreadsheets for tracking quotes and possibly using the timecard module.']
+MSOFFICE_1_OUT = [u'I noticed you recently downloaded OpenERP.', 'Uou mentioned you wish to use OpenERP in your own company.', 'Belgium: +32.81.81.37.00']
# ------------------------------------------------------------
@@ -432,7 +432,7 @@ MSOFFICE_1_OUT = ['I noticed you recently downloaded OpenERP.', 'Uou mentioned y
# ------------------------------------------------------------
# bug: read more not apparent, strange message in read more span
-BUG1 = """<pre>Hi Migration Team,
+BUG1 = u"""<pre>Hi Migration Team,
Paragraph 1, blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah blah blah blah
@@ -472,10 +472,10 @@ Tel: +32.81.81.37.00
Web: http://www.openerp.com</pre>"""
BUG_1_IN = [
- 'Hi Migration Team',
- 'Paragraph 1'
+ u'Hi Migration Team',
+ u'Paragraph 1'
]
-BUG_1_OUT = ["""
+BUG_1_OUT = [u"""
--
Olivier Laurent
Migration Manager
@@ -486,7 +486,7 @@ Tel: +32.81.81.37.00
Web: http://www.openerp.com"""]
-REMOVE_CLASS = """
+REMOVE_CLASS = u"""
<div style="FONT-SIZE: 12pt; FONT-FAMILY: 'Times New Roman'; COLOR: #000000">
<div>Hello</div>
<div>I have just installed Odoo 9 and I've got the following error:</div>
@@ -510,9 +510,9 @@ REMOVE_CLASS = """
</div>
"""
REMOVE_CLASS_IN = [
- '<div style="font-size: 12pt; font-family: \'Times New Roman\'; color: #000000">',
- 'An error occured in a modal and I will send you back the html to try opening one on your end']
+ u'<div style="font-size: 12pt; font-family: \'Times New Roman\'; color: #000000">',
+ u'An error occured in a modal and I will send you back the html to try opening one on your end']
REMOVE_CLASS_OUT = [
- '<div class="modal-backdrop in">',
- '<div class="modal-content openerp">',
- '<div class="modal-header">']
+ u'<div class="modal-backdrop in">',
+ u'<div class="modal-content openerp">',
+ u'<div class="modal-header">']
diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py
index 530f3e63862..b45de63cc71 100644
--- a/odoo/tools/mail.py
+++ b/odoo/tools/mail.py
@@ -170,7 +170,7 @@ def html_sanitize(src, silent=True, sanitize_tags=True, sanitize_attributes=Fals
src = ustr(src, errors='replace')
# html: remove encoding attribute inside tags
doctype = re.compile(r'(<[^>]*\s)(encoding=(["\'][^"\']*?["\']|[^\s\n\r>]+)(\s[^>]*|/)?>)', re.IGNORECASE | re.DOTALL)
- src = doctype.sub(r"", src)
+ src = doctype.sub(u"", src)
logger = logging.getLogger(__name__ + '.html_sanitize')
@@ -178,10 +178,10 @@ def html_sanitize(src, silent=True, sanitize_tags=True, sanitize_attributes=Fals
part = re.compile(r"(<(([^a<>]|a[^<>\s])[^<>]*)@[^<>]+>)", re.IGNORECASE | re.DOTALL)
# remove results containing cite="mid:email_like@address" (ex: blockquote cite)
# cite_except = re.compile(r"^((?!cite[\s]*=['\"]).)*$", re.IGNORECASE)
- src = part.sub(lambda m: ('cite=' not in m.group(1) and 'alt=' not in m.group(1)) and misc.html_escape(m.group(1)) or m.group(1), src)
+ src = part.sub(lambda m: (u'cite=' not in m.group(1) and u'alt=' not in m.group(1)) and misc.html_escape(m.group(1)) or m.group(1), src)
# html encode mako tags <% ... %> to decode them later and keep them alive, otherwise they are stripped by the cleaner
- src = src.replace('<%', misc.html_escape('<%'))
- src = src.replace('%>', misc.html_escape('%>'))
+ src = src.replace(u'<%', misc.html_escape(u'<%'))
+ src = src.replace(u'%>', misc.html_escape(u'%>'))
kwargs = {
'page_structure': True,
@@ -222,33 +222,34 @@ def html_sanitize(src, silent=True, sanitize_tags=True, sanitize_attributes=Fals
# some corner cases make the parser crash (such as <SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT> in test_mail)
cleaner = _Cleaner(**kwargs)
cleaned = cleaner.clean_html(src)
+ assert isinstance(cleaned, pycompat.text_type)
# MAKO compatibility: $, { and } inside quotes are escaped, preventing correct mako execution
- cleaned = cleaned.replace('%24', '$')
- cleaned = cleaned.replace('%7B', '{')
- cleaned = cleaned.replace('%7D', '}')
- cleaned = cleaned.replace('%20', ' ')
- cleaned = cleaned.replace('%5B', '[')
- cleaned = cleaned.replace('%5D', ']')
- cleaned = cleaned.replace('%7C', '|')
- cleaned = cleaned.replace('&lt;%', '<%')
- cleaned = cleaned.replace('%&gt;', '%>')
+ cleaned = cleaned.replace(u'%24', u'$')
+ cleaned = cleaned.replace(u'%7B', u'{')
+ cleaned = cleaned.replace(u'%7D', u'}')
+ cleaned = cleaned.replace(u'%20', u' ')
+ cleaned = cleaned.replace(u'%5B', u'[')
+ cleaned = cleaned.replace(u'%5D', u']')
+ cleaned = cleaned.replace(u'%7C', u'|')
+ cleaned = cleaned.replace(u'&lt;%', u'<%')
+ cleaned = cleaned.replace(u'%&gt;', u'%>')
# html considerations so real html content match database value
- cleaned.replace(u'\xa0', '&nbsp;')
+ cleaned.replace(u'\xa0', u'&nbsp;')
except etree.ParserError as e:
- if 'empty' in str(e):
- return ""
+ if u'empty' in pycompat.text_type(e):
+ return u""
if not silent:
raise
- logger.warning('ParserError obtained when sanitizing %r', src, exc_info=True)
- cleaned = '<p>ParserError when sanitizing</p>'
+ logger.warning(u'ParserError obtained when sanitizing %r', src, exc_info=True)
+ cleaned = u'<p>ParserError when sanitizing</p>'
except Exception:
if not silent:
raise
- logger.warning('unknown error obtained when sanitizing %r', src, exc_info=True)
- cleaned = '<p>Unknown error when sanitizing</p>'
+ logger.warning(u'unknown error obtained when sanitizing %r', src, exc_info=True)
+ cleaned = u'<p>Unknown error when sanitizing</p>'
# this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that
- if cleaned.startswith('<div>') and cleaned.endswith('</div>'):
+ if cleaned.startswith(u'<div>') and cleaned.endswith(u'</div>'):
cleaned = cleaned[5:-6]
return cleaned