[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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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é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><<a href="mailto:dummy@example.com">dummy@example.com</a>></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</span></li></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><<a href="mailto:dummy@example.com">dummy@example.com</a>></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</span></li></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&db=mail_1&login=demo">http://localhost:8069#action=login&db=mail_1&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
@@ -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('<%', '<%')
|
||||
cleaned = cleaned.replace('%>', '%>')
|
||||
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'<%', u'<%')
|
||||
cleaned = cleaned.replace(u'%>', u'%>')
|
||||
# html considerations so real html content match database value
|
||||
cleaned.replace(u'\xa0', ' ')
|
||||
cleaned.replace(u'\xa0', u' ')
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user