From f2a795a648bae76f298285fc35a0a47a86c91e4e Mon Sep 17 00:00:00 2001 From: Robert Klep Date: Wed, 17 Sep 2014 12:50:51 +0200 Subject: [PATCH] Fixed customized attributions for Yosemite --- quotefix/attribution.py | 199 ++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 110 deletions(-) diff --git a/quotefix/attribution.py b/quotefix/attribution.py index 4a8c5a0..66a3ae8 100644 --- a/quotefix/attribution.py +++ b/quotefix/attribution.py @@ -22,12 +22,19 @@ class MCMessageHeaders(Category(MCMessageHeaders)): def registerQuoteFixApplication(cls, app): cls.app = app -# XXX: doesn't exist on Yosemite -# @swizzle(MCMessageHeaders, 'htmlStringShowingHeaderDetailLevel:useBold:useGray:') -# def htmlStringShowingHeaderDetailLevel_useBold_useGray_(self, original, level, bold, gray): -# if self.app.use_custom_forwarding_attribution and self.app.remove_apple_mail_forward_attribution: -# return '' -# return original(self, level, bold, gray) + try: + @swizzle(MCMessageHeaders, 'htmlStringShowingHeaderDetailLevel:useBold:useGray:') + def htmlStringShowingHeaderDetailLevel_useBold_useGray_(self, original, level, bold, gray): + if self.app.use_custom_forwarding_attribution and self.app.remove_apple_mail_forward_attribution: + return '' + return original(self, level, bold, gray) + except: + # Yosemite + @swizzle(MCMessageHeaders, 'htmlStringUseBold:useGray:') + def htmlStringUseBold_useGray_(self, original, bold, gray): + if self.app.use_custom_forwarding_attribution and self.app.remove_apple_mail_forward_attribution: + return '' + return original(self, bold, gray) MessageHeaders = MCMessageHeaders except: from AppKit import MessageHeaders @@ -95,6 +102,20 @@ def customize_attribution(cls, original, editor, dom, reply, inreplyto, template is_reply = messagetype == REPLY is_sendagain = messagetype == SENDAGAIN + attribution = cls.render_attribution( + reply = reply, + inreplyto = inreplyto, + template = template, + is_html = False, + ) + + # replace leading whitespace with non-breaking spaces + attribution = re.sub(r'(?m)^( +)' , lambda m: u'\u00a0' * len(m.group(1)), attribution) + attribution = re.sub(r'(?m)^(\t+)', lambda m: u'\u00a0\u00a0' * len(m.group(1)), attribution) + + # replace newlines with hard linebreaks + attribution = attribution.replace('\n', '
') + # create matcher for matching original attribution (and replace # nsbp's with normal spaces) if original: @@ -104,114 +125,72 @@ def customize_attribution(cls, original, editor, dom, reply, inreplyto, template original = re.sub(r'\s+', '\\s+', original) matcher = re.compile(original) else: - matcher = None - - # find possible nodes which can contain attribution - root = dom.documentElement() - if is_sendagain: - # Special case: Mail doesn't include an attribution for Send Again messages, - # so we'll just use the root element - node = root - children = node.getElementsByTagName_('body') - else: - nodes = root.getElementsByClassName_('AppleOriginalContents') - if not nodes.length(): - nodes = root.getElementsByClassName_('ApplePlainTextBody') - if not nodes.length(): - return False - node = nodes.item_(0) - children = node.childNodes() + matcher = None # rich text message? is_rich = editor.backEnd().containsRichText() - # when message isn't rich text, replace  's with regular spaces to - # prevent issues with finding the attribution node (XXX: this *may* - # break other parts of the message, to perhaps we should limit this to - # only the start of the message somehow) - if not is_rich: - node.setInnerHTML_(node.innerHTML().replace(' ', ' ')) - children = node.childNodes() - - # check children for attribution node - for i in range(children.length()): - child = children.item_(i) - if not is_sendagain: - if child.nodeType() == 1: - html = child.innerHTML() - if matcher and not matcher.match(html): - continue - elif child.nodeType() == 3: - text = child.data() - if matcher and not matcher.match(text): - continue - - # should attribution be treated as HTML? - is_html = (is_forward and cls.app.custom_forwarding_is_html) or \ - (is_sendagain and cls.app.custom_sendagain_is_html) or \ - (is_reply and cls.app.custom_reply_is_html) - - # check if message is rich text with HTML-attribution - if is_html and not is_rich: - if (is_forward and cls.app.custom_forwarding_convert_to_rich) or \ - (is_sendagain and cls.app.custom_sendagain_convert_to_rich) or \ - (is_reply and cls.app.custom_reply_convert_to_rich): - editor.makeRichText_(editor) - elif not cls.app.dont_show_html_attribution_warning: - idx = NSRunAlertPanel( - "QuoteFix warning", - "You are using an HTML-attribution, but the current message format is plain text.\n\n" + - "Unless you convert to rich text, the HTML-formatting will be lost when sending the message.", - "OK", - "Don't show this warning again", - None - ) - if idx == 0: - cls.app.dont_show_html_attribution_warning = True - - # render attribution - attribution = cls.render_attribution( - reply = reply, - inreplyto = inreplyto, - template = template, - is_html = is_html, - ) - - # replace leading whitespace with non-breaking spaces - attribution = re.sub(r'(?m)^( +)' , lambda m: u'\u00a0' * len(m.group(1)), attribution) - attribution = re.sub(r'(?m)^(\t+)', lambda m: u'\u00a0\u00a0' * len(m.group(1)), attribution) - - # replace newlines with hard linebreaks - attribution = attribution.replace('\n', '
') - - # replace old attribution with new, depending on node type - if is_sendagain: - newnode = dom.createElement_("span") - newnode.setInnerHTML_(attribution) - child.insertBefore_refChild_(newnode, child.firstChild()) - copynode = newnode - elif child.nodeType() == 1: - child.setInnerHTML_(attribution) - copynode = child - else: - newnode = dom.createElement_("span") - newnode.setInnerHTML_(attribution) - node.replaceChild_oldChild_(newnode, child) - copynode = newnode - - # increase quote level of attribution? - if (is_forward and cls.app.custom_forwarding_increase_quotelevel) or \ - (is_reply and cls.app.custom_reply_increase_quotelevel): - copy = copynode.cloneNode_(True) - copynode.parentNode().removeChild_(copynode) - blockquote = root.firstDescendantBlockQuote() - blockquote.insertBefore_refChild_(copy, blockquote.childNodes().item_(0)) - - # done - return True - - # done nothing - return False + # should attribution be treated as HTML? + is_html = (is_forward and cls.app.custom_forwarding_is_html) or \ + (is_sendagain and cls.app.custom_sendagain_is_html) or \ + (is_reply and cls.app.custom_reply_is_html) + + # check if message is rich text with HTML-attribution + if is_html and not is_rich: + if (is_forward and cls.app.custom_forwarding_convert_to_rich) or \ + (is_sendagain and cls.app.custom_sendagain_convert_to_rich) or \ + (is_reply and cls.app.custom_reply_convert_to_rich): + editor.makeRichText_(editor) + elif not cls.app.dont_show_html_attribution_warning: + idx = NSRunAlertPanel( + "QuoteFix warning", + "You are using an HTML-attribution, but the current message format is plain text.\n\n" + + "Unless you convert to rich text, the HTML-formatting will be lost when sending the message.", + "OK", + "Don't show this warning again", + None + ) + if idx == 0: + cls.app.dont_show_html_attribution_warning = True + + # render attribution + attribution = cls.render_attribution( + reply = reply, + inreplyto = inreplyto, + template = template, + is_html = is_html, + ) + + # replace leading whitespace with non-breaking spaces + attribution = re.sub(r'(?m)^( +)' , lambda m: u'\u00a0' * len(m.group(1)), attribution) + attribution = re.sub(r'(?m)^(\t+)', lambda m: u'\u00a0\u00a0' * len(m.group(1)), attribution) + + # replace newlines with hard linebreaks + attribution = attribution.replace('\n', '
') + + # Get HTML contents of e-mail. + root = dom.documentElement() + html = root.innerHTML() + + # Special case: Mail doesn't include an attribution for Send Again messages, + # so we'll just add a customized attribution right after the element. + if is_sendagain or matcher == None: + html = re.sub(r'(?i)(?P<\s?body.*?>)', r'\g' + attribution, html) + elif matcher: + html = matcher.sub(attribution, html) + + # Restore HTML of root element. + root.setInnerHTML_(html) + + # TODO: increase quote level of attribution? +# if (is_forward and cls.app.custom_forwarding_increase_quotelevel) or \ +# (is_reply and cls.app.custom_reply_increase_quotelevel): +# copy = copynode.cloneNode_(True) +# copynode.parentNode().removeChild_(copynode) +# blockquote = root.firstDescendantBlockQuote() +# blockquote.insertBefore_refChild_(copy, blockquote.childNodes().item_(0)) + + return True @classmethod def render_attribution(cls, reply, inreplyto, template, is_html):