From 6bc32a8b4bec08accba274ac3c12751b285312ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 17 Oct 2013 13:05:25 +0200 Subject: [PATCH] [FIX] tools: mail: fixed / improved html_email_clean. Read more links could not appear when the shorten limit was obtained inside a quote that is removed in the process, removing the read more link node. It now get the first parent that is not inside a quote to be sure the link is displayed. Added the test-case that helped find this bug. bzr revid: tde@openerp.com-20131017110525-etn0u5s91zbnvul1 --- openerp/tests/test_mail.py | 6 + openerp/tests/test_mail_examples.py | 263 ++++++++++++++++++++++++++++ openerp/tools/mail.py | 63 ++++--- 3 files changed, 311 insertions(+), 21 deletions(-) diff --git a/openerp/tests/test_mail.py b/openerp/tests/test_mail.py index fd27ebdf833..55265760627 100755 --- a/openerp/tests/test_mail.py +++ b/openerp/tests/test_mail.py @@ -230,6 +230,12 @@ class TestCleaner(unittest2.TestCase): for ext in test_mail_examples.BUG_1_OUT: self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content') + new_html = html_email_clean(test_mail_examples.BUG2, remove=True, shorten=True, max_length=100) + for ext in test_mail_examples.BUG_2_IN: + self.assertIn(ext, new_html, 'html_email_cleaner wrongly removed valid content') + for ext in test_mail_examples.BUG_2_OUT: + self.assertNotIn(ext, new_html, 'html_email_cleaner did not removed invalid content') + def test_90_misc(self): # False boolean for text must return empty string new_html = html_email_clean(False) diff --git a/openerp/tests/test_mail_examples.py b/openerp/tests/test_mail_examples.py index 9ad058c380b..54671106334 100644 --- a/openerp/tests/test_mail_examples.py +++ b/openerp/tests/test_mail_examples.py @@ -693,3 +693,266 @@ BUG_1_OUT = [ '81.81.37.00', 'openerp.com', ] + + +BUG2 = """ +
+
+

+
+ -------- Original Message -------- + + + + + + + + + + + + + + + + + + + +
Subject: + Fwd: TR: OpenERP S.A. Payment Reminder
Date: Wed, 16 Oct 2013 14:11:13 +0200
From: Christine Herrmann <che@openerp.com>
To: online@openerp.com
+
+
+ +
+

+
+ -------- Message original -------- + + + + + + + + + + + + + + + + + + + + + + + +
Sujet: + TR: OpenERP S.A. Payment Reminder
Date : + Wed, 16 Oct 2013 10:34:45 -0000
De : Ida Siwatala <infos@inzoservices.com>
Répondre + + à : catchall@openerp.my.openerp.com
Pour : + Christine Herrmann (che) <che@openerp.com>
+
+
+
+
+

Bonjour,

+

+

 

+

Pourriez-vous + + me faire un retour sur ce point.

+

+

 

+

Cordialement

+

+

 

+
+
+

De : + Ida Siwatala [mailto:infos@inzoservices.com] +
+ Envoyé : vendredi 4 octobre 2013 20:03
+ À : 'Followers of + INZO-services-8-all-e-Maxime-Lisbonne-77176-Savigny-le-temple-France'
+ Objet : RE: OpenERP S.A. Payment Reminder

+
+
+

 

+

Bonsoir,

+

+

 

+

Je + + me permets de revenir vers vous par écrit , car j’ai + fait 2 appels vers votre service en exposant mon + problème, mais je n’ai pas eu de retour.

+

Cela + + fait un mois que j’ai fait la souscription de votre + produit, mais je me rends compte qu’il est pas adapté à + ma situation ( fonctionnalité manquante et surtout je + n’ai pas beaucoup de temps à passer à résoudre des + bugs).

+

C’est + + pourquoi , j’ai demandé qu’un accord soit trouvé avec + vous pour annuler le contrat (tout en vous payant le + mois d’utilisation de septembre).

+

+

 

+

Pourriez-vous + + me faire un retour sur ce point.

+

+

 

+

Cordialement,

+

+

 

+

Ida + + Siwatala

+

+

 

+

De : + che@openerp.com + [mailto:che@openerp.com] +
+ Envoyé : vendredi 4 octobre 2013 17:41
+ À : infos@inzoservices.com
+ Objet : OpenERP S.A. Payment Reminder

+

 

+
+

Dear + + INZO services,

+

Exception + + made if there was a mistake of ours, it seems that the + following amount stays unpaid. Please, take + appropriate measures in order to carry out this + payment in the next 8 days.

+

+

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Date de facturation

+
+

Description

+
+

Reference

+
+

Due Date

+
+

Amount (€)

+
+

Lit.

+
+

2013-09-24

+
+

2013/1121

+
+

Enterprise - Inzo Services + - Juillet 2013

+
+

2013-09-24

+
+

420.0

+

+

+

+

+

+

+

+
+

Amount + + due : 420.00 €

+

Would + + your payment have been carried out after this mail was + sent, please ignore this message. Do not hesitate to + contact our accounting department.

+


