[Rt-commit] rt branch, 4.2/inline-images, created. rt-4.1.8-24-gbba8478
Thomas Sibley
trs at bestpractical.com
Wed Apr 10 15:08:47 EDT 2013
The branch, 4.2/inline-images has been created
at bba8478f4fde765a7b343b8e395f50f210360ed9 (commit)
- Log -----------------------------------------------------------------
commit fbf575d05da309d134dff8e75a1527d2d8798ecb
Author: Thomas Sibley <trs at bestpractical.com>
Date: Sat Jun 30 22:38:01 2012 -0700
Method to find the siblings of an RT::Attachment
Siblings are attachments on the same MIME part level as each other.
This is especially useful for children of multipart/related parents,
since the first child usually embeds its siblings in its content.
diff --git a/lib/RT/Attachment.pm b/lib/RT/Attachment.pm
index be01ea9..a7e2985 100644
--- a/lib/RT/Attachment.pm
+++ b/lib/RT/Attachment.pm
@@ -278,6 +278,30 @@ sub Children {
return($kids);
}
+=head2 Siblings
+
+Returns an L<RT::Attachments> object containing all the attachments sharing
+the same immediate parent as the current object, excluding the current
+attachment itself.
+
+If the current attachment is a top-level part (i.e. Parent == 0) then a
+guaranteed empty L<RT::Attachments> object is returned.
+
+=cut
+
+sub Siblings {
+ my $self = shift;
+ my $siblings = RT::Attachments->new( $self->CurrentUser );
+ if ($self->Parent) {
+ $siblings->ChildrenOf( $self->Parent );
+ $siblings->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->Id );
+ } else {
+ # Ensure emptiness
+ $siblings->Limit( FIELD => 'id', VALUE => 0 );
+ }
+ return $siblings;
+}
+
=head2 Content
Returns the attachment's content. if it's base64 encoded, decode it
commit 8f3d0e63233e73a8cfc42fc676a2685efab16895
Author: Thomas Sibley <trs at bestpractical.com>
Date: Wed Apr 10 11:59:15 2013 -0700
Method to find the nearest ancestor of an RT::Attachment with a certain MIME type
The nearest matching ancestor, or closest containing part, is
particularly useful when finding the enclosing multipart parent for
further processing.
diff --git a/lib/RT/Attachment.pm b/lib/RT/Attachment.pm
index a7e2985..c7a9aa0 100644
--- a/lib/RT/Attachment.pm
+++ b/lib/RT/Attachment.pm
@@ -262,6 +262,34 @@ sub ParentObj {
return $parent;
}
+=head2 Closest
+
+Takes a MIME type as a string or regex. Returns an L<RT::Attachment> object
+for the nearest containing part with a matching L</ContentType>. Strings must
+match exactly and all matches are done case insensitively. Strings ending in a
+C</> must only match the first part of the MIME type. For example:
+
+ # Find the nearest multipart/* container
+ my $container = $attachment->Closest("multipart/");
+
+Returns undef if no such object is found.
+
+=cut
+
+sub Closest {
+ my $self = shift;
+ my $type = shift;
+ my $part = $self->ParentObj;
+
+ $type = qr/^\Q$type\E$/
+ unless ref $type eq "REGEX";
+
+ $part = $part->ParentObj or last
+ while lc($part->ContentType) !~ $type;
+
+ return ($part and $part->id) ? $part : undef;
+}
+
=head2 Children
Returns an L<RT::Attachments> object which is preloaded with
commit 4b2901467583e1492c73953b0170954a235a4598
Author: Thomas Sibley <trs at bestpractical.com>
Date: Mon Nov 7 10:44:31 2011 -0500
Display embedded images in HTML messages inline with the text
Attached images with a Content-ID header referred to by an <img
src="cid:..."> tag in the HTML are now properly displayed as long as the
existing $ShowTransactionImages option is enabled. If the option is
disabled, images are not inlined with the HTML.
This makes RT's message display even more like a real mail client's.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 7056d24..cd09202 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1568,6 +1568,89 @@ sub PotentialPageAction {
return "";
}
+=head2 RewriteInlineImages PARAMHASH
+
+Turns C<< <img src="cid:..."> >> elements in HTML into working images pointing
+back to RT's stored copy.
+
+Takes the following parameters:
+
+=over 4
+
+=item Content
+
+Scalar ref of the HTML content to rewrite. Modified in place to support the
+most common use-case.
+
+=item Attachment
+
+The L<RT::Attachment> object from which the Content originates.
+
+=item Related (optional)
+
+Array ref of related L<RT::Attachment> objects to use for C<Content-ID> matching.
+
+Defaults to the result of the C<Siblings> method on the passed Attachment.
+
+=item AttachmentPath (optional)
+
+The base path to use when rewriting C<src> attributes.
+
+Defaults to C< $WebPath/Ticket/Attachment >
+
+=back
+
+In scalar context, returns the number of elements rewritten.
+
+In list content, returns the attachments IDs referred to by the rewritten <img>
+elements, in the order found. There may be duplicates.
+
+=cut
+
+sub RewriteInlineImages {
+ my %args = (
+ Content => undef,
+ Attachment => undef,
+ Related => undef,
+ AttachmentPath => RT->Config->Get('WebPath')."/Ticket/Attachment",
+ @_
+ );
+
+ return unless defined $args{Content}
+ and ref $args{Content} eq 'SCALAR'
+ and defined $args{Attachment};
+
+ my $related_part = $args{Attachment}->Closest("multipart/related")
+ or return;
+
+ $args{Related} ||= $related_part->Children->ItemsArrayRef;
+ return unless @{$args{Related}};
+
+ my $content = $args{'Content'};
+ my @rewritten;
+
+ require HTML::RewriteAttributes::Resources;
+ $$content = HTML::RewriteAttributes::Resources->rewrite($$content, sub {
+ my $cid = shift;
+ my %meta = @_;
+ return $cid unless lc $meta{tag} eq 'img'
+ and lc $meta{attr} eq 'src'
+ and $cid =~ s/^cid://i;
+
+ for my $attach (@{$args{Related}}) {
+ if (($attach->GetHeader('Content-ID') || '') eq "<$cid>") {
+ push @rewritten, $attach->Id;
+ return "$args{AttachmentPath}/" . $attach->TransactionId . '/' . $attach->Id;
+ }
+ }
+
+ # No attachments means this is a bogus CID. Just pass it through.
+ RT->Logger->debug(qq[Found bogus inline image src="cid:$cid"]);
+ return "cid:$cid";
+ });
+ return @rewritten;
+}
+
package HTML::Mason::Commands;
use vars qw/$r $m %session/;
@@ -3552,6 +3635,15 @@ our %SCRUBBER_ALLOWED_ATTRIBUTES = (
our %SCRUBBER_RULES = ();
+# If we're displaying images, let embedded ones through
+if (RT->Config->Get('ShowTransactionImages')) {
+ $SCRUBBER_RULES{'img'} = {
+ '*' => 0,
+ alt => 1,
+ src => qr/^cid:/i,
+ };
+}
+
sub _NewScrubber {
require HTML::Scrubber;
my $scrubber = HTML::Scrubber->new();
diff --git a/share/html/Elements/ShowTransactionAttachments b/share/html/Elements/ShowTransactionAttachments
index b66929c..047eadf 100644
--- a/share/html/Elements/ShowTransactionAttachments
+++ b/share/html/Elements/ShowTransactionAttachments
@@ -86,7 +86,9 @@ $m->comp(
$m->current_comp,
%ARGS,
Parent => $message->id,
- ParentObj => $message
+ ParentObj => $message,
+
+ displayed_inline => $displayed_inline,
);
</%PERL>
@@ -103,6 +105,9 @@ $AttachmentContent => {}
$Parent => 0
$ParentObj => undef
$WarnUnsigned => 0
+
+# Keep track of CID images we display inline
+$displayed_inline => {}
</%ARGS>
<%INIT>
my @DisplayHeaders=qw(_all);
@@ -194,6 +199,19 @@ my $render_attachment = sub {
object => $Object,
);
+ if (RT->Config->Get('ShowTransactionImages')) {
+ my @rewritten = RT::Interface::Web::RewriteInlineImages(
+ Content => \$content,
+ Attachment => $message,
+ # Not technically correct to search all parts of the
+ # MIME structure, but it saves having to go to the
+ # database again and is unlikely to break display.
+ Related => [ map { @$_ } values %$Attachments ],
+ AttachmentPath => $AttachmentPath,
+ );
+ $displayed_inline->{$_}++ for @rewritten;
+ }
+
require HTML::Quoted;
$content = HTML::Quoted->extract($content) unless length $name;
@@ -229,7 +247,11 @@ my $render_attachment = sub {
# if it's an image, show it as an image
elsif ( RT->Config->Get('ShowTransactionImages') and $content_type =~ m{^image/} ) {
- if ( $disposition ne 'inline' ) {
+ if ( $displayed_inline->{$message->Id} ) {
+ $m->out('<p><i>'. loc( 'Image displayed inline above' ) .'</i></p>');
+ return;
+ }
+ elsif ( $disposition ne 'inline' ) {
$m->out('<p>'. loc( 'Message body is not shown because sender requested not to inline it.' ) .'</p>');
return;
}
commit 0a09bbc189756d3ede6f06c8d71badf92dc44a96
Author: Thomas Sibley <trs at bestpractical.com>
Date: Fri Jun 29 15:11:49 2012 -0700
Move the HTML::RewriteAttributes dependency to CORE from DASHBOARDS
It's unlikely anyone configures RT with --without-dashboards, but just
in case.
diff --git a/sbin/rt-test-dependencies.in b/sbin/rt-test-dependencies.in
index a2319dc..263017c 100644
--- a/sbin/rt-test-dependencies.in
+++ b/sbin/rt-test-dependencies.in
@@ -204,6 +204,7 @@ HTML::FormatText
HTML::Mason 1.43
HTML::Mason::PSGIHandler 0.52
HTML::Quoted
+HTML::RewriteAttributes 0.05
HTML::Scrubber 0.08
HTML::TreeBuilder
HTTP::Message 6.0
@@ -325,7 +326,6 @@ Data::ICal
.
$deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
-HTML::RewriteAttributes 0.05
MIME::Types
URI 1.59
.
commit 6b5c5483d23ef47360fc9a366e5bab1bd3293657
Author: Thomas Sibley <trs at bestpractical.com>
Date: Fri Jun 29 17:21:47 2012 -0700
Explain why attached images aren't shown if ShowTransactionImages is off
This is similar to our behaviour with SuppressInlineTextFiles and more
obvious what's happening.
diff --git a/share/html/Elements/ShowTransactionAttachments b/share/html/Elements/ShowTransactionAttachments
index 047eadf..c770dfc 100644
--- a/share/html/Elements/ShowTransactionAttachments
+++ b/share/html/Elements/ShowTransactionAttachments
@@ -246,8 +246,12 @@ my $render_attachment = sub {
}
# if it's an image, show it as an image
- elsif ( RT->Config->Get('ShowTransactionImages') and $content_type =~ m{^image/} ) {
- if ( $displayed_inline->{$message->Id} ) {
+ elsif ( $content_type =~ m{^image/} ) {
+ if (not RT->Config->Get('ShowTransactionImages')) {
+ $m->out('<p><i>'. loc( 'Image not shown because display is disabled in system configuration.' ) .'</i></p>');
+ return;
+ }
+ elsif ( $displayed_inline->{$message->Id} ) {
$m->out('<p><i>'. loc( 'Image displayed inline above' ) .'</i></p>');
return;
}
commit a8285be9b08d4f7c6c0ffa7dccb5e9a58a6b5786
Author: Thomas Sibley <trs at bestpractical.com>
Date: Fri Jun 29 17:23:26 2012 -0700
It's not a message body, it's an image
diff --git a/share/html/Elements/ShowTransactionAttachments b/share/html/Elements/ShowTransactionAttachments
index c770dfc..6c98ba9 100644
--- a/share/html/Elements/ShowTransactionAttachments
+++ b/share/html/Elements/ShowTransactionAttachments
@@ -256,7 +256,7 @@ my $render_attachment = sub {
return;
}
elsif ( $disposition ne 'inline' ) {
- $m->out('<p>'. loc( 'Message body is not shown because sender requested not to inline it.' ) .'</p>');
+ $m->out('<p>'. loc( 'Image not shown because sender requested not to inline it.' ) .'</p>');
return;
}
commit cd9501cd37b02e5cab8a04147f30ca2881580baa
Author: Thomas Sibley <trs at bestpractical.com>
Date: Sat Jun 30 22:57:15 2012 -0700
Tidy up indenting and whitespace
Whitespace only changes, no functional difference.
diff --git a/share/html/Ticket/Attachment/dhandler b/share/html/Ticket/Attachment/dhandler
index 4cd2493..e90e513 100644
--- a/share/html/Ticket/Attachment/dhandler
+++ b/share/html/Ticket/Attachment/dhandler
@@ -46,50 +46,48 @@
%#
%# END BPS TAGGED BLOCK }}}
<%perl>
- my ($ticket, $trans,$attach, $filename);
- my $arg = $m->dhandler_arg; # get rest of path
- if ($arg =~ m{^(\d+)/(\d+)}) {
- $trans = $1;
- $attach = $2;
- }
- else {
- Abort("Corrupted attachment URL.");
- }
- my $AttachmentObj = RT::Attachment->new($session{'CurrentUser'});
- $AttachmentObj->Load($attach) || Abort("Attachment '$attach' could not be loaded");
+my ( $ticket, $trans, $attach, $filename );
+my $arg = $m->dhandler_arg; # get rest of path
+if ( $arg =~ m{^(\d+)/(\d+)} ) {
+ $trans = $1;
+ $attach = $2;
+}
+else {
+ Abort("Corrupted attachment URL.");
+}
+my $AttachmentObj = RT::Attachment->new( $session{'CurrentUser'} );
+$AttachmentObj->Load($attach) || Abort("Attachment '$attach' could not be loaded");
+unless ( $AttachmentObj->id ) {
+ Abort("Bad attachment id. Couldn't find attachment '$attach'\n");
+}
+unless ( $AttachmentObj->TransactionId() == $trans ) {
+ Abort("Bad transaction number for attachment. $trans should be". $AttachmentObj->TransactionId() . "\n");
+}
- unless ($AttachmentObj->id) {
- Abort("Bad attachment id. Couldn't find attachment '$attach'\n");
- }
- unless ($AttachmentObj->TransactionId() == $trans ) {
- Abort("Bad transaction number for attachment. $trans should be".$AttachmentObj->TransactionId() ."\n");
+my $content_type = $AttachmentObj->ContentType || 'text/plain';
- }
+if ( RT->Config->Get('AlwaysDownloadAttachments') ) {
+ $r->headers_out->{'Content-Disposition'} = "attachment; filename=" . $AttachmentObj->Filename;
+}
+elsif ( !RT->Config->Get('TrustHTMLAttachments') ) {
+ $content_type = 'text/plain' if ( $content_type =~ /^text\/html/i );
+}
- my $content_type = $AttachmentObj->ContentType || 'text/plain';
+my $enc = $AttachmentObj->OriginalEncoding || 'utf-8';
+my $iana = Encode::find_encoding($enc);
+ $iana = $iana ? $iana->mime_name : $enc;
- if (RT->Config->Get('AlwaysDownloadAttachments')) {
- $r->headers_out->{'Content-Disposition'} = "attachment; filename=" . $AttachmentObj->Filename;
- }
- elsif (!RT->Config->Get('TrustHTMLAttachments')) {
- $content_type = 'text/plain' if ($content_type =~ /^text\/html/i);
- }
+require MIME::Types;
+my $mimetype = MIME::Types->new->type($content_type);
+unless ( $mimetype && $mimetype->isBinary ) {
+ $content_type .= ";charset=$iana";
+}
- my $enc = $AttachmentObj->OriginalEncoding || 'utf-8';
- my $iana = Encode::find_encoding( $enc );
- $iana = $iana? $iana->mime_name : $enc;
-
- require MIME::Types;
- my $mimetype = MIME::Types->new->type($content_type);
- unless ( $mimetype && $mimetype->isBinary ) {
- $content_type .= ";charset=$iana";
- }
-
- $r->content_type( $content_type );
- $m->clear_buffer();
- $m->out($AttachmentObj->OriginalContent);
- $m->abort;
+$r->content_type($content_type);
+$m->clear_buffer();
+$m->out( $AttachmentObj->OriginalContent );
+$m->abort;
</%perl>
<%attr>
AutoFlush => 0
commit e305d24dfa665f28a1d39fc7cbe6f7521464f7cc
Author: Thomas Sibley <trs at bestpractical.com>
Date: Sat Jun 30 23:06:41 2012 -0700
Rewrite inline images when serving a single HTML attachment for display
This only happens in the non-default configuration of
AlwaysDownloadAttachments off (default) and TrustHTMLAttachments on
(non-default). The latter option is primarily used by people so HTML is
rendered in full without the restrictions imposed within ticket history.
It's reasonable to surmise that a site already trusting HTML attachments
for better display will prefer working embedded images.
diff --git a/share/html/Ticket/Attachment/dhandler b/share/html/Ticket/Attachment/dhandler
index e90e513..5907427 100644
--- a/share/html/Ticket/Attachment/dhandler
+++ b/share/html/Ticket/Attachment/dhandler
@@ -65,6 +65,7 @@ unless ( $AttachmentObj->TransactionId() == $trans ) {
Abort("Bad transaction number for attachment. $trans should be". $AttachmentObj->TransactionId() . "\n");
}
+my $content = $AttachmentObj->OriginalContent;
my $content_type = $AttachmentObj->ContentType || 'text/plain';
if ( RT->Config->Get('AlwaysDownloadAttachments') ) {
@@ -73,6 +74,15 @@ if ( RT->Config->Get('AlwaysDownloadAttachments') ) {
elsif ( !RT->Config->Get('TrustHTMLAttachments') ) {
$content_type = 'text/plain' if ( $content_type =~ /^text\/html/i );
}
+elsif (lc $content_type eq 'text/html') {
+ # If we're trusting and serving HTML for display not download, try to do
+ # inline <img> rewriting to be extra helpful.
+ my $count = RT::Interface::Web::RewriteInlineImages(
+ Content => \$content,
+ Attachment => $AttachmentObj,
+ );
+ RT->Logger->debug("Rewrote $count CID images when displaying original HTML attachment #$attach");
+}
my $enc = $AttachmentObj->OriginalEncoding || 'utf-8';
my $iana = Encode::find_encoding($enc);
@@ -86,7 +96,7 @@ unless ( $mimetype && $mimetype->isBinary ) {
$r->content_type($content_type);
$m->clear_buffer();
-$m->out( $AttachmentObj->OriginalContent );
+$m->out($content);
$m->abort;
</%perl>
<%attr>
commit bba8478f4fde765a7b343b8e395f50f210360ed9
Author: Thomas Sibley <trs at bestpractical.com>
Date: Fri Jun 29 16:14:53 2012 -0700
Preserve the Content-ID of redistributed attachments
This opens the door for supporting inlined images in HTML notifications.
diff --git a/lib/RT/Action/SendEmail.pm b/lib/RT/Action/SendEmail.pm
index abb0dac..65f99fd 100644
--- a/lib/RT/Action/SendEmail.pm
+++ b/lib/RT/Action/SendEmail.pm
@@ -406,6 +406,7 @@ sub AddAttachment {
Data => $attach->OriginalContent,
Disposition => $disp, # a false value defaults to inline in MIME::Entity
Filename => $self->MIMEEncodeString( $attach->Filename ),
+ Id => $attach->GetHeader('Content-ID'),
'RT-Attachment:' => $self->TicketObj->Id . "/"
. $self->TransactionObj->Id . "/"
. $attach->id,
-----------------------------------------------------------------------
More information about the Rt-commit
mailing list