diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py
index 0f6a8c86b30..b5477caa3d3 100644
--- a/odoo/tools/mail.py
+++ b/odoo/tools/mail.py
@@ -76,7 +76,7 @@ class _Cleaner(clean.Cleaner):
def __call__(self, doc):
# perform quote detection before cleaning and class removal
for el in doc.iter(tag=etree.Element):
- self.tag_quote(el)
+ tag_quote(el)
super(_Cleaner, self).__call__(doc)
@@ -90,74 +90,6 @@ class _Cleaner(clean.Cleaner):
for el in doc.iter(tag=etree.Element):
self.parse_style(el)
- def tag_quote(self, el):
- def _create_new_node(tag, text, tail=None, attrs=None):
- new_node = etree.Element(tag)
- new_node.text = text
- new_node.tail = tail
- if attrs:
- for key, val in attrs.items():
- new_node.set(key, val)
- return new_node
-
- def _tag_matching_regex_in_text(regex, node, tag='span', attrs=None):
- text = node.text or ''
- if not re.search(regex, text):
- return
-
- child_node = None
- idx, node_idx = 0, 0
- for item in re.finditer(regex, text):
- new_node = _create_new_node(tag, text[item.start():item.end()], None, attrs)
- if child_node is None:
- node.text = text[idx:item.start()]
- new_node.tail = text[item.end():]
- node.insert(node_idx, new_node)
- else:
- child_node.tail = text[idx:item.start()]
- new_node.tail = text[item.end():]
- node.insert(node_idx, new_node)
- child_node = new_node
- idx = item.end()
- node_idx = node_idx + 1
-
- el_class = el.get('class', '') or ''
- el_id = el.get('id', '') or ''
-
- # gmail or yahoo // # outlook, html // # msoffice
- if 'gmail_extra' in el_class or \
- 'divRplyFwdMsg' in el_id or \
- ('SkyDrivePlaceholder' in el_class or 'SkyDrivePlaceholder' in el_class):
- el.set('data-o-mail-quote', '1')
- if el.getparent() is not None:
- el.getparent().set('data-o-mail-quote-container', '1')
-
- if (el.tag == 'hr' and ('stopSpelling' in el_class or 'stopSpelling' in el_id)) or \
- 'yahoo_quoted' in el_class:
- # Quote all elements after this one
- el.set('data-o-mail-quote', '1')
- for sibling in el.itersiblings(preceding=False):
- sibling.set('data-o-mail-quote', '1')
-
- # html signature (--
blah)
- signature_begin = re.compile(r"((?:(?:^|\n)[-]{2}[\s]?$))")
- if el.text and el.find('br') is not None and re.search(signature_begin, el.text):
- el.set('data-o-mail-quote', '1')
- if el.getparent() is not None:
- el.getparent().set('data-o-mail-quote-container', '1')
-
- # text-based quotes (>, >>) and signatures (-- Signature)
- text_complete_regex = re.compile(r"((?:\n[>]+[^\n\r]*)+|(?:(?:^|\n)[-]{2}[\s]?[\r\n]{1,2}[\s\S]+))")
- if not el.get('data-o-mail-quote'):
- _tag_matching_regex_in_text(text_complete_regex, el, 'span', {'data-o-mail-quote': '1'})
-
- if el.tag == 'blockquote':
- # remove single node
- el.set('data-o-mail-quote-node', '1')
- el.set('data-o-mail-quote', '1')
- if el.getparent() is not None and (el.getparent().get('data-o-mail-quote') or el.getparent().get('data-o-mail-quote-container')) and not el.getparent().get('data-o-mail-quote-node'):
- el.set('data-o-mail-quote', '1')
-
def strip_class(self, el):
if el.attrib.get('class'):
del el.attrib['class']
@@ -177,6 +109,75 @@ class _Cleaner(clean.Cleaner):
del el.attrib['style']
+def tag_quote(el):
+ def _create_new_node(tag, text, tail=None, attrs=None):
+ new_node = etree.Element(tag)
+ new_node.text = text
+ new_node.tail = tail
+ if attrs:
+ for key, val in attrs.items():
+ new_node.set(key, val)
+ return new_node
+
+ def _tag_matching_regex_in_text(regex, node, tag='span', attrs=None):
+ text = node.text or ''
+ if not re.search(regex, text):
+ return
+
+ child_node = None
+ idx, node_idx = 0, 0
+ for item in re.finditer(regex, text):
+ new_node = _create_new_node(tag, text[item.start():item.end()], None, attrs)
+ if child_node is None:
+ node.text = text[idx:item.start()]
+ new_node.tail = text[item.end():]
+ node.insert(node_idx, new_node)
+ else:
+ child_node.tail = text[idx:item.start()]
+ new_node.tail = text[item.end():]
+ node.insert(node_idx, new_node)
+ child_node = new_node
+ idx = item.end()
+ node_idx = node_idx + 1
+
+ el_class = el.get('class', '') or ''
+ el_id = el.get('id', '') or ''
+
+ # gmail or yahoo // # outlook, html // # msoffice
+ if 'gmail_extra' in el_class or \
+ 'divRplyFwdMsg' in el_id or \
+ ('SkyDrivePlaceholder' in el_class or 'SkyDrivePlaceholder' in el_class):
+ el.set('data-o-mail-quote', '1')
+ if el.getparent() is not None:
+ el.getparent().set('data-o-mail-quote-container', '1')
+
+ if (el.tag == 'hr' and ('stopSpelling' in el_class or 'stopSpelling' in el_id)) or \
+ 'yahoo_quoted' in el_class:
+ # Quote all elements after this one
+ el.set('data-o-mail-quote', '1')
+ for sibling in el.itersiblings(preceding=False):
+ sibling.set('data-o-mail-quote', '1')
+
+ # html signature (--
blah)
+ signature_begin = re.compile(r"((?:(?:^|\n)[-]{2}[\s]?$))")
+ if el.text and el.find('br') is not None and re.search(signature_begin, el.text):
+ el.set('data-o-mail-quote', '1')
+ if el.getparent() is not None:
+ el.getparent().set('data-o-mail-quote-container', '1')
+
+ # text-based quotes (>, >>) and signatures (-- Signature)
+ text_complete_regex = re.compile(r"((?:\n[>]+[^\n\r]*)+|(?:(?:^|\n)[-]{2}[\s]?[\r\n]{1,2}[\s\S]+))")
+ if not el.get('data-o-mail-quote'):
+ _tag_matching_regex_in_text(text_complete_regex, el, 'span', {'data-o-mail-quote': '1'})
+
+ if el.tag == 'blockquote':
+ # remove single node
+ el.set('data-o-mail-quote-node', '1')
+ el.set('data-o-mail-quote', '1')
+ if el.getparent() is not None and (el.getparent().get('data-o-mail-quote') or el.getparent().get('data-o-mail-quote-container')) and not el.getparent().get('data-o-mail-quote-node'):
+ el.set('data-o-mail-quote', '1')
+
+
def html_sanitize(src, silent=True, sanitize_tags=True, sanitize_attributes=False, sanitize_style=False, sanitize_form=True, strip_style=False, strip_classes=False):
if not src:
return src