+ Best Regards,
+ Aurore Lesage
+ OpenERP
+ Chaussée de Namur, 40
+ B-1367 Grand Rosières
+ Tel: +32.81.81.37.00 - Fax: +32.81.73.35.01
+ E-mail : ale@openerp.com
+ Web: http://www.openerp.com

+
+
+
+ --
+ INZO services Sent by OpenERP + S.A. using OpenERP. + Access your messages and documents in + + OpenERP
+
-- 
+Christine Herrmann 
+
+OpenERP 
+Chaussée de Namur, 40 
+B-1367 Grand Rosières 
+Tel: +32.81.81.37.00 - Fax: +32.81.73.35.01 
+
+Web: http://www.openerp.com 
+
+
+
+
+
+
+ +
""" + +BUG_2_IN = [ + 'read more', + '...', +] +BUG_2_OUT = [ + 'Fwd: TR: OpenERP S.A' + 'fait un mois' +] diff --git a/openerp/tools/mail.py b/openerp/tools/mail.py index 879e25171be..528140e90dc 100644 --- a/openerp/tools/mail.py +++ b/openerp/tools/mail.py @@ -175,6 +175,32 @@ def html_email_clean(html, remove=False, shorten=False, max_length=300): iteration += 1 new_node = _insert_new_node(node, -1, new_node_tag, text[idx:] + (cur_node.tail or ''), None, {}) + def _truncate_node(node, position, find_first_blank=True): + if node.text is None: + node.text = '' + # truncate text + end_position = position if len(node.text) >= position else len(node.text) + innertext = node.text[0:end_position] + outertext = node.text[end_position:] + if find_first_blank: + stop_idx = outertext.find(' ') + if stop_idx == -1: + stop_idx = len(outertext) + else: + stop_idx = 0 + node.text = innertext + outertext[0:stop_idx] + # create ... read more node + read_more_node = _create_node('span', ' ... ', None, {'class': 'oe_mail_expand'}) + read_more_link_node = _create_node('a', 'read more', None, {'href': '#', 'class': 'oe_mail_expand'}) + read_more_node.append(read_more_link_node) + # create outertext node + overtext_node = _create_node('span', outertext[stop_idx:]) + # tag node + overtext_node.set('in_overlength', '1') + # add newly created nodes in dom + node.append(read_more_node) + node.append(overtext_node) + if not html or not isinstance(html, basestring): return html html = ustr(html) @@ -226,14 +252,16 @@ def html_email_clean(html, remove=False, shorten=False, max_length=300): if 'SkyDrivePlaceholder' in node.get('class', '') or 'SkyDrivePlaceholder' in node.get('id', ''): root.set('hotmail', '1') - # state of the parsing + # state of the parsing: flag quotes and tails to remove if quote_begin: node.set('in_quote', '1') node.set('tail_remove', '1') + # state of the parsing: flag when being in over-length content if overlength: node.set('in_overlength', '1') node.set('tail_remove', '1') + # find quote in msoffice / hotmail / blockquote / text quote and signatures if root.get('msoffice') and node.tag == 'div' and 'border-top:solid' in node.get('style', ''): quote_begin = True node.set('in_quote', '1') @@ -242,35 +270,28 @@ def html_email_clean(html, remove=False, shorten=False, max_length=300): quote_begin = True node.set('in_quote', '1') node.set('tail_remove', '1') + if node.tag == 'blockquote' or node.get('text_quote') or node.get('text_signature'): + node.set('in_quote', '1') # shorten: # 1/ truncate the text at the next available space # 2/ create a 'read more' node, next to current node # 3/ add the truncated text in a new node, next to 'read more' node if shorten and not overlength and cur_char_nbr + len(node.text or '') > max_length: + node_to_truncate = node + while node.get('in_quote') and node.getparent(): + node_to_truncate = node.getparent() overlength = True - # truncate text - innertext = node.text[0:(max_length - cur_char_nbr)] - outertext = node.text[(max_length - cur_char_nbr):] - stop_idx = outertext.find(' ') - if stop_idx == -1: - stop_idx = len(outertext) - node.text = innertext + outertext[0:stop_idx] - # create ... read more node - read_more_node = _create_node('span', ' ... ', None, {'class': 'oe_mail_expand'}) - read_more_link_node = _create_node('a', 'read more', None, {'href': '#', 'class': 'oe_mail_expand'}) - read_more_node.append(read_more_link_node) - # create outertext node - new_node = _create_node('span', outertext[stop_idx:]) - # add newly created nodes in dom - node.append(read_more_node) - # tag node - new_node.set('in_overlength', '1') + node_to_truncate.set('truncate', '1') + node_to_truncate.set('truncate_position', str(max_length - cur_char_nbr)) + cur_char_nbr += len(node.text or '') - cur_char_nbr += len(node.text or '') + # Tree modification + # ------------------------------------------------------------ - if node.tag == 'blockquote' or node.get('text_quote') or node.get('text_signature'): - node.set('in_quote', '1') + for node in root.iter(): + if node.get('truncate'): + _truncate_node(node, int(node.get('truncate_position', '0'))) # Post processing # ------------------------------------------------------------