begin require "escape_utils" rescue LoadError => _ abort "Missing dependency 'escape_utils' for EmailReplyFilter. See README.md for details." end begin require "email_reply_parser" rescue LoadError => _ abort "Missing dependency 'email_reply_parser' for EmailReplyFilter. See README.md for details." end module HTML class Pipeline # HTML Filter that converts email reply text into an HTML DocumentFragment. # It must be used as the first filter in a pipeline. # # Context options: # None # # This filter does not write any additional information to the context hash. class EmailReplyFilter < TextFilter include EscapeUtils EMAIL_HIDDEN_HEADER = %(<span class="email-hidden-toggle"><a href="#">…</a></span><div class="email-hidden-reply" style="display:none">).freeze EMAIL_QUOTED_HEADER = %(<div class="email-quoted-reply">).freeze EMAIL_SIGNATURE_HEADER = %(<div class="email-signature-reply">).freeze EMAIL_FRAGMENT_HEADER = %(<div class="email-fragment">).freeze EMAIL_HEADER_END = "</div>".freeze # Scans an email body to determine which bits are quoted and which should # be hidden. EmailReplyParser is used to split the comment into an Array # of quoted or unquoted Blocks. Now, we loop through them and attempt to # add <div> tags around them so we can hide the hidden blocks, and style # the quoted blocks differently. Since multiple blocks may be hidden, be # sure to keep the "email-hidden-reply" <div>s around "email-quoted-reply" # <div> tags. Call this on each comment of a visible thread in the order # that they are displayed. Note: all comments are processed so we can # maintain a Set of SHAs of paragraphs. Only plaintext comments skip the # markdown step. # # Returns the email comment HTML as a String def call found_hidden = nil paragraphs = EmailReplyParser.read(text.dup).fragments.map do |fragment| pieces = [escape_html(fragment.to_s.strip).gsub(/^\s*(>|>)/, '')] if fragment.quoted? pieces.unshift EMAIL_QUOTED_HEADER pieces << EMAIL_HEADER_END elsif fragment.signature? pieces.unshift EMAIL_SIGNATURE_HEADER pieces << EMAIL_HEADER_END else pieces.unshift EMAIL_FRAGMENT_HEADER pieces << EMAIL_HEADER_END end if fragment.hidden? && !found_hidden found_hidden = true pieces.unshift EMAIL_HIDDEN_HEADER end pieces.join end paragraphs << EMAIL_HEADER_END if found_hidden paragraphs.join("\n") end end end end