[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
This commit is contained in:
Xavier Morel
2017-08-20 23:25:54 +02:00
committed by Olivier Dony
parent ae866acf08
commit 200612ca1e
8 changed files with 112 additions and 120 deletions
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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
+1 -1
View File
@@ -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
+33 -42
View File
@@ -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)
+1 -1
View File
@@ -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):
+7 -7
View File
@@ -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
+42 -42
View File
@@ -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">']
+22 -21
View File
@@ -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