PlainTextPre

From Request Tracker Wiki
Jump to navigation Jump to search

This patch is at http://issues.bestpractical.com/Ticket/Display.html?id=8374. It was tested with 3.6.1 and I believe it will work as-is with 3.6.3.

Many of the messages our RT instance handles contain space- and tab-based formatting in plain text. The way RT displays them (as HTML with <br> used to preserve line breaks) causes this formatting to be lost. Eg, this text:

left side           right side
 ---------           ----------
 item one            value one
 item two            value two
 
 

won't look right on the web page with a standard RT setup.

I couldn't figure out a clean way to change this via an overlay or callback. One problem is the <br>s get added after the callback returns. Another is that you want to use one <pre> for the whole message body (because it introduces vertical space the way a <p> does), but ShowMessageStanza is called for each blob of text Text::Quoted extracts. Additionally the standard ShowMessageStanza adds newlines around the <div>s, and these cause blank lines inside <pre>.

There might be a way to do have space- and tab-based formatting work based on "white-space: pre;" style instead of <pre>, but this didn't line things up correctly when I tried it. I am no HTML expert.

Here's the solution I came up with. It adds a new config variable, $RT::PlainTextPre, which allows you to tell the system to use <pre> on plain text message bodies.

--- etc/RT_Config.pm.in.~1~	2006-10-25 23:18:11.000000000 -0400
 +++ etc/RT_Config.pm.in	2007-05-10 15:34:04.000000000 -0400
 @@ -463,6 +463,13 @@
 
  Set($ShowTransactionImages, 1);
 
 +# Normally plain text attachments are displayed as HTML with line
 +# breaks preserved.  This causes space- and tab-based formatting not
 +# to be displayed correctly.  By setting $PlainTextPre they'll be
 +# displayed using <pre> instead so such formatting works, but they'll
 +# use a monospaced font.
 +
 +Set($PlainTextPre, 0);
 
  # $HomepageComponents is an arrayref of allowed components on a user's
  # customized homepage ("RT at a glance").
 --- html/Ticket/Elements/ShowMessageStanza.~1~	2006-06-19 18:44:04.000000000 -0400
 +++ html/Ticket/Elements/ShowMessageStanza	2007-05-10 14:52:13.000000000 -0400
 @@ -1,3 +1,4 @@
 +%# $Id: PlainTextPre,v 1.1 2007/05/10 20:30:13 www-data Exp www-data $
  %# BEGIN BPS TAGGED BLOCK {{{
  %#
  %# COPYRIGHT:
 @@ -43,35 +44,73 @@
  %# those contributions and any derivatives thereof.
  %#
  %# END BPS TAGGED BLOCK }}}
 -% if (ref($Message)) {
 -<div class="message-stanza-depth-<% $Depth %>">
  <%perl>
 -foreach my $stanza (@$Message) {
 -    if ( ref $stanza eq "ARRAY" ) {
 -        $m->comp( 'ShowMessageStanza',
 -                  Depth   => $Depth + 1,
 -                  Transaction => $Transaction,
 -                  Message => $stanza );
 -    }
 -    elsif ( ref $stanza eq "HASH" ) {
 -        my $content = $stanza->{raw};
 -        RT::Interface::Web::EscapeUTF8(\$content);
 -        $m->comp('/Elements/Callback', content => \$content, %ARGS);
 -        $content =~ s{$}{<br />}mg
 -            if defined $content;
 
 +# This component is called to display plain text attachments.  At the
 +# top level it's passed the data structure returned by Text::Quoted,
 +# then it uses itself recursively to display it.
 +#
 +# By default the messages are displayed as simple escapped HTML.  Line
 +# breaks are preserved by adding <br>.
 +#
 +# If you set $RT::PlainTextPre then the messages are displayed inside a
 +# <pre> section.  This allows space- and tab-based formatting to work,
 +# at the cost of using a monospaced font.
 +#
 +# There might be a way to do have space- and tab-based formatting work
 +# based on "white-space: pre;" style instead of <pre>, but this didn't
 +# line things up correctly when I tried it.  I am no HTML expert.
 +
 +# Because the output might be inside <pre> this code has to be careful
 +# not to introduce extra newlines.
 +
 +# An oddity of Text::Quoted is that it strips the trailing newline from
 +# each returned strings.  The strings can be multi-line, they'll always
 +# be missing the newline at the end.
 +
 +if (ref($Message)) {
 +    my ($pon, $poff) = ($RT::PlainTextPre && $Depth == 0)
 +                            ? ('<pre>', '</pre>')
 +                            : ('', '');
 +    $m->out($pon);
 +    $m->out(qq(<div class="message-stanza-depth-$Depth">));
 +    foreach my $stanza (@$Message) {
 +        if ( ref $stanza eq "ARRAY" ) {
 +            $m->comp( 'ShowMessageStanza',
 +                      Depth   => $Depth + 1,
 +                      Transaction => $Transaction,
 +                      Message => $stanza );
 +        }
 +        elsif ( ref $stanza eq "HASH" ) {
 +            my $content = $stanza->{raw};
 +            RT::Interface::Web::EscapeUTF8(\$content);
 +            $m->comp('/Elements/Callback', content => \$content, %ARGS);
 +            # I put the trailing \n back here, after the callback,
 +            # because the callback was historically called without it
 +            # attached.
 +            $content .= "\n";
 +            $content =~ s{\n}{<br />\n}mg
 +                if !$RT::PlainTextPre;
 +            $m->out($content);
 +        }
 +    }
 +    $m->out('</div>');
 +    $m->out($poff);
 +} else {
 +    my $content = $Message;
 +    RT::Interface::Web::EscapeUTF8(\$content);
 +    $m->comp('/Elements/Callback', content => \$content, %ARGS);
 +    $content .= "\n"
 +        if $content !~ /\n$/;
 +    if ($RT::PlainTextPre) {
 +        $content = "<pre>$content</pre>";
 +    }
 +    else {
 +        $content =~ s{\n}{<br />\n}mg;
 +    }
 +    $m->out($content);
 +}
  </%perl>
 -<%$content |n%>
 -%   }
 -% } # end foreach
 -</div>
 -% } else {
 -%       my $content = $Message;
 -%       RT::Interface::Web::EscapeUTF8(\$content);
 -%       $m->comp('/Elements/Callback', content => \$content, %ARGS);
 -%       $content =~ s{$}{<br />}mg;
 -<%$content |n%>
 -% }
  <%INIT>
  use URI::URL;
  </%INIT>