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