[Rt-commit] rt branch, 4.2/generic-history-rendering, created. rt-4.0.8-527-gd78cf7a

Thomas Sibley trs at bestpractical.com
Thu Nov 29 20:12:44 EST 2012


The branch, 4.2/generic-history-rendering has been created
        at  d78cf7af01cd2076cab9fbd29859d9a525ce892c (commit)

- Log -----------------------------------------------------------------
commit 2c8912e93cee9c67d9b552d1f0d8e4a79a135efa
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jun 29 03:35:14 2010 +0400

    indent and minor refactoring

diff --git a/share/html/Ticket/Elements/ShowHistory b/share/html/Ticket/Elements/ShowHistory
index 909ea01..a0bb71b 100644
--- a/share/html/Ticket/Elements/ShowHistory
+++ b/share/html/Ticket/Elements/ShowHistory
@@ -55,11 +55,8 @@ if ($ShowDisplayModes or $ShowTitle) {
                     ? loc('History')
                     : ' ';
 
-    my $titleright;
-
+    my $titleright = '';
     if ($ShowDisplayModes) {
-        $titleright = '';
-        
         my $open_all  = $m->interp->apply_escapes( loc("Show all quoted text"), 'j' );
         my $open_html = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
         my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'j' );
@@ -96,6 +93,7 @@ for my $attachment (@{$Attachments->ItemsArrayRef()}) {
     push (@{$trans_attachments->{$attachment->TransactionId}}, $attachment)
 }
 
+my $i = 1;
 while ( my $Transaction = $Transactions->Next ) {
     my $skip = 0;
     $m->callback(
@@ -106,31 +104,30 @@ while ( my $Transaction = $Transactions->Next ) {
     );
     next if $skip;
 
-    $i++;
-
     my $IsLastTransaction = 0;
     if ( RT->Config->Get( 'OldestTransactionsFirst', $session{'CurrentUser'} )){
         $IsLastTransaction = $Transactions->IsLast;
     } else {
-        $IsLastTransaction = 1 if ( $i == 1 );
+        $IsLastTransaction = 1 if $i == 1;
     }
 
     #Args is first because we're clobbering the "Attachments" parameter 
     $m->comp( 'ShowTransaction',
-            %ARGS,
+        %ARGS,
+        Ticket               => $Ticket,
+        Transaction          => $Transaction,
+        ShowHeaders          => $ShowHeaders,
+        RowNum               => $i,
+        Attachments          => $trans_attachments->{$Transaction->id},
+        AttachmentContent    => $trans_content,
+        LastTransaction      => $IsLastTransaction
+    );
 
-              Ticket               => $Ticket,
-              Transaction          => $Transaction,
-              ShowHeaders          => $ShowHeaders,
-              RowNum               => $i,
-              Attachments          => $trans_attachments->{$Transaction->id},
-              AttachmentContent    => $trans_content,
-              LastTransaction      => $IsLastTransaction
- );
+    # manually flush the content buffer after each txn,
+    # so the user sees some update
+    $m->flush_buffer;
 
-# manually flush the content buffer after each txn, so the user sees
-# some update
-$m->flush_buffer(); 
+    $i++;
 }
 
 </%perl>
@@ -140,12 +137,9 @@ $m->flush_buffer();
 </div>
 % }
 <%INIT>
-
-my $i;
 $Transactions ||= $m->comp('/Ticket/Elements/FindTransactions',Ticket => $Ticket, Tickets => $Tickets || undef);
 $Attachments ||=  $m->comp('/Ticket/Elements/FindAttachments', Ticket => $Ticket, Tickets => $Tickets || undef);
 $AttachmentContent ||= $m->comp('/Ticket/Elements/LoadTextAttachments', Ticket => $Ticket);
-
 </%INIT>
 <%ARGS>
 $URIFile => RT->Config->Get('WebPath')."/Ticket/Display.html"

commit fffa0856095b37dfbac3c1b25c391e65614ffa45
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jun 30 03:43:32 2010 +0400

    minor refactoring

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 2c021ec..585f84d 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1528,7 +1528,7 @@ sub _NewTransaction {
 
 =head2 Transactions
 
-  Returns an RT::Transactions object of all transactions on this record object
+Returns an L<RT::Transactions> object of all transactions on this record object
 
 =cut
 
@@ -1537,8 +1537,6 @@ sub Transactions {
 
     use RT::Transactions;
     my $transactions = RT::Transactions->new( $self->CurrentUser );
-
-    #If the user has no rights, return an empty object
     $transactions->Limit(
         FIELD => 'ObjectId',
         VALUE => $self->id,
@@ -1548,11 +1546,9 @@ sub Transactions {
         VALUE => ref($self),
     );
 
-    return ($transactions);
+    return $transactions;
 }
 
-#
-
 sub CustomFields {
     my $self = shift;
     my $cfs  = RT::CustomFields->new( $self->CurrentUser );

commit 0a5403e2af06b286ae3290133ed3ce64994560a0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jun 30 03:44:24 2010 +0400

    Attachments and TextAttachments for all records in the system

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 585f84d..a8ef704 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1549,6 +1549,73 @@ sub Transactions {
     return $transactions;
 }
 
+=head2 Attachments
+
+Returns an L<RT::Attachments> object of all attachments on this record object
+(for all its L</Transactions>).
+
+By default Content and Headers of attachments are not fetched right away from
+database. Use C<WithContent> and C<WithHeaders> options to override this.
+
+=cut
+
+sub Attachments {
+    my $self = shift;
+    my %args = (
+        WithHeaders => 0,
+        WithContent => 0,
+        @_
+    );
+    my @columns = qw(
+        id TransactionId Parent MessageId
+        Subject Filename
+        ContentType ContentEncoding
+        Creator Created
+    );
+    push @columns, 'Headers' if $args{'WithHeaders'};
+    push @columns, 'Content' if $args{'WithContent'};
+
+    my $res = RT::Attachments->new( $self->CurrentUser );
+    $res->Columns( @columns );
+    my $txn_alias = $res->TransactionAlias;
+    $res->Limit(
+        ALIAS => $txn_alias,
+        FIELD => 'ObjectType',
+        VALUE => ref($self),
+    );
+    $res->Limit(
+        ALIAS => $txn_alias,
+        FIELD => 'ObjectId',
+        VALUE => $self->id,
+    );
+    return $res;
+}
+
+=head2 TextAttachments
+
+Returns an L<RT::Attachments> object of all attachments, like L<Attachments>,
+but only those that are text.
+
+By default Content and Headers are fetched. Use C<WithContent> and
+C<WithHeaders> options to override this.
+
+=cut
+
+sub TextAttachments {
+    my $self = shift;
+    my $res = $self->Attachments(
+        WithHeaders => 1,
+        WithContent => 1,
+        @_
+    );
+    $res->Limit( FIELD => 'ContentType', OPERATOR => '=', VALUE => 'text/plain');
+    $res->Limit( FIELD => 'ContentType', OPERATOR => 'STARTSWITH', VALUE => 'message/');
+    $res->Limit( FIELD => 'ContentType', OPERATOR => '=', VALUE => 'text');
+    $res->Limit( FIELD => 'Filename', OPERATOR => 'IS', VALUE => 'NULL')
+        if RT->Config->Get( 'SuppressInlineTextFiles', $self->CurrentUser );
+    return $res;
+}
+
 sub CustomFields {
     my $self = shift;
     my $cfs  = RT::CustomFields->new( $self->CurrentUser );

commit bef38129137b169195301072a86cbedbdc5defff
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Nov 15 17:30:59 2012 -0800

    Attachments and TextAttachments customizations for tickets

diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 47207d5..9e4cc43 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -3560,6 +3560,84 @@ sub _Value {
 
 }
 
+=head2 Attachments
+
+Customization of L<RT::Record/Attachments> for tickets.
+
+=cut
+
+sub Attachments {
+    my $self = shift;
+    my %args = (
+        WithHeaders => 0,
+        WithContent => 0,
+        @_
+    );
+    my $res = RT::Attachments->new( $self->CurrentUser );
+    unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+        $res->Limit(
+            SUBCLAUSE => 'acl',
+            FIELD    => 'id',
+            VALUE    => 0,
+            ENTRYAGGREGATOR => 'AND'
+        );
+        return $res;
+    }
+
+    my @columns = qw(
+        id TransactionId Parent MessageId
+        Subject Filename
+        ContentType ContentEncoding
+        Creator Created
+    );
+    push @columns, 'Headers' if $args{'WithHeaders'};
+    push @columns, 'Content' if $args{'WithContent'};
+
+    $res->Columns( @columns );
+    my $txn_alias = $res->TransactionAlias;
+    $res->Limit(
+        ALIAS => $txn_alias,
+        FIELD => 'ObjectType',
+        VALUE => ref($self),
+    );
+    my $ticket_alias = $res->Join(
+        ALIAS1 => $txn_alias,
+        FIELD1 => 'ObjectId',
+        TABLE2 => 'Tickets',
+        FIELD2 => 'id',
+    );
+    $res->Limit(
+        ALIAS => $ticket_alias,
+        FIELD => 'EffectiveId',
+        VALUE => $self->id,
+    );
+    return $res;
+}
+
+=head2 TextAttachments
+
+Customization of L<RT::Record/TextAttachments> for tickets.
+
+=cut
+
+sub TextAttachments {
+    my $self = shift;
+
+    my $res = $self->SUPER::TextAttachments( @_ );
+    unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+        # if the user may not see comments do not return them
+        $res->Limit(
+            SUBCLAUSE => 'ACL',
+            ALIAS     => $res->TransactionAlias,
+            FIELD     => 'Type',
+            OPERATOR  => '!=',
+            VALUE     => 'Comment',
+        );
+    }
+
+    return $res;
+}
+
 
 
 =head2 _UpdateTimeTaken

commit 2314c3d781052ef70cef5fc7483919a8583e81e9
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jun 30 03:51:58 2010 +0400

    replace mason comp with new methods

diff --git a/share/html/Ticket/Elements/LoadTextAttachments b/share/html/Ticket/Elements/LoadTextAttachments
index b1ff532..a444306 100644
--- a/share/html/Ticket/Elements/LoadTextAttachments
+++ b/share/html/Ticket/Elements/LoadTextAttachments
@@ -47,47 +47,10 @@
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
 
-my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
+# DON'T USE IT. it's here for backwards
 
-$attachments->Columns( qw(id Content ContentType TransactionId ContentEncoding));
-
-if ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
-    my $transactions = $attachments->NewAlias('Transactions');
-    $attachments->Join( ALIAS1 => 'main',
-                                           FIELD1 => 'TransactionId',
-                                           ALIAS2 => $transactions,
-                                           FIELD2 => 'id' );
-    
-    my $tickets = $attachments->NewAlias('Tickets');
-
-
-    $attachments->Join( ALIAS1 => $transactions,
-                        FIELD1 => 'ObjectId',
-                        ALIAS2 => $tickets,
-                        FIELD2 => 'id' );
-
-    $attachments->Limit( ALIAS => $transactions,
-                         FIELD => 'ObjectType',
-                        VALUE => 'RT::Ticket');
-
-
-    $attachments->Limit( ALIAS => $tickets,
-                         FIELD => 'EffectiveId',
-                         VALUE => $Ticket->id() );
-    # if the user may not see comments do not return them
-    unless ( $Ticket->CurrentUserHasRight('ShowTicketComments') ) {
-        $attachments->Limit( ALIAS    => $transactions, FIELD    => 'Type', OPERATOR => '!=', VALUE    => "Comment" );
-    }
-
-    $attachments->Limit ( FIELD => 'ContentType', OPERATOR => '=', VALUE => 'text/plain');
-    $attachments->Limit ( FIELD => 'ContentType', OPERATOR => 'STARTSWITH', VALUE => 'message/');
-    $attachments->Limit ( FIELD => 'ContentType', OPERATOR => '=', VALUE => 'text');
-    $attachments->Limit ( FIELD => 'Filename', OPERATOR => 'IS', VALUE => 'NULL')
-        if RT->Config->Get('SuppressInlineTextFiles', $Ticket->CurrentUser );
-}
-return ($attachments);
+return $Ticket->TextAttachments;
 </%INIT>
 <%ARGS>
 $Ticket => undef
 </%ARGS>
-

commit ce92315730554fc1cf70f6fbf6c6a3d17f6a55f8
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jun 30 03:59:30 2010 +0400

    use new method, other code there we have is messy

diff --git a/share/html/Ticket/Elements/FindAttachments b/share/html/Ticket/Elements/FindAttachments
index cb99751..f73386e 100644
--- a/share/html/Ticket/Elements/FindAttachments
+++ b/share/html/Ticket/Elements/FindAttachments
@@ -46,50 +46,47 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
-my %documents;
-
-#A default implementation here loops through all transactions and pulls out all their attachments.
+# A default implementation here loops through all transactions and pulls out all their attachments.
 # We end up doing an end-run around that to get a bit more performance
 
-# We force the cache of ticket transactions to get populated up front. otherwise, the 
+# We force the cache of ticket transactions to get populated up front. otherwise, the
 # code that looks at attachments will look at each one in turn.
+
+return $Ticket->Attachments unless $Tickets;
+
 my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
+$attachments->Columns( qw(
+    id TransactionId Parent MessageId
+    Subject Filename
+    ContentType ContentEncoding
+    Creator Created
+) );
 
-$attachments->Columns( qw( Id Filename Headers Subject Parent ContentEncoding ContentType TransactionId Created));
+my $transactions = $attachments->TransactionsAlias;
+my $tickets = $attachments->Join(
+    ALIAS1 => $transactions,
+    FIELD1 => 'ObjectId',
+    TABLE2 => 'Tickets',
+    FIELD2 => 'id',
+);
 
-my $transactions = $attachments->NewAlias('Transactions');
-$attachments->Join( ALIAS1 => 'main',
-		    FIELD1 => 'TransactionId',
-		    ALIAS2 => $transactions,
-		    FIELD2 => 'id' );
-    
-my $tickets = $attachments->NewAlias('Tickets');
+$attachments->Limit(
+    ALIAS => $transactions,
+    FIELD => 'ObjectType',
+    VALUE => 'RT::Ticket',
+);
 
-  $attachments->Join( ALIAS1 => $transactions,                         
-                        FIELD1 => 'ObjectId',
-                        ALIAS2 => $tickets,
-                        FIELD2 => 'id' );
-    
-    $attachments->Limit( ALIAS => $transactions,
-                         FIELD => 'ObjectType',
-                        VALUE => 'RT::Ticket');
-if ($Tickets) {
-    while ($Ticket = $Tickets->Next) {
-	$attachments->Limit( ALIAS => $tickets,
-			     FIELD => 'EffectiveId',
-			     VALUE => $Ticket->id() );
-    }
-} else {
-    $attachments->Limit( ALIAS => $tickets,
-			 FIELD => 'EffectiveId',
-			 VALUE => $Ticket->id() );
+while ( my $Ticket = $Tickets->Next) {
+    $attachments->Limit(
+        ALIAS => $tickets,
+        FIELD => 'EffectiveId',
+        VALUE => $Ticket->id,
+    );
 }
 
-
 return ($attachments);
 </%INIT>
 <%ARGS>
 $Ticket => undef
 $Tickets => undef
 </%ARGS>
-

commit 8ba0706c3d4150c1aa7288a33dc074ee00c5a02d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jun 30 07:23:21 2010 +0400

    first pass on generic ShowHistory, still on ticket's ShowTransaction

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
new file mode 100644
index 0000000..73f7b16
--- /dev/null
+++ b/share/html/Elements/ShowHistory
@@ -0,0 +1,151 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%# 
+%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+%#                                          <jesse at bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<%perl>
+if ( $ShowDisplayModes or $ShowTitle ) {
+    my $titleright = '';
+    if ( $ShowDisplayModes ) {
+        $Page ||= $m->request_path;
+        if ( $ShowHeaders ) {
+            $titleright .=
+                '<a href="'. RT->Config->Get('WebPath') . $Page
+                . '?id='. $Object->id. '">'
+                . loc("Brief headers") .'</a>'
+                . ' — '
+                . '<span class="selected">'. loc("Full headers") .'</span>';
+        }
+        else {
+            $titleright .= '<span class="selected">'. loc("Brief headers") .'</span>'
+                .' — '
+                .'<a href="'. RT->Config->Get('WebPath') . $Page
+                . '?ShowHeaders=1&id='. $Object->id. '">'
+                . loc("Full headers") .'</a>';
+        }
+    }
+</%perl>
+<div class="history">
+<& /Widgets/TitleBoxStart,
+    title => $ShowTitle ? loc('History') : ' ',
+    titleright_raw => $titleright,
+&>
+% }
+
+<div id="ticket-history">
+<%perl>
+my $i = 1;
+while ( my $Transaction = $Transactions->Next ) {
+    my $skip = 0;
+    $m->callback(
+        %ARGS,
+        Transaction   => $Transaction,
+        skip          => \$skip,
+        CallbackName  => 'SkipTransaction',
+    );
+    next if $skip;
+
+    my $IsLastTransaction = 0;
+    if ( $OldestFirst ) {
+        $IsLastTransaction = $Transactions->IsLast;
+    } else {
+        $IsLastTransaction = 1 if $i == 1;
+    }
+
+    #Args is first because we're clobbering the "Attachments" parameter 
+    $m->comp( '/Ticket/Elements/ShowTransaction',
+        %ARGS,
+
+        Ticket            => $Object,
+        Transaction       => $Transaction,
+        ShowHeaders       => $ShowHeaders,
+        RowNum            => $i,
+        Attachments       => $attachments{ $Transaction->id } || [],
+        AttachmentContent => \%attachment_content,
+        LastTransaction   => $IsLastTransaction
+    );
+
+    # manually flush the content buffer after each txn,
+    # so the user sees some update
+    $m->flush_buffer;
+
+    $i++;
+}
+
+</%perl>
+</div>
+% if ($ShowDisplayModes or $ShowTitle) {
+<& /Widgets/TitleBoxEnd &>
+</div>
+% }
+<%INIT>
+my $Transactions = $Object->Transactions;
+
+my $OldestFirst = RT->Config->Get(
+    OldestTransactionsFirst => $session{'CurrentUser'}
+);
+my $SortOrder = $OldestFirst? 'ASC': 'DESC';
+$Transactions->OrderByCols(
+    { FIELD => 'Created', ORDER => $SortOrder },
+    { FIELD => 'id', ORDER => $SortOrder },
+);
+
+my %attachments;
+push @{ $attachments{ $_->TransactionId } ||= [] }, $_
+    foreach @{ $Attachments->ItemsArrayRef };
+my %attachment_content = map { $_->id => $_ }
+    @{ $AttachmentContent->ItemsArrayRef };
+
+</%INIT>
+<%ARGS>
+$Object
+$Attachments       => $Object->Attachments( WithHeaders => 1 )
+$AttachmentContent => $Object->TextAttachments
+
+$Page              => undef 
+$ShowHeaders       => 0
+$ShowTitle         => 1
+$ShowDisplayModes  => 1
+</%ARGS>

commit 8a5119dcf82d19a71e955b11ca4cfd1d11231870
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Jul 3 04:43:38 2010 +0400

    introduce RecordType and deprecate ObjectTypeStr

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index a8ef704..dd5abf5 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -127,21 +127,35 @@ sub Delete {
     } 
 }
 
-=head2 ObjectTypeStr
+=head2 RecordType
+
+Returns a string which is this record's type. It's not localized and by
+default last part (everything after last ::) of class name is returned.
 
-Returns a string which is this object's type.  The type is the class,
-without the "RT::" prefix.
+=cut
 
+sub RecordType {
+    my $res = ref($_[0]) || $_[0];
+    $res =~ s/.*:://;
+    return $res;
+}
+
+=head2 ObjectTypeStr
+
+DEPRECATED. Stays here for backwards. Returns localized L</RecordType>.
 
 =cut
 
+# we deprecate because of:
+# * ObjectType is used in several classes with ObjectId to store
+#   records of different types, for example transactions use those
+#   and it's unclear what this method should return 'Transaction'
+#   or type of referenced record
+# * returning localized thing is not good idea
+
 sub ObjectTypeStr {
     my $self = shift;
-    if (ref($self) =~ /^.*::(\w+)$/) {
-	return $self->loc($1);
-    } else {
-	return $self->loc(ref($self));
-    }
+    return $self->loc( $self->RecordType( @_ ) );
 }
 
 =head2 Attributes

commit 07cfcd7c6fe82b7b240bcd7e42e2cfc91d95e967
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Jul 3 04:51:25 2010 +0400

    use Object->RecordType in Txn->FriendlyObjectType
    
    it gives more control to extensions that add new tables into RT

diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
index 1993824..c40606f 100644
--- a/lib/RT/Transaction.pm
+++ b/lib/RT/Transaction.pm
@@ -1124,9 +1124,7 @@ sub Object {
 
 sub FriendlyObjectType {
     my $self = shift;
-    my $type = $self->ObjectType or return undef;
-    $type =~ s/^RT:://;
-    return $self->loc($type);
+    return $self->loc( $self->Object->RecordType );
 }
 
 =head2 UpdateCustomFields

commit e15d19a61a56df5d5bb9a91eb5e75d6017bd3a3d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jul 5 03:11:53 2010 +0400

    Record's ClassifyTransaction method

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index dd5abf5..d910862 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1563,6 +1563,50 @@ sub Transactions {
     return $transactions;
 }
 
+our %TRANSACTION_CLASSIFICATION = (
+    Create     => 'message',
+    Correspond => 'message',
+    Comment    => 'message',
+
+    AddWatcher => 'people',
+    DelWatcher => 'people',
+
+    Take       => 'people',
+    Untake     => 'people',
+    Force      => 'people',
+    Steal      => 'people',
+    Give       => 'people',
+
+    AddLink    => 'links',
+    DeleteLink => 'links',
+
+    Status     => 'basics',
+    Set        => {
+        __default => 'basics',
+        map( { $_ => 'dates' } qw(
+            Told Starts Started Due LastUpdated Created LastUpdated
+        ) ),
+        map( { $_ => 'people' } qw(
+            Owner Creator LastUpdatedBy
+        ) ),
+    },
+    __default => 'other',
+);
+
+sub ClassifyTransaction {
+    my $self = shift;
+    my $txn = shift;
+
+    my $type = $txn->Type;
+
+    my $res = $TRANSACTION_CLASSIFICATION{ $type };
+    return $res || $TRANSACTION_CLASSIFICATION{ '__default' }
+        unless ref $res;
+
+    return $res->{ $txn->Field } || $res->{'__default'}
+        || $TRANSACTION_CLASSIFICATION{ '__default' }; 
+}
+
 =head2 Attachments
 
 Returns an L<RT::Attachments> object of all attachments on this record object
diff --git a/share/html/Ticket/Elements/ShowTransaction b/share/html/Ticket/Elements/ShowTransaction
index 2e0acd3..1becb3c 100644
--- a/share/html/Ticket/Elements/ShowTransaction
+++ b/share/html/Ticket/Elements/ShowTransaction
@@ -93,52 +93,13 @@ $ShowBody => 1
 $LastTransaction => 0
 $WarnUnsigned => undef
 </%ARGS>
-<%ONCE>
-
-my %class = (
-    Correspond => 'message',
-    Comment    => 'message',
-
-    AddWatcher => 'people',
-    DelWatcher => 'people',
-    Take       => 'people',
-    Untake     => 'people',
-    Force      => 'people',
-    Steal      => 'people',
-    Give       => 'people',
-
-    AddLink    => 'links',
-    DeleteLink => 'links',
-);
-
-</%ONCE>
 <%INIT>
 
 my $transdate = $Transaction->CreatedAsString();
 $transdate =~ s/\s/ /g;
 
 my ($type, $field) = ($Transaction->Type, $Transaction->Field || '');
-my $type_class = $class{ $type };
-if ( $type eq 'Create' && $Transaction->ObjectType eq 'RT::Ticket' ) {
-    $type_class = 'message';
-}
-
-unless ( $type_class ) {
-    if ( $field eq 'Owner' ) {
-        $type_class = 'people';
-    }
-    elsif ( $type =~ /^(Status|Set|Told)$/ ) {
-        if ( $field =~ /^(Told|Starts|Started|Due)$/ ) {
-            $type_class = 'dates';
-        }
-        else {
-            $type_class = 'basics';
-        }
-    }
-    else {
-        $type_class = 'other';
-    }
-}
+my $type_class  = $Ticket->ClassifyTransaction($Transaction);
 
 $m->callback(
     CallbackName => 'MassageTypeClass',

commit be123049cb54cad09eebe0c9450b86a9d5f858c2
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jul 5 03:13:05 2010 +0400

    generate default paths using $m->request_path

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 73f7b16..71f9819 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -138,6 +138,25 @@ push @{ $attachments{ $_->TransactionId } ||= [] }, $_
 my %attachment_content = map { $_->id => $_ }
     @{ $AttachmentContent->ItemsArrayRef };
 
+{
+    my %tmp = (
+        DisplayPath     => 'Display.html',
+        AttachmentPath  => 'Attachment',
+        UpdatePath      => 'Update.html',
+        ForwardPath     => 'Forward.html',
+        EmailRecordPath => 'ShowEmailRecord.html',
+        EncryptionPath  => 'GnuPG.html',
+    );
+
+    my $request_path = $m->request_path;
+    $request_path =~ s/[^\/]+$//;
+
+    while ( my ($arg, $path) = each %tmp ) {
+        next if defined $ARGS{ $arg };
+
+        $ARGS{ $arg } = $request_path . $path;
+    }
+}
 </%INIT>
 <%ARGS>
 $Object

commit c86f1dd3a30be6cb2c8a04d14b9fed393aee2af9
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jul 5 23:03:56 2010 +0400

    first implementation of ShowTransaction

diff --git a/share/html/Elements/ShowTransaction b/share/html/Elements/ShowTransaction
new file mode 100644
index 0000000..5038f6b
--- /dev/null
+++ b/share/html/Elements/ShowTransaction
@@ -0,0 +1,254 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%# 
+%# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+%#                                          <jesse at bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<div class="<% join ' ', @classes %>">
+<div class="<% $record_type %>-transaction">
+  <div class="metadata">
+    <span class="type">
+      <a name="txn-<% $Transaction->id %>" \
+% if ( $DisplayPath ) {
+      href="<% $DisplayPath %>#txn-<% $Transaction->id %>" \
+% }
+      >#</a>
+% if ( $LastTransaction ) {
+      <a id="lasttrans" name="lasttrans"></a>
+% }
+    </span>
+% $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterAnchor' );
+    <span class="date"><% $date |n %></span>
+    <span class="description">
+      <& /Elements/ShowUser, User => $Transaction->CreatorObj &> - <% $desc %>
+% $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterDescription' );
+    </span>
+    <span class="time-taken"><% $time %></span>
+% if ( $actions ) {
+    <span class="actions"><% $actions |n %></span>
+% }
+  </div>
+
+  <div class="content">
+<%PERL>
+if ( $Transaction->CustomFieldValues->Count ) {
+    $m->comp('/Elements/ShowCustomFields', Object => $Transaction );
+}
+$m->comp(
+    '/Ticket/Elements/ShowTransactionAttachments',
+    %ARGS,
+    Parent => 0
+) if $ShowBody;
+</%PERL>
+  </div>
+
+</div>
+</div>
+
+<%ARGS>
+$Transaction
+$Object => $Transaction->Object
+
+$Attachments => undef
+$AttachmentContent => undef
+
+$ShowBody => 1
+$ShowActions => 1
+$RowNum => 1
+$LastTransaction => 0
+
+$DisplayPath => undef
+$AttachmentPath => undef
+$UpdatePath => undef
+$ForwardPath => undef
+$EncryptionPath => undef
+$EmailRecordPath => undef
+</%ARGS>
+
+<%ONCE>
+
+</%ONCE>
+<%INIT>
+my $record_type = $Object->RecordType;
+
+my @classes = (
+    $record_type . '-transaction',
+    $Object->ClassifyTransaction( $Transaction ),
+    ($RowNum % 2 ? 'odd' : 'even')
+);
+
+my $desc = $Transaction->BriefDescription;
+if ( $Object->id != $Transaction->ObjectId ) {
+    # merged objects
+    $desc = loc("[_1] #[_1]:", loc($record_type), $Transaction->ObjectId)
+        .' - '. $desc;
+}
+
+my $date = $Transaction->CreatedAsString;
+
+my $time = '';
+$time = loc('[quant,_1,min,min]', $Transaction->TimeTaken)
+    if $Transaction->TimeTaken;
+
+if ( $ShowBody && !$Attachments ) {
+    my $attachments = $Transaction->Attachments;
+    $attachments->Columns( qw(
+        Id TransactionId Parent MessageId
+        Subject Filename Headers 
+        ContentEncoding ContentType
+        Creator Created
+    ) );
+    $ARGS{'Attachments'} = $Attachments = $attachments->ItemsArrayRef;
+}
+
+my @actions = ();
+if ( $ShowActions ) {
+    my $txn_type = $Transaction->Type;
+    if ( $txn_type =~ /EmailRecord$/ ) {
+        push @actions, {
+            title  => loc('Show'),
+            target => '_blank',
+            path   => $EmailRecordPath
+                .'?id='. $Object->id
+                .'&Transaction='. $Transaction->id
+                .'&Attachment='. ( $Attachments->[0] && $Attachments->[0]->id ),
+        } if $EmailRecordPath;
+
+        $ShowBody = 0;
+    }
+
+    # If the transaction has anything attached to it at all
+    elsif ( @$Attachments ) {
+        my %has_right = map {
+            $_ => RT::ACE->CanonicalizeRightName( $_ . $record_type )
+        } qw(Modify CommentOn ReplyTo);
+
+        my $can_modify = $has_right{'Modify'}
+            && $Object->CurrentUserHasRight( $has_right{'Modify'} );
+
+        if ( $UpdatePath && $has_right{'ReplyTo'}
+            && ( $can_modify
+                || $Object->CurrentUserHasRight( $has_right{'ReplyTo'} )
+            )
+        ) {
+            push @actions, {
+                title  => loc('Reply'),
+                path   => $UpdatePath
+                    .'?id='. $Object->id
+                    .'&QuoteTransaction='. $Transaction->id
+                    .'&Action=Respond'
+                ,
+            };
+        }
+        if ( $UpdatePath && $has_right{'CommentOn'}
+            && ( $can_modify
+                || $Object->CurrentUserHasRight( $has_right{'CommentOn'} )
+            )
+        ) {
+            push @actions, {
+                title  => loc('Comment'),
+                path   => $UpdatePath
+                    .'?id='. $Object->id
+                    .'&QuoteTransaction='. $Transaction->id
+                    .'&Action=Comment'
+                ,
+            };
+        }
+        if ( $ForwardPath && $Object->CurrentUserHasRight('ForwardMessage') ) {
+            push @actions, {
+                title  => loc('Forward'),
+                path   => $ForwardPath
+                    .'?id='. $Object->id
+                    .'&QuoteTransaction='. $Transaction->id
+                ,
+            };
+        }
+        if ( $EncryptionPath && $can_modify
+            && RT->Config->Get('GnuPG')->{'Enable'}
+            && RT->Config->Get('GnuPG')->{'AllowEncryptDataInDB'}
+        ) {
+            push @actions, {
+                title  => loc('Encrypt/Decrypt'),
+                path   => $EncryptionPath
+                    .'?id='. $Transaction->id
+                    .'&QuoteTransaction='. $Transaction->id
+                ,
+            };
+        }
+    }
+}
+
+$m->callback(
+    %ARGS,
+    Transaction => $Transaction,
+    Object      => $Object,
+
+    Classes     => \@classes,
+    Actions     => \@actions,
+    Created     => \$date,
+    TimeTaken   => \$time,
+    Description => \$desc,
+);
+
+my $actions = '';
+if ( @actions ) {
+    my $i = $m->interp;
+
+    foreach my $a ( @actions ) {
+        $a = '<a'
+            .' href="'. $i->apply_escapes( $a->{'path'}, 'h' ) .'"'
+            . ($a->{'target'}
+                ? ' target="'. $i->apply_escapes( $a->{'target'}, 'h' ) .'"'
+                : ''
+            )
+            .'>'. $i->apply_escapes( $a->{'title'}, 'h' ) .'</a>'
+        ;
+    }
+    $actions = join ' ', map "[$_]", @actions;
+}
+
+# make date unbreakable
+$date = $m->interp->apply_escapes( $date, 'h' );
+$date =~ s/\s/ /g;
+</%INIT>

commit 8c7587ce431df8aabcaa14e6b8b8e86baaee3723
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jul 5 23:04:24 2010 +0400

    use new ShowTransaction instead of ticket's

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 71f9819..e75f2fc 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -95,7 +95,7 @@ while ( my $Transaction = $Transactions->Next ) {
     }
 
     #Args is first because we're clobbering the "Attachments" parameter 
-    $m->comp( '/Ticket/Elements/ShowTransaction',
+    $m->comp( '/Elements/ShowTransaction',
         %ARGS,
 
         Ticket            => $Object,

commit 3b95e63c017a0d501a15ab2dd68b9ffc6a5d7de1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jul 6 17:14:30 2010 +0400

    FriendlyContentLength method for RT::Attachments

diff --git a/lib/RT/Attachment.pm b/lib/RT/Attachment.pm
index 785b204..3072822 100644
--- a/lib/RT/Attachment.pm
+++ b/lib/RT/Attachment.pm
@@ -394,6 +394,34 @@ sub ContentLength {
     return $len;
 }
 
+=head2 FriendlyContentLength
+
+Returns L</ContentLength> in bytes, kilobytes, or megabytes as most
+appropriate.  The size is suffixed with C<M>, C<k>, and C<b> and the returned
+string is localized.
+
+Returns the empty string if the L</ContentLength> is 0 or undefined.
+
+=cut
+
+sub FriendlyContentLength {
+    my $self = shift;
+    my $size = $self->ContentLength;
+    return '' unless $size;
+
+    my $res = '';
+    if ( $size > 1024*1024 ) {
+        $res = $self->loc( "[_1]M", int( $size / 1024 / 102.4 ) / 10 );
+    }
+    elsif ( $size > 1024 ) {
+        $res = $self->loc( "[_1]k", int( $size / 102.4 ) / 10 );
+    }
+    else {
+        $res = $self->loc( "[_1]b", $size );
+    }
+    return $res;
+}
+
 =head2 Quote
 
 =cut
diff --git a/share/html/Ticket/Elements/ShowTransactionAttachments b/share/html/Ticket/Elements/ShowTransactionAttachments
index 95a2341..1ba25c3 100644
--- a/share/html/Ticket/Elements/ShowTransactionAttachments
+++ b/share/html/Ticket/Elements/ShowTransactionAttachments
@@ -59,9 +59,8 @@ foreach my $message ( grep $_->__Value('Parent') == $Parent, @$Attachments ) {
               DisplayHeaders => \@DisplayHeaders,
             );
 
-    my $size = $message->ContentLength;
     my $name = defined $message->Filename && length $message->Filename ?  $message->Filename : '';
-    if ( $size ) {
+    if ( $message->ContentLength ) {
 </%PERL>
 <div class="downloadattachment">
 <a href="<% $AttachPath %>/<% $Transaction->Id %>/<% $message->Id %>/<% $name | u%>"><&|/l&>Download</&> <% length $name ? $name : loc('(untitled)') %></a>\
@@ -70,7 +69,7 @@ foreach my $message ( grep $_->__Value('Parent') == $Parent, @$Attachments ) {
 % }
 % $m->callback(CallbackName => 'AfterDownloadLinks', ARGSRef => \%ARGS, Ticket => $Ticket, Transaction => $Transaction, Attachment => $message);
 <br />
-<span class="downloadcontenttype"><% $message->ContentType %> <% $size_to_str->( $size ) %></span>
+<span class="downloadcontenttype"><% $message->ContentType %> <% $message->FriendlyContentLength %></span>
 </div>
 %   }
 %# If there is sub-messages, open a dedicated div
@@ -125,21 +124,6 @@ elsif (!$ShowHeaders)  {
 
 $m->callback(CallbackName => 'MassageDisplayHeaders', DisplayHeaders => \@DisplayHeaders, Transaction => $Transaction);
 
-my $size_to_str = sub {
-    my $size = shift;
-    # show a download link
-    if ( $size > 1024*1024 ) {
-        $size = loc( "[_1]m", int( $size / 1024 / 102.4 ) / 10 );
-    }
-    elsif ( $size > 1024 ) {
-        $size = loc( "[_1]k", int( $size / 102.4 ) / 10 );
-    }
-    else {
-        $size = loc( "[_1]b", $size );
-    }
-    return $size;
-};
-
 my $render_attachment = sub {
     my $message = shift;
     my $name = defined $message->Filename && length $message->Filename ?  $message->Filename : '';

commit f0137c97671e79a77f21370db5de3b7662bedb9f
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jul 6 17:51:45 2010 +0400

    delete variables we don't use

diff --git a/share/html/Ticket/Elements/ShowTransactionAttachments b/share/html/Ticket/Elements/ShowTransactionAttachments
index 1ba25c3..cda751d 100644
--- a/share/html/Ticket/Elements/ShowTransactionAttachments
+++ b/share/html/Ticket/Elements/ShowTransactionAttachments
@@ -96,16 +96,10 @@ $m->comp(
 $Ticket => undef
 $Transaction => undef
 $ShowHeaders => 0
-$Collapsed => undef
 $DownloadableHeaders => 1
-$ShowTitleBarCommands => 1
-$RowNum => 1
 $AttachPath => RT->Config->Get('WebPath')."/Ticket/Attachment"
-$UpdatePath => RT->Config->Get('WebPath')."/Ticket/Update.html"
-$EmailRecordPath => RT->Config->Get('WebPath')."/Ticket/ShowEmailRecord.html"
 $Attachments => undef
 $AttachmentContent => {}
-$ShowBody => 1
 $Parent => 0
 $ParentObj => undef
 $WarnUnsigned => 0

commit 599fcda86772fb5797057cd3243ab1b9166b0d69
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jul 7 17:03:57 2010 +0400

    escape filename for title/alt and add it the end of URL for download

diff --git a/share/html/Ticket/Elements/ShowTransactionAttachments b/share/html/Ticket/Elements/ShowTransactionAttachments
index cda751d..47d0c3e 100644
--- a/share/html/Ticket/Elements/ShowTransactionAttachments
+++ b/share/html/Ticket/Elements/ShowTransactionAttachments
@@ -234,19 +234,14 @@ my $render_attachment = sub {
             return;
         }
 
-        my $filename =  length $name ? $name : loc('(untitled)');
-        $m->out('<img'
-              . ' alt="'
-              . $filename
-              . '"' 
-              . ' title="'
-              . $filename
-              . '"' 
-              . ' src="'
-              . $AttachPath . '/'
-              . $Transaction->Id . '/'
-              . $message->Id
-              . '/" />' );
+        my $filename = length $name ? $name : loc('(untitled)');
+        my $efilename = $m->interp->apply_escapes( $filename, 'h' );
+        $m->out(
+            qq{<img alt="$efilename" title="$efilename"}
+            . ' src="'. $AttachPath .'/'. $Transaction->Id .'/'. $message->Id .'/'
+                . $m->interp->apply_escapes( $filename, 'u', 'h' )
+            . '" />'
+        );
     }
     elsif ( $message->ContentLength && $message->ContentLength > 0 ) {
         $m->out( '<p>' .

commit 7b88108f22ab54e33683a59b7b054ef9b4af5c58
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jul 7 17:19:13 2010 +0400

    announce record type as CSS class on history wrapping div

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index e75f2fc..aa189d4 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -67,7 +67,7 @@ if ( $ShowDisplayModes or $ShowTitle ) {
         }
     }
 </%perl>
-<div class="history">
+<div class="history <% lc $record_type %>">
 <& /Widgets/TitleBoxStart,
     title => $ShowTitle ? loc('History') : ' ',
     titleright_raw => $titleright,
@@ -157,6 +157,8 @@ my %attachment_content = map { $_->id => $_ }
         $ARGS{ $arg } = $request_path . $path;
     }
 }
+
+my $record_type = $Object->RecordType;
 </%INIT>
 <%ARGS>
 $Object

commit 905a5a46ed8c966b9e0e460225575bbfedc714d6
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jul 7 17:21:38 2010 +0400

    use <record type>-<id>-history id for history wrapper

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index aa189d4..5cd3877 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -74,7 +74,7 @@ if ( $ShowDisplayModes or $ShowTitle ) {
 &>
 % }
 
-<div id="ticket-history">
+<div id="<% lc $record_type %>-<% $Object->id %>-history">
 <%perl>
 my $i = 1;
 while ( my $Transaction = $Transactions->Next ) {

commit 5052f08938b07e78b39e0e73c77eec01aa1d696f
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jul 7 17:25:26 2010 +0400

    move <div class="history <% lc $record_type %>"> out of if clause

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 5cd3877..3730119 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -45,6 +45,7 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
+<div class="history <% lc $record_type %>">
 <%perl>
 if ( $ShowDisplayModes or $ShowTitle ) {
     my $titleright = '';
@@ -67,7 +68,6 @@ if ( $ShowDisplayModes or $ShowTitle ) {
         }
     }
 </%perl>
-<div class="history <% lc $record_type %>">
 <& /Widgets/TitleBoxStart,
     title => $ShowTitle ? loc('History') : ' ',
     titleright_raw => $titleright,
@@ -118,8 +118,8 @@ while ( my $Transaction = $Transactions->Next ) {
 </div>
 % if ($ShowDisplayModes or $ShowTitle) {
 <& /Widgets/TitleBoxEnd &>
-</div>
 % }
+</div>
 <%INIT>
 my $Transactions = $Object->Transactions;
 

commit 8e16edbb146a8f7cdae5efde38658e2162623b23
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Sep 19 04:40:31 2010 +0400

    return error asap from RT::Attachment->Create

diff --git a/lib/RT/Attachment.pm b/lib/RT/Attachment.pm
index 3072822..7322285 100644
--- a/lib/RT/Attachment.pm
+++ b/lib/RT/Attachment.pm
@@ -177,6 +177,7 @@ sub Create {
 
         unless ($id) {
             $RT::Logger->crit("Attachment insert failed - ". $RT::Handle->dbh->errstr);
+            return ($id);
         }
 
         foreach my $part ( $Attachment->parts ) {
@@ -188,6 +189,7 @@ sub Create {
             );
             unless ($id) {
                 $RT::Logger->crit("Attachment insert failed: ". $RT::Handle->dbh->errstr);
+                return ($id);
             }
         }
         return ($id);

commit 0fb4b72ba301f7bdd9652713de8b988f189dd252
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Sep 20 03:00:45 2010 +0400

    use new Attachments retrieving API instead of comps

diff --git a/share/html/Helpers/TicketHistory b/share/html/Helpers/TicketHistory
index 357ee8f..f89521f 100644
--- a/share/html/Helpers/TicketHistory
+++ b/share/html/Helpers/TicketHistory
@@ -52,8 +52,8 @@ $id
 my $TicketObj = RT::Ticket->new($session{'CurrentUser'});
 $TicketObj->Load($id);
 
-my $attachments = $m->comp('/Ticket/Elements/FindAttachments', Ticket => $TicketObj);
-my $attachment_content = $m->comp('/Ticket/Elements/LoadTextAttachments', Ticket => $TicketObj);
+my $attachments = $TicketObj->Attachments;
+my $attachment_content = $TicketObj->TextAttachments;
 
 $m->comp('/Ticket/Elements/ShowHistory',
     Ticket => $TicketObj, 
diff --git a/share/html/Ticket/Display.html b/share/html/Ticket/Display.html
index 391b7df..45f5bcd 100644
--- a/share/html/Ticket/Display.html
+++ b/share/html/Ticket/Display.html
@@ -220,7 +220,7 @@ MaybeRedirectForResults(
 # Get the transactoins before the attachments, for great ACL justice
 my $transactions = $m->comp('Elements/FindTransactions',Ticket => $TicketObj, Tickets => $Tickets || undef);
 my $attachments = $m->comp('Elements/FindAttachments', Ticket => $TicketObj, Tickets => $Tickets);
-my $attachment_content = $m->comp('Elements/LoadTextAttachments', Ticket => $TicketObj);
+my $attachment_content = $TicketObj->TextAttachments;
 
 my %link_rel;
 if (defined $session{'tickets'} and ($ARGS{'Query'} or $session{'CurrentSearchHash'}->{'Query'})) {
diff --git a/share/html/Ticket/Elements/LoadTextAttachments b/share/html/Ticket/Elements/LoadTextAttachments
index a444306..ce1eadc 100644
--- a/share/html/Ticket/Elements/LoadTextAttachments
+++ b/share/html/Ticket/Elements/LoadTextAttachments
@@ -47,7 +47,7 @@
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
 
-# DON'T USE IT. it's here for backwards
+# DON'T USE IT. it's here for backwards compatibility
 
 return $Ticket->TextAttachments;
 </%INIT>
diff --git a/share/html/Ticket/Elements/ShowHistory b/share/html/Ticket/Elements/ShowHistory
index a0bb71b..0562413 100644
--- a/share/html/Ticket/Elements/ShowHistory
+++ b/share/html/Ticket/Elements/ShowHistory
@@ -139,7 +139,7 @@ while ( my $Transaction = $Transactions->Next ) {
 <%INIT>
 $Transactions ||= $m->comp('/Ticket/Elements/FindTransactions',Ticket => $Ticket, Tickets => $Tickets || undef);
 $Attachments ||=  $m->comp('/Ticket/Elements/FindAttachments', Ticket => $Ticket, Tickets => $Tickets || undef);
-$AttachmentContent ||= $m->comp('/Ticket/Elements/LoadTextAttachments', Ticket => $Ticket);
+$AttachmentContent ||= $Ticket->TextAttachments;
 </%INIT>
 <%ARGS>
 $URIFile => RT->Config->Get('WebPath')."/Ticket/Display.html"
diff --git a/share/html/Ticket/History.html b/share/html/Ticket/History.html
index afc174f..4475e7a 100644
--- a/share/html/Ticket/History.html
+++ b/share/html/Ticket/History.html
@@ -77,9 +77,8 @@ unless ($Ticket->CurrentUserHasRight('ShowTicket')) {
     Abort("No permission to view ticket");
 }
 
-my $attachments = $m->comp('Elements/FindAttachments', Ticket => $Ticket);
-my $attachment_content = $m->comp('Elements/LoadTextAttachments', Ticket =>
-$Ticket);
+my $attachments = $Ticket->Attachments;
+my $attachment_content = $Ticket->TextAttachments;
 
 
 </%INIT>

commit fa4a863a047c353c2081045b88dfe8ae12b80085
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Sep 21 00:37:30 2010 +0400

    make CSS classes ticket-agnostic

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 3730119..2fe99f8 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -74,7 +74,7 @@ if ( $ShowDisplayModes or $ShowTitle ) {
 &>
 % }
 
-<div id="<% lc $record_type %>-<% $Object->id %>-history">
+<div id="<% lc $record_type %>-<% $Object->id %>-history" class="history-container">
 <%perl>
 my $i = 1;
 while ( my $Transaction = $Transactions->Next ) {
diff --git a/share/html/NoAuth/css/aileron/msie.css b/share/html/NoAuth/css/aileron/msie.css
index c5c091d..5f3c460 100644
--- a/share/html/NoAuth/css/aileron/msie.css
+++ b/share/html/NoAuth/css/aileron/msie.css
@@ -104,7 +104,7 @@ div#nav li.last {
 }
 
 
-.ticket-transaction .type a { font-weight: normal; text-decoration: none; color: #fff; }
+.transaction .type a { font-weight: normal; text-decoration: none; color: #fff; }
 
 
 
diff --git a/share/html/NoAuth/css/aileron/msie6.css b/share/html/NoAuth/css/aileron/msie6.css
index 6341e88..cd0f293 100644
--- a/share/html/NoAuth/css/aileron/msie6.css
+++ b/share/html/NoAuth/css/aileron/msie6.css
@@ -70,7 +70,7 @@ div#body {
 }
 
 
-.ticket-transaction .messagebody img {
+.transaction .messagebody img {
     /* ie6 does not support max-width */
     width: expression(this.width > 401 ? 400 : true);
 }
diff --git a/share/html/NoAuth/css/aileron/ticket.css b/share/html/NoAuth/css/aileron/ticket.css
index 7b573f7..1c22066 100644
--- a/share/html/NoAuth/css/aileron/ticket.css
+++ b/share/html/NoAuth/css/aileron/ticket.css
@@ -45,17 +45,17 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-div#ticket-history div.ticket-transaction {
+div.history-container div.transaction {
  border-top: 1px solid #ccc;
  padding-bottom: 0.25em;
     position: relative; /* gives us a container for position: absolute */
 }
 
-div#ticket-history div.odd {
+div.history-container div.odd {
  background-color: #fff;
 }
 
-div#ticket-history {
+div.history-container {
 
  margin-top: 0.75em;
  border-left: 1px solid #ccc;
@@ -65,7 +65,7 @@ div#ticket-history {
 
 }
 
-.ticket-transaction div.metadata span.actions {
+.transaction div.metadata span.actions {
  position: absolute;
  top: 0;
  right: 0;
@@ -82,7 +82,7 @@ div#ticket-history {
  border-radius: 0 0 0 0.5em;
 }
 
-.ticket-transaction div.metadata  span.type {
+.transaction div.metadata  span.type {
  text-align: center;
  float: left;
  margin: 0.25em 0.70em 0.25em 0.25em;
@@ -97,13 +97,13 @@ div#ticket-history {
 
 }
 
-div#ticket-history span.type a {
+div.history-container span.type a {
  color: #fff;
  padding-top: 0.75em;
  display: block;
 }
 
-#ticket-history a#lasttrans {
+.history-container a#lasttrans {
     display: inline;
     height: 0;
     width: 0;
@@ -112,28 +112,28 @@ div#ticket-history span.type a {
 }
 
 
-div#ticket-history span.date {
+div.history-container span.date {
  width: 10em;
 }
 
 
-div#ticket-history span.description {
+div.history-container span.description {
  margin-left: 1em;
  font-weight: bold;
 }
 
-div#ticket-history span.time-taken {
+div.history-container span.time-taken {
  margin-left: 1em;
 }
 
-div#ticket-history div.content {
+div.history-container div.content {
  padding-right: 1em;
  padding-bottom: 0.7em;
  margin-left: 1.5em;
 }
 
 
-.ticket-transaction .messagebody {
+.transaction .messagebody {
  font-size: 1em;
  padding-left: 1em;
  margin-top: 0.5em;
@@ -146,11 +146,11 @@ div#ticket-history div.content {
  word-wrap: break-word;
 }
 
-.ticket-transaction .messagebody img {
+.transaction .messagebody img {
  max-width: 100%;
 }
 
-div#ticket-history div.downloadattachment {
+div.history-container div.downloadattachment {
 float: right;
 clear: both;
 font-size: 0.9em;
@@ -168,20 +168,20 @@ margin-top: 0.5em;
  border-radius: 0.5em;
 }
 
-div#ticket-history div.downloadattachment .downloadcontenttype{
+div.history-container div.downloadattachment .downloadcontenttype{
 color: #666;
 padding-right:0.25em;
 }
 
 
-div#ticket-history .message-header-key {
+div.history-container .message-header-key {
   width: 7em;
   font-weight: bold;
   color: #666;
 }
 
 
-div#ticket-history .messagebody .messagebody{
+div.history-container .messagebody .messagebody{
  font-size: 1em;
  padding: 0;
  border: 0;
@@ -190,14 +190,14 @@ div#ticket-history .messagebody .messagebody{
 
 
 
-.ticket-transaction.basics .type { background: #b32; }
-.ticket-transaction.cfs .type { background: #b32; }
-.ticket-transaction.people .type { background: #48c; }
-.ticket-transaction.links .type { background: #316531; }
-.ticket-transaction.dates .type { background: #633063; }
-.ticket-transaction.message .type { background: #069; }
-.ticket-transaction.reminders .type { background: #369; }
-.ticket-transaction.other .type { background: #abc; }
+.transaction.basics .type { background: #b32; }
+.transaction.cfs .type { background: #b32; }
+.transaction.people .type { background: #48c; }
+.transaction.links .type { background: #316531; }
+.transaction.dates .type { background: #633063; }
+.transaction.message .type { background: #069; }
+.transaction.reminders .type { background: #369; }
+.transaction.other .type { background: #abc; }
 
 
 /* Color the titlebox tabs */
diff --git a/share/html/NoAuth/css/ballard/msie.css b/share/html/NoAuth/css/ballard/msie.css
index ca2676f..dc05298 100644
--- a/share/html/NoAuth/css/ballard/msie.css
+++ b/share/html/NoAuth/css/ballard/msie.css
@@ -140,7 +140,7 @@ div#nav li.last {
 }
 
 
-.ticket-transaction .type a { font-weight: normal; text-decoration: none; color: #fff; }
+.transaction .type a { font-weight: normal; text-decoration: none; color: #fff; }
 
 
 .titlebox {
diff --git a/share/html/NoAuth/css/ballard/msie6.css b/share/html/NoAuth/css/ballard/msie6.css
index e5aef51..2467d33 100644
--- a/share/html/NoAuth/css/ballard/msie6.css
+++ b/share/html/NoAuth/css/ballard/msie6.css
@@ -81,7 +81,7 @@ div#page-navigation ul#page-menu {
 }
 
 
-.ticket-transaction .messagebody img {
+.transaction .messagebody img {
     /* ie6 does not support max-width */
     width: expression(this.width > 401 ? 400 : true);
 }
diff --git a/share/html/NoAuth/css/ballard/ticket.css b/share/html/NoAuth/css/ballard/ticket.css
index 4d416e1..536fca8 100644
--- a/share/html/NoAuth/css/ballard/ticket.css
+++ b/share/html/NoAuth/css/ballard/ticket.css
@@ -45,17 +45,17 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-div#ticket-history div.ticket-transaction {
+div.history-container div.transaction {
  border-top: 1px solid #ccc;
  padding-bottom: 0.25em;
     position: relative; /* gives us a container for position: absolute */
 }
 
-div#ticket-history div.odd {
+div.history-container div.odd {
  background-color: #fff;
 }
 
-div#ticket-history {
+div.history-container {
 
  margin-top: 0.75em;
  border-left: 1px solid #ccc;
@@ -65,7 +65,7 @@ div#ticket-history {
 
 }
 
-.ticket-transaction div.metadata span.actions {
+.transaction div.metadata span.actions {
  position: absolute;
  top: 0;
  right: 0;
@@ -81,7 +81,7 @@ div#ticket-history {
  white-space: nowrap;
 }
 
-.ticket-transaction div.metadata  span.type {
+.transaction div.metadata  span.type {
  text-align: center;
  float: left;
  margin: 0.25em 0.70em 0.25em 0.25em;
@@ -95,32 +95,32 @@ div#ticket-history {
  border-bottom-right-radius: 0.25em;
 }
 
-div#ticket-history span.type a {
+div.history-container span.type a {
  color: #fff;
 }
 
 
-div#ticket-history span.date {
+div.history-container span.date {
  width: 10em;
 }
 
 
-div#ticket-history span.description {
+div.history-container span.description {
  margin-left: 1em;
  font-weight: bold;
 }
 
-div#ticket-history span.time-taken {
+div.history-container span.time-taken {
  margin-left: 1em;
 }
 
-div#ticket-history div.content {
+div.history-container div.content {
  padding-right: 1em;
  padding-bottom: 0.7em;
  margin-left: 1.5em;
 }
 
-.ticket-transaction .messagebody {
+.transaction .messagebody {
  font-size: 1em;
  padding-left: 1em;
  margin-top: 0.5em;
@@ -133,11 +133,11 @@ div#ticket-history div.content {
  word-wrap: break-word;
 }
 
-.ticket-transaction .messagebody img {
+.transaction .messagebody img {
  max-width: 100%;
 }
 
-div#ticket-history div.downloadattachment {
+div.history-container div.downloadattachment {
 float: right;
 clear: both;
 font-size: 0.9em;
@@ -156,20 +156,20 @@ border-radius: 0.5em;
 
 }
 
-div#ticket-history div.downloadattachment .downloadcontenttype{
+div.history-container div.downloadattachment .downloadcontenttype{
 color: #666;
 padding-right:0.25em;
 }
 
 
-div#ticket-history .message-header-key {
+div.history-container .message-header-key {
   width: 7em;
   font-weight: bold;
   color: #666;
 }
 
 
-div#ticket-history .messagebody .messagebody{
+div.history-container .messagebody .messagebody{
  font-size: 1em;
  padding: 0; 
  border: 0;
@@ -178,14 +178,14 @@ div#ticket-history .messagebody .messagebody{
 
 
 
-.ticket-transaction.basics .type { background: #b32; }
-.ticket-transaction.cfs .type { background: #b32; }
-.ticket-transaction.people .type { background: #48c; }
-.ticket-transaction.links .type { background: #316531; }
-.ticket-transaction.dates .type { background: #633063; }
-.ticket-transaction.message .type { background: #069; }
-.ticket-transaction.reminders .type { background: #369; }
-.ticket-transaction.other .type { background: #abc; }
+.transaction.basics .type { background: #b32; }
+.transaction.cfs .type { background: #b32; }
+.transaction.people .type { background: #48c; }
+.transaction.links .type { background: #316531; }
+.transaction.dates .type { background: #633063; }
+.transaction.message .type { background: #069; }
+.transaction.reminders .type { background: #369; }
+.transaction.other .type { background: #abc; }
 
 
 
diff --git a/share/html/NoAuth/css/print.css b/share/html/NoAuth/css/print.css
index 391fa93..6f25413 100644
--- a/share/html/NoAuth/css/print.css
+++ b/share/html/NoAuth/css/print.css
@@ -89,8 +89,8 @@ div#header h1 {
 .ticket-info-links .titlebox-title .right,
 .ticket-info-links .titlebox-content .create,
 .history .titlebox .titlebox-title .right,
-.ticket-transaction .metadata .actions,
-.ticket-transaction .content .downloadattachment,
+.transaction .metadata .actions,
+.transaction .content .downloadattachment,
 #comp-Search-Results #body .refresh,
 .search-result-actions,
 #comp-Search-Chart #body div,
@@ -113,7 +113,7 @@ a:link, a:visited {
     font-weight: bold !important;
 }
 
-div.ticket-transaction .metadata .type {
+div.transaction .metadata .type {
     display: none;
 }
 
@@ -162,11 +162,11 @@ div.titlebox-title {
     border: 0 !important;
 }
 
-#ticket-history, div.ticket-transaction {
+.history-container, div.transaction {
     border: 0 !important;
 }
 
-div.ticket-transaction {
+div.transaction {
     page-break-inside: avoid;
 }
 
diff --git a/share/html/NoAuth/css/web2/msie.css b/share/html/NoAuth/css/web2/msie.css
index 81b31e4..5411729 100644
--- a/share/html/NoAuth/css/web2/msie.css
+++ b/share/html/NoAuth/css/web2/msie.css
@@ -141,7 +141,7 @@ div#nav li.last {
 }
 
 
-.ticket-transaction .type a { font-weight: normal; text-decoration: none; color: #fff; }
+.transaction .type a { font-weight: normal; text-decoration: none; color: #fff; }
 
 
 .titlebox {
diff --git a/share/html/NoAuth/css/web2/msie6.css b/share/html/NoAuth/css/web2/msie6.css
index 25ef858..a632750 100644
--- a/share/html/NoAuth/css/web2/msie6.css
+++ b/share/html/NoAuth/css/web2/msie6.css
@@ -81,7 +81,7 @@ div#page-navigation {
 }
 
 
-.ticket-transaction .messagebody img {
+.transaction .messagebody img {
     /* ie6 does not support max-width */
     width: expression(this.width > 401 ? 400 : true);
 }
diff --git a/share/html/NoAuth/css/web2/ticket.css b/share/html/NoAuth/css/web2/ticket.css
index 7d08166..12abf41 100644
--- a/share/html/NoAuth/css/web2/ticket.css
+++ b/share/html/NoAuth/css/web2/ticket.css
@@ -45,17 +45,17 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-div#ticket-history div.ticket-transaction {
+div.history-container div.transaction {
  border-top: 1px solid #ccc;
  padding-bottom: 0.25em;
     position: relative; /* gives us a container for position: absolute */
 }
 
-div#ticket-history div.odd {
+div.history-container div.odd {
  background-color: #fff;
 }
 
-div#ticket-history {
+div.history-container {
 
  margin-top: 0.75em;
  border-left: 1px solid #ccc;
@@ -65,7 +65,7 @@ div#ticket-history {
 
 }
 
-.ticket-transaction div.metadata span.actions {
+.transaction div.metadata span.actions {
  position: absolute;
  top: 0;
  right: 0;
@@ -82,7 +82,7 @@ div#ticket-history {
  border-radius: 0 0 0 0.5em;
 }
 
-.ticket-transaction div.metadata  span.type {
+.transaction div.metadata  span.type {
  text-align: center;
  float: left;
  margin: 0.25em 0.70em 0.25em 0.25em;
@@ -98,32 +98,32 @@ div#ticket-history {
 
 }
 
-div#ticket-history span.type a {
+.transaction div.metadata span.type a {
  color: #fff;
 }
 
 
-div#ticket-history span.date {
+.transaction div.metadata span.date {
  width: 10em;
 }
 
 
-div#ticket-history span.description {
+.transaction div.metadata span.description {
  margin-left: 1em;
  font-weight: bold;
 }
 
-div#ticket-history span.time-taken {
+.transaction div.metadata span.time-taken {
  margin-left: 1em;
 }
 
-div#ticket-history div.content {
+.transaction div.content {
  padding-right: 1em;
  padding-bottom: 0.7em;
  margin-left: 1.5em;
 }
 
-.ticket-transaction .messagebody {
+.transaction .messagebody {
  font-size: 1em;
  padding-left: 1em;
  margin-top: 0.5em;
@@ -136,11 +136,11 @@ div#ticket-history div.content {
  word-wrap: break-word;
 }
 
-.ticket-transaction .messagebody img {
+.transaction .messagebody img {
  max-width: 100%;
 }
 
-div#ticket-history div.downloadattachment {
+div.history-container div.downloadattachment {
 float: right;
 clear: both;
 font-size: 0.9em;
@@ -158,20 +158,20 @@ margin-top: 0.5em;
  border-radius: 0.5em;
 }
 
-div#ticket-history div.downloadattachment .downloadcontenttype{
+div.history-container div.downloadattachment .downloadcontenttype {
 color: #666;
 padding-right:0.25em;
 }
 
 
-div#ticket-history .message-header-key {
+div.history-container .message-header-key {
   width: 7em;
   font-weight: bold;
   color: #666;
 }
 
 
-div#ticket-history .messagebody .messagebody{
+div.history-container .messagebody .messagebody{
  font-size: 1em;
  padding: 0; 
  border: 0;
@@ -180,14 +180,14 @@ div#ticket-history .messagebody .messagebody{
 
 
 
-.ticket-transaction.basics .type { background: #b32; }
-.ticket-transaction.cfs .type { background: #b32; }
-.ticket-transaction.people .type { background: #48c; }
-.ticket-transaction.links .type { background: #316531; }
-.ticket-transaction.dates .type { background: #633063; }
-.ticket-transaction.message .type { background: #069; }
-.ticket-transaction.reminders .type { background: #369; }
-.ticket-transaction.other .type { background: #abc; }
+.transaction.basics .type { background: #b32; }
+.transaction.cfs .type { background: #b32; }
+.transaction.people .type { background: #48c; }
+.transaction.links .type { background: #316531; }
+.transaction.dates .type { background: #633063; }
+.transaction.message .type { background: #069; }
+.transaction.reminders .type { background: #369; }
+.transaction.other .type { background: #abc; }
 
 
 
diff --git a/share/html/Ticket/Elements/ShowHistory b/share/html/Ticket/Elements/ShowHistory
index 0562413..2508ee0 100644
--- a/share/html/Ticket/Elements/ShowHistory
+++ b/share/html/Ticket/Elements/ShowHistory
@@ -45,6 +45,7 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
+<div class="history ticket">
 <%doc>
 #   This is (ab)used in Admin/(Users|Groups)/History.html and should probably
 #   be generalized at some point.
@@ -77,10 +78,9 @@ if ($ShowDisplayModes or $ShowTitle) {
         }
     }
 </%perl>
-<div class="history">
 <& /Widgets/TitleBoxStart, title => $title, titleright_raw => $titleright &>
 % }
-<div id="ticket-history">
+<div id="ticket-<% $Ticket->id %>-history" class="history-container">
 <%perl>
 my $trans_content = {};
 my $trans_attachments = {};
@@ -134,8 +134,8 @@ while ( my $Transaction = $Transactions->Next ) {
 </div>
 % if ($ShowDisplayModes or $ShowTitle) {
 <& /Widgets/TitleBoxEnd &>
-</div>
 % }
+</div>
 <%INIT>
 $Transactions ||= $m->comp('/Ticket/Elements/FindTransactions',Ticket => $Ticket, Tickets => $Tickets || undef);
 $Attachments ||=  $m->comp('/Ticket/Elements/FindAttachments', Ticket => $Ticket, Tickets => $Tickets || undef);
diff --git a/share/html/Ticket/Elements/ShowTransaction b/share/html/Ticket/Elements/ShowTransaction
index 1becb3c..0be60c3 100644
--- a/share/html/Ticket/Elements/ShowTransaction
+++ b/share/html/Ticket/Elements/ShowTransaction
@@ -45,9 +45,10 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<div class="ticket-transaction <% $type_class %> <%$type%> <% $RowNum % 2 ? 'odd' : 'even' %>">
+<div class="transaction <% $type_class %> <%$type%> <% $RowNum % 2 ? 'odd' : 'even' %>">
 % $m->callback( titlebar_cmd => \$titlebar_commands, Transaction => $Transaction, %ARGS, CallbackName => 'ModifyDisplay' );
-<div class="ticket-transaction">
+
+<div class="transaction">
 % $m->callback( titlebar_cmd => \$titlebar_commands, Transaction => $Transaction, %ARGS, CallbackName => 'ModifyCommand' );
   <div class="metadata">
     <span class="type">

commit 52ab3efd749e9842dea3498b82a92918f49bddf1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Sep 21 00:42:31 2010 +0400

    organize pre-fetched attachments like we need them later

diff --git a/share/html/Elements/ShowTransaction b/share/html/Elements/ShowTransaction
index 5038f6b..adc6539 100644
--- a/share/html/Elements/ShowTransaction
+++ b/share/html/Elements/ShowTransaction
@@ -132,6 +132,8 @@ $time = loc('[quant,_1,min,min]', $Transaction->TimeTaken)
     if $Transaction->TimeTaken;
 
 if ( $ShowBody && !$Attachments ) {
+    $ARGS{'Attachments'} = $Attachments = {};
+
     my $attachments = $Transaction->Attachments;
     $attachments->Columns( qw(
         Id TransactionId Parent MessageId
@@ -139,7 +141,8 @@ if ( $ShowBody && !$Attachments ) {
         ContentEncoding ContentType
         Creator Created
     ) );
-    $ARGS{'Attachments'} = $Attachments = $attachments->ItemsArrayRef;
+    push @{ $Attachments->{ $_->Parent || 0 } ||= [] }, $_
+        foreach @{ $attachments->ItemsArrayRef };
 }
 
 my @actions = ();
diff --git a/share/html/Ticket/Elements/ShowHistory b/share/html/Ticket/Elements/ShowHistory
index 2508ee0..2bb7fa3 100644
--- a/share/html/Ticket/Elements/ShowHistory
+++ b/share/html/Ticket/Elements/ShowHistory
@@ -90,7 +90,8 @@ for my $content (@{$AttachmentContent->ItemsArrayRef()}) {
 }
 
 for my $attachment (@{$Attachments->ItemsArrayRef()}) {
-    push (@{$trans_attachments->{$attachment->TransactionId}}, $attachment)
+    my $tmp = $trans_attachments->{ $attachment->TransactionId } ||= {};
+    push @{ $tmp->{ $attachment->Parent || 0 } ||= [] }, $attachment;
 }
 
 my $i = 1;
@@ -118,7 +119,7 @@ while ( my $Transaction = $Transactions->Next ) {
         Transaction          => $Transaction,
         ShowHeaders          => $ShowHeaders,
         RowNum               => $i,
-        Attachments          => $trans_attachments->{$Transaction->id},
+        Attachments          => $trans_attachments->{$Transaction->id} || {},
         AttachmentContent    => $trans_content,
         LastTransaction      => $IsLastTransaction
     );
diff --git a/share/html/Ticket/Elements/ShowTransaction b/share/html/Ticket/Elements/ShowTransaction
index 0be60c3..07027d8 100644
--- a/share/html/Ticket/Elements/ShowTransaction
+++ b/share/html/Ticket/Elements/ShowTransaction
@@ -121,7 +121,10 @@ $TimeTaken = $Transaction->TimeTaken . " min"
 unless ($Attachments) { 
     my $attachments = $Transaction->Attachments;
     $attachments->Columns( qw( Id Filename ContentType Headers Subject Parent ContentEncoding ContentType TransactionId) );
-    $ARGS{'Attachments'} = $Attachments = $attachments->ItemsArrayRef();
+    $ARGS{'Attachments'} = $Attachments = {};
+
+    push @{ $Attachments->{ $_->Parent || 0 } ||= [] }, $_
+        foreach $attachments->ItemsArrayRef();
 }
 my $titlebar_commands = '';
 
@@ -140,7 +143,7 @@ if ( $type =~ /EmailRecord$/ ) {
       . "&Transaction="
       . $Transaction->Id
       . "&Attachment="
-      . ( $Attachments->[0] && $Attachments->[0]->id )
+      . ( $Attachments->{0}[0] && $Attachments->{0}[0]->id )
       . '">' . loc('Show') . "</a>] ";
     $ShowBody = 0;
 }
@@ -149,7 +152,7 @@ if ( $type =~ /EmailRecord$/ ) {
 # If the transaction has anything attached to it at all
 else {
 
-    if ( $Attachments->[0] && $ShowTitleBarCommands ) {
+    if ( keys %$Attachments && $ShowTitleBarCommands ) {
         my $ticket = $Transaction->TicketObj;
         my $can_modify = $ticket->CurrentUserHasRight('ModifyTicket');
         if ( $can_modify || $ticket->CurrentUserHasRight('ReplyToTicket') ) {
diff --git a/share/html/Ticket/Elements/ShowTransactionAttachments b/share/html/Ticket/Elements/ShowTransactionAttachments
index 47d0c3e..fbb0507 100644
--- a/share/html/Ticket/Elements/ShowTransactionAttachments
+++ b/share/html/Ticket/Elements/ShowTransactionAttachments
@@ -48,7 +48,7 @@
 <%PERL>
 # Find all the attachments which have parent $Parent
 # For each of these attachments
-foreach my $message ( grep $_->__Value('Parent') == $Parent, @$Attachments ) {
+foreach my $message ( @{ $Attachments->{ $Parent || 0 } || [] } ) {
 
     if (RT->Config->Get('GnuPG')->{'Enable'}) {
         $m->comp( 'ShowGnuPGStatus', Attachment => $message, WarnUnsigned => $WarnUnsigned );
@@ -73,7 +73,7 @@ foreach my $message ( grep $_->__Value('Parent') == $Parent, @$Attachments ) {
 </div>
 %   }
 %# If there is sub-messages, open a dedicated div
-% if ( scalar ( grep $_->__Value('Parent') == $message->id, @$Attachments ) ) {
+% if ( $Attachments->{ $message->id } ) {
 <div class="messageattachments">
 % } else {
 <div class="messagebody">

commit d7db2c3e1949e95f19f4779ce81fe631d83a5df1
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Sep 7 22:28:38 2012 -0700

    Filter down from all readable columns rather than hardcode a list
    
    One less array of hardcoded columns which we'll forget to update in the
    future.

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index d910862..2e9cf9d 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1624,12 +1624,8 @@ sub Attachments {
         WithContent => 0,
         @_
     );
-    my @columns = qw(
-        id TransactionId Parent MessageId
-        Subject Filename
-        ContentType ContentEncoding
-        Creator Created
-    );
+    my @columns = grep { not /^(Headers|Content)$/ }
+                       RT::Attachment->ReadableAttributes;
     push @columns, 'Headers' if $args{'WithHeaders'};
     push @columns, 'Content' if $args{'WithContent'};
 
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 9e4cc43..d620713 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -3584,12 +3584,8 @@ sub Attachments {
         return $res;
     }
 
-    my @columns = qw(
-        id TransactionId Parent MessageId
-        Subject Filename
-        ContentType ContentEncoding
-        Creator Created
-    );
+    my @columns = grep { not /^(Headers|Content)$/ }
+                       RT::Attachment->ReadableAttributes;
     push @columns, 'Headers' if $args{'WithHeaders'};
     push @columns, 'Content' if $args{'WithContent'};
 

commit b03146927ad83b81d45b161c2dfbe5223739845f
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Nov 16 14:57:58 2012 -0800

    Add comment about a potential perf improvement when listing attachments

diff --git a/share/html/Ticket/Elements/ShowAttachments b/share/html/Ticket/Elements/ShowAttachments
index 12130e4..13e4ea8 100644
--- a/share/html/Ticket/Elements/ShowAttachments
+++ b/share/html/Ticket/Elements/ShowAttachments
@@ -102,6 +102,8 @@ if ($size) {
 # then we need to find one
 $Attachments ||= $m->comp('FindAttachments', Ticket => $Ticket);
 
+# XXX PERF: why doesn't this Limit on Filename to avoid fetching *all* the
+# attachments?
 my %documents;
 while ( my $attach = $Attachments->Next() ) {
     next unless defined $attach->Filename && length $attach->Filename;

commit ff7d41079af5bfc68e5e92fc83a45da911d9642d
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Nov 16 15:50:22 2012 -0800

    Remove the AttachmentUnits option and use FriendlyContentLength instead
    
    This option was added with 3.8.8 specifically for a single customer and
    is of dubious general utility.  Additionally, it didn't control display
    of size in the transaction history.  By refactoring our display of
    ContentLength into FriendlyContentLength, anyone requiring a specific
    display can simply overlay the method in RT::Attachment.

diff --git a/docs/UPGRADING-4.2 b/docs/UPGRADING-4.2
index 66af083..e61f4f4 100644
--- a/docs/UPGRADING-4.2
+++ b/docs/UPGRADING-4.2
@@ -4,3 +4,7 @@ UPGRADING FROM RT 4.0.0 and greater
   describes what the log level controls.  Setting $LogToScreen will still work,
   but an informational notice will be issued on server start telling you about
   the rename.  To avoid this you should set $LogToSTDERR instead.
+
+* The $AttachmentUnits option was removed in preference of always displaying in
+  megabytes, kilobytes, or bytes as appropriate.  The option was incompletely
+  implemented and controlled display in the attachments list but not history.
diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 07923ac..3e50ec7 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1546,18 +1546,6 @@ overrides C<TrustHTMLAttachments>.
 
 Set($AlwaysDownloadAttachments, undef);
 
-=item C<$AttachmentUnits>
-
-Controls the units (kilobytes or bytes) that attachment sizes use for
-display. The default is to display kilobytes if the attachment is
-larger than 1024 bytes, bytes otherwise. If you set
-C<$AttachmentUnits> to C<'k'> then attachment sizes will always be
-displayed in kilobytes. If set to C<'b'>, then sizes will be bytes.
-
-=cut
-
-Set($AttachmentUnits, undef);
-
 =item C<$PreferRichText>
 
 If C<$PreferRichText> is set to 1, RT will show HTML/Rich text messages
diff --git a/share/html/Ticket/Elements/ShowAttachments b/share/html/Ticket/Elements/ShowAttachments
index 13e4ea8..f7e6d1f 100644
--- a/share/html/Ticket/Elements/ShowAttachments
+++ b/share/html/Ticket/Elements/ShowAttachments
@@ -56,34 +56,10 @@
 <%$key%><br />
 <ul>
 % foreach my $rev (@{$documents{$key}}) {
-
-<%PERL>
-my $size = $rev->ContentLength;
-
-if ($size) {
-    my $kb = int($size/102.4) / 10;
-    my $units = RT->Config->Get('AttachmentUnits');
-
-    if (!defined($units)) {
-        if ($size > 1024) {
-            $size = $kb . "k";
-        }
-        else {
-            $size = $size . "b";
-        }
-    }
-    elsif ($units eq 'k') {
-        $size = $kb . "k";
-    }
-    else {
-        $size = $size . "b";
-    }
-
-</%PERL>
-
+% if ($rev->ContentLength) {
 <li><font size="-2">
 <a href="<%RT->Config->Get('WebPath')%>/Ticket/Attachment/<%$rev->TransactionId%>/<%$rev->Id%>/<%$rev->Filename | u%>">
-% my $desc = loc("[_1] ([_2]) by [_3]", $rev->CreatedAsString, $size, $m->scomp('/Elements/ShowUser', User => $rev->CreatorObj));
+% my $desc = loc("[_1] ([_2]) by [_3]", $rev->CreatedAsString, $rev->FriendlyContentLength, $m->scomp('/Elements/ShowUser', User => $rev->CreatorObj));
 <% $desc |n%>
 </a>
 </font></li>
diff --git a/share/html/m/ticket/show b/share/html/m/ticket/show
index 2b41fd8..4c3b2c4 100644
--- a/share/html/m/ticket/show
+++ b/share/html/m/ticket/show
@@ -314,34 +314,10 @@ my $print_value = sub {
 <%$key%><br />
 <ul>
 % foreach my $rev (@{$documents{$key}}) {
-
-<%PERL>
-my $size = $rev->ContentLength;
-
-if ($size) {
-    my $kb = int($size/102.4) / 10;
-    my $units = RT->Config->Get('AttachmentUnits');
-
-    if (!defined($units)) {
-        if ($size > 1024) {
-            $size = $kb . "k";
-        }
-        else {
-            $size = $size . "b";
-        }
-    }
-    elsif ($units eq 'k') {
-        $size = $kb . "k";
-    }
-    else {
-        $size = $size . "b";
-    }
-
-</%PERL>
-
+% if ($rev->ContentLength) {
 <li><font size="-2">
 <a href="<%RT->Config->Get('WebPath')%>/Ticket/Attachment/<%$rev->TransactionId%>/<%$rev->Id%>/<%$rev->Filename | u%>">
-<&|/l, $rev->CreatedAsString, $size, $rev->CreatorObj->Name &>[_1] ([_2]) by [_3]</&>
+<&|/l, $rev->CreatedAsString, $rev->FriendlyContentLength, $rev->CreatorObj->Name &>[_1] ([_2]) by [_3]</&>
 </a>
 </font></li>
 % }

commit 30a303dcca591cb149a86d072af57861b0b646ee
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Nov 16 16:51:44 2012 -0800

    Remove support for intermixing transactions from multiple tickets in history
    
    Not supporting this anymore simplifies a lot of the ticket attachment
    and transaction finding code.  Core RT never used the functionality; it
    was added back in 2004 by 15fb328 to support an extension for a single
    customer.  Many places displaying history also neglected to support it
    since the functionality was in Ticket/Display.html instead of an
    Element.  Any future extensions which need to accomplish the same result
    may overlay/extend the FindTransactions element and the Attachments
    method in RT::Ticket instead of using callbacks to set a collection
    object.
    
    Note that this wasn't used for merged tickets; those are handled
    simply by limiting on EffectiveId instead of id.
    
    /Ticket/Elements/FindAttachments is now deprecated.

diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 23c7467..0845ff5 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -215,9 +215,7 @@ if ( defined ($id[0]) && $id[0] eq 'new' ) {
     }
 
     my $Transactions = $Ticket->Transactions;
-
-    my $attachments =
-      $m->comp( '/Ticket/Elements/FindAttachments', Ticket => $Ticket );
+    my $attachments = $Ticket->Attachments;
 
     my $LinkBasicsTitle = $Ticket->CurrentUserHasRight('ModifyTicket')
                           || $Ticket->CurrentUserHasRight('ReplyToTicket');
diff --git a/share/html/Ticket/Display.html b/share/html/Ticket/Display.html
index 45f5bcd..4c40e87 100644
--- a/share/html/Ticket/Display.html
+++ b/share/html/Ticket/Display.html
@@ -72,7 +72,6 @@
 % } else {
     <& /Ticket/Elements/ShowHistory ,
           Ticket => $TicketObj,
-          Tickets => $Tickets,
           Transactions => $transactions,
           Collapsed => $ARGS{'Collapsed'},
           ShowHeaders => $ARGS{'ShowHeaders'},
@@ -107,7 +106,7 @@ if ( ! $ARGS{'NoRedirect'} && RT::Interface::Web->MobileClient()) {
 }
 
 
-my (@Actions, $Tickets, $title);
+my (@Actions, $title);
 
 
 unless ($id || $TicketObj) {
@@ -142,7 +141,7 @@ if ($ARGS{'id'} eq 'new') {
     my $SkipProcessing;
 
     $m->callback( CallbackName => 'BeforeProcessArguments',
-        TicketObj => $TicketObj, Tickets => $Tickets,
+        TicketObj => $TicketObj,
         ActionsRef => \@Actions, ARGSRef => \%ARGS,
         SkipProcessing => \$SkipProcessing );
 
@@ -200,7 +199,6 @@ $title = loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject || '');
 $m->callback(
     CallbackName => 'BeforeDisplay',
     TicketObj => \$TicketObj,
-    Tickets => \$Tickets,
     Actions => \@Actions,
     title => \$title,
     ARGSRef => \%ARGS,
@@ -218,8 +216,8 @@ MaybeRedirectForResults(
 );
 
 # Get the transactoins before the attachments, for great ACL justice
-my $transactions = $m->comp('Elements/FindTransactions',Ticket => $TicketObj, Tickets => $Tickets || undef);
-my $attachments = $m->comp('Elements/FindAttachments', Ticket => $TicketObj, Tickets => $Tickets);
+my $transactions = $m->comp('Elements/FindTransactions', Ticket => $TicketObj);
+my $attachments = $TicketObj->Attachments;
 my $attachment_content = $TicketObj->TextAttachments;
 
 my %link_rel;
diff --git a/share/html/Ticket/Elements/FindAttachments b/share/html/Ticket/Elements/FindAttachments
index f73386e..9b997f8 100644
--- a/share/html/Ticket/Elements/FindAttachments
+++ b/share/html/Ticket/Elements/FindAttachments
@@ -46,47 +46,9 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
-# A default implementation here loops through all transactions and pulls out all their attachments.
-# We end up doing an end-run around that to get a bit more performance
-
-# We force the cache of ticket transactions to get populated up front. otherwise, the
-# code that looks at attachments will look at each one in turn.
-
-return $Ticket->Attachments unless $Tickets;
-
-my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
-$attachments->Columns( qw(
-    id TransactionId Parent MessageId
-    Subject Filename
-    ContentType ContentEncoding
-    Creator Created
-) );
-
-my $transactions = $attachments->TransactionsAlias;
-my $tickets = $attachments->Join(
-    ALIAS1 => $transactions,
-    FIELD1 => 'ObjectId',
-    TABLE2 => 'Tickets',
-    FIELD2 => 'id',
-);
-
-$attachments->Limit(
-    ALIAS => $transactions,
-    FIELD => 'ObjectType',
-    VALUE => 'RT::Ticket',
-);
-
-while ( my $Ticket = $Tickets->Next) {
-    $attachments->Limit(
-        ALIAS => $tickets,
-        FIELD => 'EffectiveId',
-        VALUE => $Ticket->id,
-    );
-}
-
-return ($attachments);
+# DEPRECATED: use the method below directly
+return $Ticket->Attachments;
 </%INIT>
 <%ARGS>
-$Ticket => undef
-$Tickets => undef
+$Ticket
 </%ARGS>
diff --git a/share/html/Ticket/Elements/FindTransactions b/share/html/Ticket/Elements/FindTransactions
index 6faf33e..c0ae5ca 100644
--- a/share/html/Ticket/Elements/FindTransactions
+++ b/share/html/Ticket/Elements/FindTransactions
@@ -46,16 +46,7 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
-my $Transactions = RT::Transactions->new($session{'CurrentUser'});
-if ($Tickets) {
-    while (my $t = $Tickets->Next) {
-        $Transactions->LimitToTicket($t->id);
-    }
-} else {
-    $Transactions = $Ticket->Transactions;
-}
-
-
+my $Transactions = $Ticket->Transactions;
 my $OldestFirst = RT->Config->Get( 'OldestTransactionsFirst', $session{'CurrentUser'} );
 my $SortOrder = $OldestFirst? 'ASC': 'DESC';
 $Transactions->OrderByCols( { FIELD => 'Created',
@@ -67,6 +58,5 @@ $Transactions->Next(); $Transactions->GotoFirstItem(); # actually do the search
 return ($Transactions);
 </%INIT>
 <%ARGS>
-$Ticket => undef
-$Tickets => undef
+$Ticket
 </%ARGS>
diff --git a/share/html/Ticket/Elements/LoadTextAttachments b/share/html/Ticket/Elements/LoadTextAttachments
index ce1eadc..a295f15 100644
--- a/share/html/Ticket/Elements/LoadTextAttachments
+++ b/share/html/Ticket/Elements/LoadTextAttachments
@@ -46,11 +46,9 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%INIT>
-
-# DON'T USE IT. it's here for backwards compatibility
-
+# DEPRECATED: use the method below directly
 return $Ticket->TextAttachments;
 </%INIT>
 <%ARGS>
-$Ticket => undef
+$Ticket
 </%ARGS>
diff --git a/share/html/Ticket/Elements/ShowAttachments b/share/html/Ticket/Elements/ShowAttachments
index f7e6d1f..71daf05 100644
--- a/share/html/Ticket/Elements/ShowAttachments
+++ b/share/html/Ticket/Elements/ShowAttachments
@@ -76,7 +76,7 @@
 
 # If we haven't been passed in an Attachments object (through the precaching mechanism)
 # then we need to find one
-$Attachments ||= $m->comp('FindAttachments', Ticket => $Ticket);
+$Attachments ||= $Ticket->Attachments;
 
 # XXX PERF: why doesn't this Limit on Filename to avoid fetching *all* the
 # attachments?
diff --git a/share/html/Ticket/Elements/ShowHistory b/share/html/Ticket/Elements/ShowHistory
index 2bb7fa3..6829f3e 100644
--- a/share/html/Ticket/Elements/ShowHistory
+++ b/share/html/Ticket/Elements/ShowHistory
@@ -138,14 +138,13 @@ while ( my $Transaction = $Transactions->Next ) {
 % }
 </div>
 <%INIT>
-$Transactions ||= $m->comp('/Ticket/Elements/FindTransactions',Ticket => $Ticket, Tickets => $Tickets || undef);
-$Attachments ||=  $m->comp('/Ticket/Elements/FindAttachments', Ticket => $Ticket, Tickets => $Tickets || undef);
+$Transactions ||= $m->comp('/Ticket/Elements/FindTransactions', Ticket => $Ticket);
+$Attachments  ||= $Ticket->Attachments;
 $AttachmentContent ||= $Ticket->TextAttachments;
 </%INIT>
 <%ARGS>
 $URIFile => RT->Config->Get('WebPath')."/Ticket/Display.html"
 $Ticket => undef
-$Tickets => undef
 $Transactions => undef
 $Attachments => undef
 $AttachmentContent => undef
diff --git a/share/html/m/ticket/show b/share/html/m/ticket/show
index 4c3b2c4..5cb9439 100644
--- a/share/html/m/ticket/show
+++ b/share/html/m/ticket/show
@@ -152,7 +152,7 @@ if (@Actions) {
 
 # If we haven't been passed in an Attachments object (through the precaching mechanism)
 # then we need to find one
-my $Attachments = $m->comp('/Ticket/Elements/FindAttachments', Ticket => $Ticket);
+my $Attachments = $Ticket->Attachments;
 
 my %documents;
 while ( my $attach = $Attachments->Next() ) {

commit 03b03897c2036a2d2b9b11b06761f147a85507fa
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Nov 16 17:02:39 2012 -0800

    Respect the OldestTransactionsFirst preference under Self Service …
    
    … by using the standard component for finding ticket transactions to
    display in history.

diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 0845ff5..c6d4e38 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -214,7 +214,7 @@ if ( defined ($id[0]) && $id[0] eq 'new' ) {
         RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."SelfService/Display.html?id=". $Ticket->id."&results=".$key);
     }
 
-    my $Transactions = $Ticket->Transactions;
+    my $Transactions = $m->comp("/Ticket/Elements/FindTransactions", Ticket => $Ticket);
     my $attachments = $Ticket->Attachments;
 
     my $LinkBasicsTitle = $Ticket->CurrentUserHasRight('ModifyTicket')

commit bb8310b8ac6b4c717461fab71df66ca6473aa818
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 15:29:01 2012 -0800

    Parity with the latest versions of ShowHistory and ShowTransaction
    
    The generic components drifted slightly from the ticket specific
    components, but now they're the same functionality-wise.

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 2fe99f8..30958ea 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -48,30 +48,36 @@
 <div class="history <% lc $record_type %>">
 <%perl>
 if ( $ShowDisplayModes or $ShowTitle ) {
+    my $title = $ShowTitle
+                    ? loc('History')
+                    : ' ';
+
     my $titleright = '';
     if ( $ShowDisplayModes ) {
         $Page ||= $m->request_path;
-        if ( $ShowHeaders ) {
-            $titleright .=
-                '<a href="'. RT->Config->Get('WebPath') . $Page
-                . '?id='. $Object->id. '">'
-                . loc("Brief headers") .'</a>'
-                . ' — '
-                . '<span class="selected">'. loc("Full headers") .'</span>';
-        }
-        else {
-            $titleright .= '<span class="selected">'. loc("Brief headers") .'</span>'
-                .' — '
-                .'<a href="'. RT->Config->Get('WebPath') . $Page
-                . '?ShowHeaders=1&id='. $Object->id. '">'
-                . loc("Full headers") .'</a>';
+        $Page = RT->Config->Get("WebPath") . $Page;
+
+        my $open_all  = $m->interp->apply_escapes( loc("Show all quoted text"), 'j' );
+        my $open_html = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
+        my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'j' );
+        $titleright .=    '<a href="#" data-direction="open" '
+                        . qq{onclick="return toggle_all_folds(this, $open_all, $close_all);"}
+                        . ">$open_html</a> — ";
+
+        if ($ShowHeaders) {
+            $titleright .= qq{<a href="$Page?id=} .
+                           $Object->id.qq{">} .
+                           loc("Show brief headers") .
+                           qq{</a>};
+        } else {
+            $titleright .= qq{<a href="$Page?ShowHeaders=1;id=} .
+                           $Object->id.qq{">} .
+                           loc("Show full headers") .
+                           qq{</a>};
         }
     }
 </%perl>
-<& /Widgets/TitleBoxStart,
-    title => $ShowTitle ? loc('History') : ' ',
-    titleright_raw => $titleright,
-&>
+<& /Widgets/TitleBoxStart, title => $title, titleright_raw => $titleright &>
 % }
 
 <div id="<% lc $record_type %>-<% $Object->id %>-history" class="history-container">
@@ -94,16 +100,15 @@ while ( my $Transaction = $Transactions->Next ) {
         $IsLastTransaction = 1 if $i == 1;
     }
 
-    #Args is first because we're clobbering the "Attachments" parameter 
-    $m->comp( '/Elements/ShowTransaction',
+    # ARGS is first because we're clobbering the "Attachments" parameter
+    $m->comp( 'ShowTransaction',
         %ARGS,
-
-        Ticket            => $Object,
+        Object            => $Object,
         Transaction       => $Transaction,
         ShowHeaders       => $ShowHeaders,
         RowNum            => $i,
-        Attachments       => $attachments{ $Transaction->id } || [],
-        AttachmentContent => \%attachment_content,
+        Attachments       => $trans_attachments->{$Transaction->id} || {},
+        AttachmentContent => $trans_content,
         LastTransaction   => $IsLastTransaction
     );
 
@@ -121,8 +126,6 @@ while ( my $Transaction = $Transactions->Next ) {
 % }
 </div>
 <%INIT>
-my $Transactions = $Object->Transactions;
-
 my $OldestFirst = RT->Config->Get(
     OldestTransactionsFirst => $session{'CurrentUser'}
 );
@@ -132,11 +135,17 @@ $Transactions->OrderByCols(
     { FIELD => 'id', ORDER => $SortOrder },
 );
 
-my %attachments;
-push @{ $attachments{ $_->TransactionId } ||= [] }, $_
-    foreach @{ $Attachments->ItemsArrayRef };
-my %attachment_content = map { $_->id => $_ }
-    @{ $AttachmentContent->ItemsArrayRef };
+my $trans_content = {};
+my $trans_attachments = {};
+
+for my $content (@{$AttachmentContent->ItemsArrayRef()}) {
+    $trans_content->{$content->TransactionId}->{$content->Id} = $content;
+}
+
+for my $attachment (@{$Attachments->ItemsArrayRef()}) {
+    my $tmp = $trans_attachments->{ $attachment->TransactionId } ||= {};
+    push @{ $tmp->{ $attachment->Parent || 0 } ||= [] }, $attachment;
+}
 
 {
     my %tmp = (
@@ -162,6 +171,7 @@ my $record_type = $Object->RecordType;
 </%INIT>
 <%ARGS>
 $Object
+$Transactions      => $Object->Transactions
 $Attachments       => $Object->Attachments( WithHeaders => 1 )
 $AttachmentContent => $Object->TextAttachments
 
diff --git a/share/html/Elements/ShowTransaction b/share/html/Elements/ShowTransaction
index adc6539..4315012 100644
--- a/share/html/Elements/ShowTransaction
+++ b/share/html/Elements/ShowTransaction
@@ -46,7 +46,6 @@
 %# 
 %# END BPS TAGGED BLOCK }}}
 <div class="<% join ' ', @classes %>">
-<div class="<% $record_type %>-transaction">
   <div class="metadata">
     <span class="type">
       <a name="txn-<% $Transaction->id %>" \
@@ -82,8 +81,6 @@ $m->comp(
 ) if $ShowBody;
 </%PERL>
   </div>
-
-</div>
 </div>
 
 <%ARGS>
@@ -111,10 +108,19 @@ $EmailRecordPath => undef
 </%ONCE>
 <%INIT>
 my $record_type = $Object->RecordType;
+my $type_class  = $Object->ClassifyTransaction( $Transaction );
+
+$m->callback(
+    CallbackName => 'MassageTypeClass',
+    Transaction  => $Transaction,
+    TypeClassRef => \$type_class,
+    ARGSRef      => \%ARGS,
+);
 
 my @classes = (
-    $record_type . '-transaction',
-    $Object->ClassifyTransaction( $Transaction ),
+    "transaction",
+    "$record_type-transaction",
+    $type_class,
     ($RowNum % 2 ? 'odd' : 'even')
 );
 
@@ -134,13 +140,7 @@ $time = loc('[quant,_1,min,min]', $Transaction->TimeTaken)
 if ( $ShowBody && !$Attachments ) {
     $ARGS{'Attachments'} = $Attachments = {};
 
-    my $attachments = $Transaction->Attachments;
-    $attachments->Columns( qw(
-        Id TransactionId Parent MessageId
-        Subject Filename Headers 
-        ContentEncoding ContentType
-        Creator Created
-    ) );
+    my $attachments = $Transaction->Attachments( WithHeaders => 1 );
     push @{ $Attachments->{ $_->Parent || 0 } ||= [] }, $_
         foreach @{ $attachments->ItemsArrayRef };
 }
@@ -155,17 +155,18 @@ if ( $ShowActions ) {
             path   => $EmailRecordPath
                 .'?id='. $Object->id
                 .'&Transaction='. $Transaction->id
-                .'&Attachment='. ( $Attachments->[0] && $Attachments->[0]->id ),
+                .'&Attachment='. ( $Attachments->{0}[0] && $Attachments->{0}[0]->id ),
         } if $EmailRecordPath;
 
         $ShowBody = 0;
     }
 
     # If the transaction has anything attached to it at all
-    elsif ( @$Attachments ) {
+    elsif ( %$Attachments ) {
         my %has_right = map {
             $_ => RT::ACE->CanonicalizeRightName( $_ . $record_type )
         } qw(Modify CommentOn ReplyTo);
+        $has_right{'Forward'} = RT::ACE->CanonicalizeRightName('ForwardMessage');
 
         my $can_modify = $has_right{'Modify'}
             && $Object->CurrentUserHasRight( $has_right{'Modify'} );
@@ -176,6 +177,7 @@ if ( $ShowActions ) {
             )
         ) {
             push @actions, {
+                class  => "reply-link",
                 title  => loc('Reply'),
                 path   => $UpdatePath
                     .'?id='. $Object->id
@@ -190,6 +192,7 @@ if ( $ShowActions ) {
             )
         ) {
             push @actions, {
+                class  => "comment-link",
                 title  => loc('Comment'),
                 path   => $UpdatePath
                     .'?id='. $Object->id
@@ -198,8 +201,11 @@ if ( $ShowActions ) {
                 ,
             };
         }
-        if ( $ForwardPath && $Object->CurrentUserHasRight('ForwardMessage') ) {
+        if ( $ForwardPath && $has_right{'Forward'}
+            && $Object->CurrentUserHasRight( $has_right{'Forward'} )
+        ) {
             push @actions, {
+                class  => "forward-link",
                 title  => loc('Forward'),
                 path   => $ForwardPath
                     .'?id='. $Object->id
@@ -212,6 +218,7 @@ if ( $ShowActions ) {
             && RT->Config->Get('GnuPG')->{'AllowEncryptDataInDB'}
         ) {
             push @actions, {
+                class  => "gpg-link",
                 title  => loc('Encrypt/Decrypt'),
                 path   => $EncryptionPath
                     .'?id='. $Transaction->id
@@ -245,6 +252,10 @@ if ( @actions ) {
                 ? ' target="'. $i->apply_escapes( $a->{'target'}, 'h' ) .'"'
                 : ''
             )
+            . ($a->{'class'}
+                ? ' class="'. $i->apply_escapes( $a->{'class'}, 'h' ) .'"'
+                : ''
+            )
             .'>'. $i->apply_escapes( $a->{'title'}, 'h' ) .'</a>'
         ;
     }

commit 8274e2b6b424b20f98f580884e227ee40a0f562a
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 15:32:46 2012 -0800

    Use relative links in history rather than guessing the current page
    
    History display mode toggles and action links are now always relative to
    the page history is being displayed on.  This avoids incorrect links
    when displaying history AJAX loaded via /Helpers/TicketHistory.

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 30958ea..c66d509 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -54,9 +54,6 @@ if ( $ShowDisplayModes or $ShowTitle ) {
 
     my $titleright = '';
     if ( $ShowDisplayModes ) {
-        $Page ||= $m->request_path;
-        $Page = RT->Config->Get("WebPath") . $Page;
-
         my $open_all  = $m->interp->apply_escapes( loc("Show all quoted text"), 'j' );
         my $open_html = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
         my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'j' );
@@ -65,12 +62,12 @@ if ( $ShowDisplayModes or $ShowTitle ) {
                         . ">$open_html</a> — ";
 
         if ($ShowHeaders) {
-            $titleright .= qq{<a href="$Page?id=} .
+            $titleright .= qq{<a href="?id=} .
                            $Object->id.qq{">} .
                            loc("Show brief headers") .
                            qq{</a>};
         } else {
-            $titleright .= qq{<a href="$Page?ShowHeaders=1;id=} .
+            $titleright .= qq{<a href="?ShowHeaders=1;id=} .
                            $Object->id.qq{">} .
                            loc("Show full headers") .
                            qq{</a>};
@@ -157,13 +154,9 @@ for my $attachment (@{$Attachments->ItemsArrayRef()}) {
         EncryptionPath  => 'GnuPG.html',
     );
 
-    my $request_path = $m->request_path;
-    $request_path =~ s/[^\/]+$//;
-
     while ( my ($arg, $path) = each %tmp ) {
         next if defined $ARGS{ $arg };
-
-        $ARGS{ $arg } = $request_path . $path;
+        $ARGS{ $arg } = $path;
     }
 }
 
@@ -175,7 +168,6 @@ $Transactions      => $Object->Transactions
 $Attachments       => $Object->Attachments( WithHeaders => 1 )
 $AttachmentContent => $Object->TextAttachments
 
-$Page              => undef 
 $ShowHeaders       => 0
 $ShowTitle         => 1
 $ShowDisplayModes  => 1

commit 1a3327ce992dd66cf6660c0454a6146cf3b39065
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 15:37:43 2012 -0800

    Force history display when toggling header mode
    
    The toggle now does what it says when using "click to show history"
    instead of being ignored.

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index c66d509..0917103 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -62,12 +62,12 @@ if ( $ShowDisplayModes or $ShowTitle ) {
                         . ">$open_html</a> — ";
 
         if ($ShowHeaders) {
-            $titleright .= qq{<a href="?id=} .
+            $titleright .= qq{<a href="?ForceShowHistory=1;id=} .
                            $Object->id.qq{">} .
                            loc("Show brief headers") .
                            qq{</a>};
         } else {
-            $titleright .= qq{<a href="?ShowHeaders=1;id=} .
+            $titleright .= qq{<a href="?ForceShowHistory=1;ShowHeaders=1;id=} .
                            $Object->id.qq{">} .
                            loc("Show full headers") .
                            qq{</a>};

commit 4ebc502d2797f4b629af2a642b7067175ab4cd6d
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 15:44:58 2012 -0800

    Add an AfterContent callback to ShowTransaction
    
    Provides opportunity to customize the end of transaction rendering,
    which was otherwise neglected by the existing callbacks.

diff --git a/share/html/Elements/ShowTransaction b/share/html/Elements/ShowTransaction
index 4315012..3abb34e 100644
--- a/share/html/Elements/ShowTransaction
+++ b/share/html/Elements/ShowTransaction
@@ -81,6 +81,7 @@ $m->comp(
 ) if $ShowBody;
 </%PERL>
   </div>
+% $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterContent' );
 </div>
 
 <%ARGS>

commit 775f4bd1fd8f78d18c9cce988b444bb04906d26e
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 15:52:34 2012 -0800

    Fully replace ticket-specific history display with generic components
    
    Moves several components out of /Ticket/Elements/ and into /Elements/,
    with only a few small tweaks needed.  All history display now goes
    through /Elements/ShowHistory, which no longer relies on any
    /Ticket/Elements/ components in its rendering call chain.
    
    Previously required arguments affecting link paths are removed as they
    are unnecessary with the generic component.
    
    Removes passing the long unsupported Collapsed argument as well, which
    the generic component doesn't even pretend to honor.

diff --git a/docs/UPGRADING-4.2 b/docs/UPGRADING-4.2
index e61f4f4..38e9f5b 100644
--- a/docs/UPGRADING-4.2
+++ b/docs/UPGRADING-4.2
@@ -8,3 +8,7 @@ UPGRADING FROM RT 4.0.0 and greater
 * The $AttachmentUnits option was removed in preference of always displaying in
   megabytes, kilobytes, or bytes as appropriate.  The option was incompletely
   implemented and controlled display in the attachments list but not history.
+
+* MakeClicky handlers added via a callback are now passed an "object" key in
+  the parameter hash instead of "ticket".  The object may be any RT::Record
+  subclass.
diff --git a/share/html/Admin/Groups/History.html b/share/html/Admin/Groups/History.html
index 9a8ca01..312a3cc 100644
--- a/share/html/Admin/Groups/History.html
+++ b/share/html/Admin/Groups/History.html
@@ -48,8 +48,8 @@
 <& /Admin/Elements/Header, Title => $title  &>
 <& /Elements/Tabs &>
 
-<& /Ticket/Elements/ShowHistory,
-    Ticket => $GroupObj,
+<& /Elements/ShowHistory,
+    Object => $GroupObj,
     ShowDisplayModes => 0,
 &>
 
diff --git a/share/html/Admin/Queues/History.html b/share/html/Admin/Queues/History.html
index 49a16fe..d7b0e3f 100644
--- a/share/html/Admin/Queues/History.html
+++ b/share/html/Admin/Queues/History.html
@@ -48,8 +48,8 @@
 <& /Admin/Elements/Header, Title => $title  &>
 <& /Elements/Tabs &>
 
-<& /Ticket/Elements/ShowHistory,
-    Ticket => $QueueObj,
+<& /Elements/ShowHistory,
+    Object => $QueueObj,
     ShowDisplayModes => 0,
 &>
 
diff --git a/share/html/Admin/Users/History.html b/share/html/Admin/Users/History.html
index fb51038..eff79db 100644
--- a/share/html/Admin/Users/History.html
+++ b/share/html/Admin/Users/History.html
@@ -48,8 +48,8 @@
 <& /Admin/Elements/Header, Title => $title  &>
 <& /Elements/Tabs &>
 
-<& /Ticket/Elements/ShowHistory,
-    Ticket => $UserObj,
+<& /Elements/ShowHistory,
+    Object => $UserObj,
     ShowDisplayModes => 0,
 &>
 
diff --git a/share/html/Approvals/Display.html b/share/html/Approvals/Display.html
index f989986..538d0fe 100644
--- a/share/html/Approvals/Display.html
+++ b/share/html/Approvals/Display.html
@@ -50,7 +50,7 @@
 <form method="post" action="<%RT->Config->Get('WebPath')%>/Approvals/index.html">
 
 <&| /Widgets/TitleBox, title => $title &>
-<& /Ticket/Elements/ShowHistory , Ticket => $Ticket, Collapsed => 0, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &>
+<& /Elements/ShowHistory , Object => $Ticket, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &>
 <hr />
 <& Elements/Approve, ticket => $Ticket, ShowApproving => 0 &>
 </&>
diff --git a/share/html/Approvals/Elements/Approve b/share/html/Approvals/Elements/Approve
index 4111680..b33757a 100644
--- a/share/html/Approvals/Elements/Approve
+++ b/share/html/Approvals/Elements/Approve
@@ -58,7 +58,7 @@
       <& /Ticket/Elements/ShowCustomFields, Ticket => $approving &>
 % }
 % if ($ShowHistory) {
-      <& /Ticket/Elements/ShowHistory, Ticket => $approving, Collapsed => 0, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &>
+      <& /Elements/ShowHistory, Object => $approving, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0 &>
 % }
     </div>
   </div>
diff --git a/share/html/Approvals/Elements/ShowDependency b/share/html/Approvals/Elements/ShowDependency
index 26dd070..35c9da7 100644
--- a/share/html/Approvals/Elements/ShowDependency
+++ b/share/html/Approvals/Elements/ShowDependency
@@ -74,7 +74,7 @@ while (my $link = $approving->Next()) {
 	$text .= $head;
     }
 
-    $text .= $m->scomp('/Ticket/Elements/ShowHistory' , Ticket => $link->BaseObj, Collapsed => ($type ne 'ticket'), ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0);
+    $text .= $m->scomp('/Elements/ShowHistory' , Object => $link->BaseObj, ShowTitle => 0, ShowHeaders => 0, ShowDisplayModes => 0, ShowTitleBarCommands => 0);
 
     $head .= $m->scomp('/Widgets/TitleBoxEnd');
     $text .= $m->scomp('/Widgets/TitleBoxEnd');
diff --git a/share/html/Ticket/Elements/FoldStanzaJS b/share/html/Elements/FoldStanzaJS
similarity index 100%
rename from share/html/Ticket/Elements/FoldStanzaJS
rename to share/html/Elements/FoldStanzaJS
diff --git a/share/html/Ticket/Elements/ShowGnuPGStatus b/share/html/Elements/ShowGnuPGStatus
similarity index 100%
rename from share/html/Ticket/Elements/ShowGnuPGStatus
rename to share/html/Elements/ShowGnuPGStatus
diff --git a/share/html/Ticket/Elements/ShowMessageHeaders b/share/html/Elements/ShowMessageHeaders
similarity index 97%
rename from share/html/Ticket/Elements/ShowMessageHeaders
rename to share/html/Elements/ShowMessageHeaders
index 5a91668..04b5f9d 100644
--- a/share/html/Ticket/Elements/ShowMessageHeaders
+++ b/share/html/Elements/ShowMessageHeaders
@@ -75,9 +75,9 @@ unless ( $display_headers{'_all'} ) {
     @headers = grep $display_headers{ lc $_->{'Tag'} }, @headers;
 }
 
-my $ticket = $Message->TransactionObj->TicketObj;
+my $object = $Message->TransactionObj->Object;
 foreach my $f (@headers) {
-    $m->comp('/Elements/MakeClicky', content => \$f->{'Value'}, ticket => $ticket, %ARGS);
+    $m->comp('/Elements/MakeClicky', content => \$f->{'Value'}, object => $object, %ARGS);
 }
 
 $m->callback(
diff --git a/share/html/Ticket/Elements/ShowMessageStanza b/share/html/Elements/ShowMessageStanza
similarity index 98%
rename from share/html/Ticket/Elements/ShowMessageStanza
rename to share/html/Elements/ShowMessageStanza
index 8a85443..99ba556 100644
--- a/share/html/Ticket/Elements/ShowMessageStanza
+++ b/share/html/Elements/ShowMessageStanza
@@ -52,7 +52,7 @@ my $plain_text_mono
     = RT->Config->Get( 'PlainTextMono', $session{'CurrentUser'} );
 my $Depth = 0;
 
-my $ticket = $Transaction ? $Transaction->TicketObj : undef;
+my $object = $Transaction ? $Transaction->Object : undef;
 
 my $print_content = sub {
     my $ref = shift;
@@ -62,7 +62,7 @@ my $print_content = sub {
     if ( $ContentType eq 'text/plain' ) {
         $m->comp( '/Elements/MakeClicky',
                   content => $ref,
-                  ticket  => $ticket,
+                  object  => $object,
                   %ARGS
                 );
 
diff --git a/share/html/Elements/ShowTransaction b/share/html/Elements/ShowTransaction
index 3abb34e..677391e 100644
--- a/share/html/Elements/ShowTransaction
+++ b/share/html/Elements/ShowTransaction
@@ -75,7 +75,7 @@ if ( $Transaction->CustomFieldValues->Count ) {
     $m->comp('/Elements/ShowCustomFields', Object => $Transaction );
 }
 $m->comp(
-    '/Ticket/Elements/ShowTransactionAttachments',
+    'ShowTransactionAttachments',
     %ARGS,
     Parent => 0
 ) if $ShowBody;
diff --git a/share/html/Ticket/Elements/ShowTransactionAttachments b/share/html/Elements/ShowTransactionAttachments
similarity index 93%
rename from share/html/Ticket/Elements/ShowTransactionAttachments
rename to share/html/Elements/ShowTransactionAttachments
index fbb0507..f5498a6 100644
--- a/share/html/Ticket/Elements/ShowTransactionAttachments
+++ b/share/html/Elements/ShowTransactionAttachments
@@ -63,11 +63,11 @@ foreach my $message ( @{ $Attachments->{ $Parent || 0 } || [] } ) {
     if ( $message->ContentLength ) {
 </%PERL>
 <div class="downloadattachment">
-<a href="<% $AttachPath %>/<% $Transaction->Id %>/<% $message->Id %>/<% $name | u%>"><&|/l&>Download</&> <% length $name ? $name : loc('(untitled)') %></a>\
+<a href="<% $AttachmentPath %>/<% $Transaction->Id %>/<% $message->Id %>/<% $name | u%>"><&|/l&>Download</&> <% length $name ? $name : loc('(untitled)') %></a>\
 % if ( $DownloadableHeaders && ! length $name && $message->ContentType =~ /text/  ) {
- / <a href="<% $AttachPath %>/WithHeaders/<% $message->Id %>"><% loc('with headers') %></a>
+ / <a href="<% $AttachmentPath %>/WithHeaders/<% $message->Id %>"><% loc('with headers') %></a>
 % }
-% $m->callback(CallbackName => 'AfterDownloadLinks', ARGSRef => \%ARGS, Ticket => $Ticket, Transaction => $Transaction, Attachment => $message);
+% $m->callback(CallbackName => 'AfterDownloadLinks', ARGSRef => \%ARGS, Object => $Object, Transaction => $Transaction, Attachment => $message);
 <br />
 <span class="downloadcontenttype"><% $message->ContentType %> <% $message->FriendlyContentLength %></span>
 </div>
@@ -93,12 +93,12 @@ $m->comp(
 </div>
 % }
 <%ARGS>
-$Ticket => undef
-$Transaction => undef
+$Transaction
+$Object => $Transaction->Object
 $ShowHeaders => 0
 $DownloadableHeaders => 1
-$AttachPath => RT->Config->Get('WebPath')."/Ticket/Attachment"
-$Attachments => undef
+$AttachmentPath => undef
+$Attachments => {}
 $AttachmentContent => {}
 $Parent => 0
 $ParentObj => undef
@@ -191,7 +191,7 @@ my $render_attachment = sub {
                     '/Elements/MakeClicky',
                     content => \$content,
                     html    => 1,
-                    ticket  => $Ticket,
+                    object  => $Object,
                 );
 
                 require HTML::Quoted;
@@ -238,7 +238,7 @@ my $render_attachment = sub {
         my $efilename = $m->interp->apply_escapes( $filename, 'h' );
         $m->out(
             qq{<img alt="$efilename" title="$efilename"}
-            . ' src="'. $AttachPath .'/'. $Transaction->Id .'/'. $message->Id .'/'
+            . ' src="'. $AttachmentPath .'/'. $Transaction->Id .'/'. $message->Id .'/'
                 . $m->interp->apply_escapes( $filename, 'u', 'h' )
             . '" />'
         );
diff --git a/share/html/Helpers/TicketHistory b/share/html/Helpers/TicketHistory
index f89521f..081beca 100644
--- a/share/html/Helpers/TicketHistory
+++ b/share/html/Helpers/TicketHistory
@@ -55,9 +55,8 @@ $TicketObj->Load($id);
 my $attachments = $TicketObj->Attachments;
 my $attachment_content = $TicketObj->TextAttachments;
 
-$m->comp('/Ticket/Elements/ShowHistory',
-    Ticket => $TicketObj, 
-    Collapsed => $ARGS{'Collapsed'}, 
+$m->comp('/Elements/ShowHistory',
+    Object => $TicketObj,
     ShowHeaders => $ARGS{'ShowHeaders'},
     Attachments => $attachments,
     AttachmentContent => $attachment_content
diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index c6d4e38..6334923 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -74,14 +74,11 @@
 
 
 
-<& /Ticket/Elements/ShowHistory, 
-      Ticket => $Ticket, 
-      URIFile => "Display.html", 
+<& /Elements/ShowHistory,
+      Object => $Ticket,
       ShowHeaders => $ARGS{'ShowHeaders'},
       DownloadableHeaders => 0,
-      AttachPath => "Attachment", 
       Attachments => $attachments, 
-      UpdatePath => "Update.html" 
 &>
 
 
diff --git a/share/html/Ticket/Display.html b/share/html/Ticket/Display.html
index 4c40e87..06a99aa 100644
--- a/share/html/Ticket/Display.html
+++ b/share/html/Ticket/Display.html
@@ -70,10 +70,9 @@
         Ticket => $TicketObj,
     &>
 % } else {
-    <& /Ticket/Elements/ShowHistory ,
-          Ticket => $TicketObj,
+    <& /Elements/ShowHistory ,
+          Object => $TicketObj,
           Transactions => $transactions,
-          Collapsed => $ARGS{'Collapsed'},
           ShowHeaders => $ARGS{'ShowHeaders'},
           Attachments => $attachments,
           AttachmentContent => $attachment_content
@@ -91,7 +90,6 @@
 $id => undef
 $TicketObj => undef
 $ShowHeaders => 0
-$Collapsed => undef
 $ForceShowHistory => 0
 </%ARGS>
 
diff --git a/share/html/Ticket/Elements/ShowHistory b/share/html/Ticket/Elements/ShowHistory
deleted file mode 100644
index 6829f3e..0000000
--- a/share/html/Ticket/Elements/ShowHistory
+++ /dev/null
@@ -1,155 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<div class="history ticket">
-<%doc>
-#   This is (ab)used in Admin/(Users|Groups)/History.html and should probably
-#   be generalized at some point.
-</%doc>
-<%perl>
-if ($ShowDisplayModes or $ShowTitle) {
-    my $title = $ShowTitle
-                    ? loc('History')
-                    : ' ';
-
-    my $titleright = '';
-    if ($ShowDisplayModes) {
-        my $open_all  = $m->interp->apply_escapes( loc("Show all quoted text"), 'j' );
-        my $open_html = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
-        my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'j' );
-        $titleright .=    '<a href="#" data-direction="open" '
-                        . qq{onclick="return toggle_all_folds(this, $open_all, $close_all);"}
-                        . ">$open_html</a> — ";
-
-        if ($ShowHeaders) {
-            $titleright .= qq{<a href="$URIFile?id=} .
-                           $Ticket->id.qq{">} .
-                           loc("Show brief headers") .
-                           qq{</a>};
-        } else {
-            $titleright .= qq{<a href="$URIFile?ShowHeaders=1;id=} .
-                           $Ticket->id.qq{">} .
-                           loc("Show full headers") .
-                           qq{</a>};
-        }
-    }
-</%perl>
-<& /Widgets/TitleBoxStart, title => $title, titleright_raw => $titleright &>
-% }
-<div id="ticket-<% $Ticket->id %>-history" class="history-container">
-<%perl>
-my $trans_content = {};
-my $trans_attachments = {};
-
-for my $content (@{$AttachmentContent->ItemsArrayRef()}) {
-    $trans_content->{$content->TransactionId}->{$content->Id} = $content;
-}
-
-for my $attachment (@{$Attachments->ItemsArrayRef()}) {
-    my $tmp = $trans_attachments->{ $attachment->TransactionId } ||= {};
-    push @{ $tmp->{ $attachment->Parent || 0 } ||= [] }, $attachment;
-}
-
-my $i = 1;
-while ( my $Transaction = $Transactions->Next ) {
-    my $skip = 0;
-    $m->callback(
-        %ARGS,
-        Transaction   => $Transaction,
-        skip          => \$skip,
-        CallbackName  => 'SkipTransaction',
-    );
-    next if $skip;
-
-    my $IsLastTransaction = 0;
-    if ( RT->Config->Get( 'OldestTransactionsFirst', $session{'CurrentUser'} )){
-        $IsLastTransaction = $Transactions->IsLast;
-    } else {
-        $IsLastTransaction = 1 if $i == 1;
-    }
-
-    #Args is first because we're clobbering the "Attachments" parameter 
-    $m->comp( 'ShowTransaction',
-        %ARGS,
-        Ticket               => $Ticket,
-        Transaction          => $Transaction,
-        ShowHeaders          => $ShowHeaders,
-        RowNum               => $i,
-        Attachments          => $trans_attachments->{$Transaction->id} || {},
-        AttachmentContent    => $trans_content,
-        LastTransaction      => $IsLastTransaction
-    );
-
-    # manually flush the content buffer after each txn,
-    # so the user sees some update
-    $m->flush_buffer;
-
-    $i++;
-}
-
-</%perl>
-</div>
-% if ($ShowDisplayModes or $ShowTitle) {
-<& /Widgets/TitleBoxEnd &>
-% }
-</div>
-<%INIT>
-$Transactions ||= $m->comp('/Ticket/Elements/FindTransactions', Ticket => $Ticket);
-$Attachments  ||= $Ticket->Attachments;
-$AttachmentContent ||= $Ticket->TextAttachments;
-</%INIT>
-<%ARGS>
-$URIFile => RT->Config->Get('WebPath')."/Ticket/Display.html"
-$Ticket => undef
-$Transactions => undef
-$Attachments => undef
-$AttachmentContent => undef
-$ShowHeaders => undef
-$ShowTitle => 1
-$ShowDisplayModes => 1
-$WarnUnsigned => undef
-</%ARGS>
diff --git a/share/html/Ticket/Elements/ShowTransaction b/share/html/Ticket/Elements/ShowTransaction
deleted file mode 100644
index 07027d8..0000000
--- a/share/html/Ticket/Elements/ShowTransaction
+++ /dev/null
@@ -1,205 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<div class="transaction <% $type_class %> <%$type%> <% $RowNum % 2 ? 'odd' : 'even' %>">
-% $m->callback( titlebar_cmd => \$titlebar_commands, Transaction => $Transaction, %ARGS, CallbackName => 'ModifyDisplay' );
-
-<div class="transaction">
-% $m->callback( titlebar_cmd => \$titlebar_commands, Transaction => $Transaction, %ARGS, CallbackName => 'ModifyCommand' );
-  <div class="metadata">
-    <span class="type">
-      <a name="txn-<% $Transaction->Id %>" href="<% $DisplayPath %>#txn-<% $Transaction->Id %>">#</a>\
-      <% $LastTransaction ? '<a id="lasttrans" name="lasttrans"></a>' : ''|n %>
-    </span>
-% $m->callback( Transaction => $Transaction, %ARGS, CallbackName => 'AfterAnchor' );
-    <span class="date"><% $transdate|n %></span>
-% my $desc = $Transaction->BriefDescription;
-% $m->callback( text => \$desc, Transaction => $Transaction, %ARGS, CallbackName => 'ModifyDisplay' );
-    <span class="description">\
-<& /Elements/ShowUser, User => $Transaction->CreatorObj &> - <% $TicketString %> <% $desc %>\
-% $m->callback( Transaction => $Transaction, %ARGS, CallbackName => 'AfterDescription' );
-</span>
-% $m->callback( TimeTaken => \$TimeTaken, Transaction => $Transaction, %ARGS, CallbackName => 'ModifyTimeTaken' );
-    <span class="time-taken"><% $TimeTaken %></span>\
-    <span class="actions<% $titlebar_commands ? '': ' hidden'%>"><% $titlebar_commands |n %></span>
-  </div>
-    <div class="content">
-% if ( $type_class eq 'message' ) {
-      <& /Elements/ShowCustomFields, Object => $Transaction &>
-% }
-% $m->comp('ShowTransactionAttachments', %ARGS, Parent => 0) unless ($Collapsed ||!$ShowBody);
-    </div>
-</div>
-</div>
-<%ARGS>
-$Ticket => undef
-$Transaction => undef
-$ShowHeaders => 0
-$Collapsed => undef
-$ShowTitleBarCommands => 1
-$RowNum => 1
-$DisplayPath => RT->Config->Get('WebPath')."/Ticket/Display.html?id=".$Ticket->id
-$AttachPath => RT->Config->Get('WebPath')."/Ticket/Attachment"
-$UpdatePath => RT->Config->Get('WebPath')."/Ticket/Update.html"
-$ForwardPath => RT->Config->Get('WebPath')."/Ticket/Forward.html"
-$EncryptionPath => RT->Config->Get('WebPath')."/Ticket/GnuPG.html"
-$EmailRecordPath => RT->Config->Get('WebPath')."/Ticket/ShowEmailRecord.html"
-$Attachments => undef
-$AttachmentContent => undef
-$ShowBody => 1
-$LastTransaction => 0
-$WarnUnsigned => undef
-</%ARGS>
-<%INIT>
-
-my $transdate = $Transaction->CreatedAsString();
-$transdate =~ s/\s/ /g;
-
-my ($type, $field) = ($Transaction->Type, $Transaction->Field || '');
-my $type_class  = $Ticket->ClassifyTransaction($Transaction);
-
-$m->callback(
-    CallbackName => 'MassageTypeClass',
-    Transaction  => $Transaction,
-    TypeClassRef => \$type_class,
-    ARGSRef      => \%ARGS,
-);
-
-my $TicketString = '';
-if ( $Ticket->Id != $Transaction->Ticket ) {
-    $TicketString = loc("Ticket #[_1]:", $Transaction->Ticket) .' ';
-}
-
-my $TimeTaken = '';
-$TimeTaken = $Transaction->TimeTaken . " min"
-    if $Transaction->TimeTaken;
-
-unless ($Attachments) { 
-    my $attachments = $Transaction->Attachments;
-    $attachments->Columns( qw( Id Filename ContentType Headers Subject Parent ContentEncoding ContentType TransactionId) );
-    $ARGS{'Attachments'} = $Attachments = {};
-
-    push @{ $Attachments->{ $_->Parent || 0 } ||= [] }, $_
-        foreach $attachments->ItemsArrayRef();
-}
-my $titlebar_commands = '';
-
-$m->callback(
-    CallbackName   => 'MassageAttachments',
-    Transaction    => $Transaction,
-    AttachmentsRef => \$Attachments,
-    ARGSRef        => \%ARGS,
-);
-
-if ( $type =~ /EmailRecord$/ ) {
-
-    $titlebar_commands .=
-        "[<a target=\"_blank\" href=\"$EmailRecordPath?id="
-      . $Transaction->Ticket
-      . "&Transaction="
-      . $Transaction->Id
-      . "&Attachment="
-      . ( $Attachments->{0}[0] && $Attachments->{0}[0]->id )
-      . '">' . loc('Show') . "</a>] ";
-    $ShowBody = 0;
-}
-
-
-# If the transaction has anything attached to it at all
-else {
-
-    if ( keys %$Attachments && $ShowTitleBarCommands ) {
-        my $ticket = $Transaction->TicketObj;
-        my $can_modify = $ticket->CurrentUserHasRight('ModifyTicket');
-        if ( $can_modify || $ticket->CurrentUserHasRight('ReplyToTicket') ) {
-            $titlebar_commands .=
-                "[<a href=\"" . $UpdatePath
-              . "?id=" . $Transaction->Ticket
-              . "&QuoteTransaction=" . $Transaction->Id
-              . "&Action=Respond\" "
-              . "class=\"reply-link\""
-              . ">"
-              . loc('Reply')
-              . "</a>] ";
-        }
-        if ( $can_modify || $ticket->CurrentUserHasRight('CommentOnTicket') ) {
-            $titlebar_commands .=
-                "[<a href=\"" . $UpdatePath
-              . "?id=" . $Transaction->Ticket
-              . "&QuoteTransaction=" . $Transaction->Id
-              . "&Action=Comment\" "
-              . "class=\"comment-link\""
-              . ">"
-              . loc('Comment')
-              . "</a>]";
-        }
-        if ( $ticket->CurrentUserHasRight('ForwardMessage') ) {
-            $titlebar_commands .=
-                "[<a href=\"" . $ForwardPath
-              . "?id=" . $Transaction->Ticket
-              . "&QuoteTransaction=" . $Transaction->Id ."\" "
-              . "class=\"forward-link\""
-              . ">"
-              . loc('Forward')
-              . "</a>]";
-        }
-        if ( $can_modify
-            && RT->Config->Get('GnuPG')->{'Enable'}
-            && RT->Config->Get('GnuPG')->{'AllowEncryptDataInDB'}
-            && $ticket->CurrentUserHasRight('ForwardMessage')
-        ) {
-            $titlebar_commands .=
-                "[<a href=\"" . $EncryptionPath
-              . "?id=" . $Transaction->Id ."\" "
-              . "class=\"gpg-link\""
-              . ">"
-              . loc('Encrypt/Decrypt')
-              . "</a>]";
-        }
-    }
-}
-</%INIT>
diff --git a/share/html/Ticket/History.html b/share/html/Ticket/History.html
index 4475e7a..16341b8 100644
--- a/share/html/Ticket/History.html
+++ b/share/html/Ticket/History.html
@@ -53,10 +53,9 @@
 
 <br />
       
-<& /Ticket/Elements/ShowHistory , 
-    Ticket => $Ticket, 
+<& /Elements/ShowHistory,
+    Object => $Ticket,
     ShowHeaders => $ARGS{'ShowHeaders'}, 
-    URIFile => 'History.html', 
     Attachments => $attachments,
     AttachmentContent => $attachment_content
     &> 

commit 920a2bd5e0ea397f0dffbbadd9066081fe6a3fad
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 16:01:40 2012 -0800

    Fix rendering regression introduced by removing nested .transaction divs
    
    Previously two divs of class "transaction" were nested for no apparent
    reason.  Simply double the padding and border applied to .transaction to
    account for the removed duplicate div.

diff --git a/share/html/NoAuth/css/aileron/ticket.css b/share/html/NoAuth/css/aileron/ticket.css
index 1c22066..471a492 100644
--- a/share/html/NoAuth/css/aileron/ticket.css
+++ b/share/html/NoAuth/css/aileron/ticket.css
@@ -46,8 +46,8 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 div.history-container div.transaction {
- border-top: 1px solid #ccc;
- padding-bottom: 0.25em;
+    border-top: 2px solid #ccc;
+    padding-bottom: 0.5em;
     position: relative; /* gives us a container for position: absolute */
 }
 
diff --git a/share/html/NoAuth/css/ballard/ticket.css b/share/html/NoAuth/css/ballard/ticket.css
index 536fca8..cc07bd2 100644
--- a/share/html/NoAuth/css/ballard/ticket.css
+++ b/share/html/NoAuth/css/ballard/ticket.css
@@ -46,8 +46,8 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 div.history-container div.transaction {
- border-top: 1px solid #ccc;
- padding-bottom: 0.25em;
+    border-top: 2px solid #ccc;
+    padding-bottom: 0.5em;
     position: relative; /* gives us a container for position: absolute */
 }
 
diff --git a/share/html/NoAuth/css/web2/ticket.css b/share/html/NoAuth/css/web2/ticket.css
index 12abf41..c7f765c 100644
--- a/share/html/NoAuth/css/web2/ticket.css
+++ b/share/html/NoAuth/css/web2/ticket.css
@@ -46,8 +46,8 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 div.history-container div.transaction {
- border-top: 1px solid #ccc;
- padding-bottom: 0.25em;
+    border-top: 2px solid #ccc;
+    padding-bottom: 0.5em;
     position: relative; /* gives us a container for position: absolute */
 }
 

commit 0d8eacf36bd8428ff1b096bb79f3b10d085e32cd
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 16:13:43 2012 -0800

    Replace Articles' history component with the new generic one

diff --git a/share/html/Articles/Article/Elements/ShowHistory b/share/html/Articles/Article/Elements/ShowHistory
deleted file mode 100644
index 6ca74bf..0000000
--- a/share/html/Articles/Article/Elements/ShowHistory
+++ /dev/null
@@ -1,76 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<table width="100%" cellspacing="0" cellpadding="2">
-% my $i;
-% while (my $transaction = $transactions->Next) {
-<tr class="<% ($i++)%2 ? 'oddline' : 'evenline'%>" >
-<td width="20%"><small><%$transaction->CreatedObj->AsString%></small></td>
-<td><%$transaction->CreatorObj->Name%></td>
-<td><%$transaction->Description%></td>
-</tr>
-% }
-</table>
-<%init>
-
-my $article = RT::Article->new($session{'CurrentUser'}); 
-
-$article->Load($id);
-unless ($article->Id) {
-    $m->comp("/Elements/Error", Why => loc("Article not found"));
-}
-
-unless ($article->ClassObj->CurrentUserHasRight('ShowArticle')) {
-    $m->comp("/Elements/Error", Why => loc("Permission Denied"));
-}
-
-my $transactions = $article->Transactions();
-
-</%init>
-<%args>
-$id => undef
-</%args>
diff --git a/share/html/Articles/Article/History.html b/share/html/Articles/Article/History.html
index 1c2873f..c0d7696 100644
--- a/share/html/Articles/Article/History.html
+++ b/share/html/Articles/Article/History.html
@@ -45,13 +45,24 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /Elements/Header, Title => $title &>
+<& /Elements/Header, Title => loc('History for article #[_1]', $id) &>
 <& /Elements/Tabs &>
-<& Elements/ShowHistory, id => $id &>  
+<& /Elements/ShowHistory,
+    Object => $article,
+    ShowHeaders => 0,
+    ShowDisplayModes => 0,
+    ShowTitleBarCommands => 0,
+    &>
 <%init>
-my $title =loc('History for article #[_1]',$id);
-</%init>
+my $article = RT::Article->new($session{'CurrentUser'});
+$article->Load($id);
+
+Abort(loc("Article #[_1] not found", $id))
+    unless $article->Id;
 
+Abort(loc("Permission Denied"))
+    unless $article->CurrentUserHasRight('ShowArticle');
+</%init>
 <%args>
 $id => undef
 </%args>

commit a52805c7e8fdf48cda95da92c6d655b39cbe3615
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 16:44:03 2012 -0800

    Remove unused FindAttachments component

diff --git a/share/html/Ticket/Elements/FindAttachments b/share/html/Ticket/Elements/FindAttachments
deleted file mode 100644
index 9b997f8..0000000
--- a/share/html/Ticket/Elements/FindAttachments
+++ /dev/null
@@ -1,54 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%INIT>
-# DEPRECATED: use the method below directly
-return $Ticket->Attachments;
-</%INIT>
-<%ARGS>
-$Ticket
-</%ARGS>

commit c464ce54489318b132bee8711c3b96b442b80bd8
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 16:44:23 2012 -0800

    Replace /Ticket/Elements/FindTransactions with RT::Record->SortedTransactions

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 2e9cf9d..59fa338 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1563,6 +1563,25 @@ sub Transactions {
     return $transactions;
 }
 
+=head2 SortedTransactions
+
+Returns the result of L</Transactions> ordered per the
+I<OldestTransactionsFirst> preference/option.
+
+=cut
+
+sub SortedTransactions {
+    my $self  = shift;
+    my $txns  = $self->Transactions;
+    my $order = RT->Config->Get("OldestTransactionsFirst", $self->CurrentUser)
+        ? 'ASC' : 'DESC';
+    $txns->OrderByCols(
+        { FIELD => 'Created',   ORDER => $order },
+        { FIELD => 'id',        ORDER => $order },
+    );
+    return $txns;
+}
+
 our %TRANSACTION_CLASSIFICATION = (
     Create     => 'message',
     Correspond => 'message',
diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 0917103..3c971fa 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -91,7 +91,7 @@ while ( my $Transaction = $Transactions->Next ) {
     next if $skip;
 
     my $IsLastTransaction = 0;
-    if ( $OldestFirst ) {
+    if ( RT->Config->Get("OldestTransactionsFirst" => $session{'CurrentUser'}) ) {
         $IsLastTransaction = $Transactions->IsLast;
     } else {
         $IsLastTransaction = 1 if $i == 1;
@@ -123,15 +123,6 @@ while ( my $Transaction = $Transactions->Next ) {
 % }
 </div>
 <%INIT>
-my $OldestFirst = RT->Config->Get(
-    OldestTransactionsFirst => $session{'CurrentUser'}
-);
-my $SortOrder = $OldestFirst? 'ASC': 'DESC';
-$Transactions->OrderByCols(
-    { FIELD => 'Created', ORDER => $SortOrder },
-    { FIELD => 'id', ORDER => $SortOrder },
-);
-
 my $trans_content = {};
 my $trans_attachments = {};
 
@@ -164,7 +155,7 @@ my $record_type = $Object->RecordType;
 </%INIT>
 <%ARGS>
 $Object
-$Transactions      => $Object->Transactions
+$Transactions      => $Object->SortedTransactions
 $Attachments       => $Object->Attachments( WithHeaders => 1 )
 $AttachmentContent => $Object->TextAttachments
 
diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 6334923..6ab7c8f 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -78,7 +78,6 @@
       Object => $Ticket,
       ShowHeaders => $ARGS{'ShowHeaders'},
       DownloadableHeaders => 0,
-      Attachments => $attachments, 
 &>
 
 
@@ -211,9 +210,6 @@ if ( defined ($id[0]) && $id[0] eq 'new' ) {
         RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."SelfService/Display.html?id=". $Ticket->id."&results=".$key);
     }
 
-    my $Transactions = $m->comp("/Ticket/Elements/FindTransactions", Ticket => $Ticket);
-    my $attachments = $Ticket->Attachments;
-
     my $LinkBasicsTitle = $Ticket->CurrentUserHasRight('ModifyTicket')
                           || $Ticket->CurrentUserHasRight('ReplyToTicket');
     my $title_box_link = RT->Config->Get('WebPath')."/SelfService/Update.html?id=".$Ticket->Id;
diff --git a/share/html/Ticket/Display.html b/share/html/Ticket/Display.html
index 06a99aa..9d86e4a 100644
--- a/share/html/Ticket/Display.html
+++ b/share/html/Ticket/Display.html
@@ -213,8 +213,7 @@ MaybeRedirectForResults(
     Arguments => { id => $TicketObj->id },
 );
 
-# Get the transactoins before the attachments, for great ACL justice
-my $transactions = $m->comp('Elements/FindTransactions', Ticket => $TicketObj);
+my $transactions = $TicketObj->SortedTransactions;
 my $attachments = $TicketObj->Attachments;
 my $attachment_content = $TicketObj->TextAttachments;
 
diff --git a/share/html/Ticket/Elements/FindTransactions b/share/html/Ticket/Elements/FindTransactions
deleted file mode 100644
index c0ae5ca..0000000
--- a/share/html/Ticket/Elements/FindTransactions
+++ /dev/null
@@ -1,62 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%INIT>
-my $Transactions = $Ticket->Transactions;
-my $OldestFirst = RT->Config->Get( 'OldestTransactionsFirst', $session{'CurrentUser'} );
-my $SortOrder = $OldestFirst? 'ASC': 'DESC';
-$Transactions->OrderByCols( { FIELD => 'Created',
-                              ORDER => $SortOrder },
-                            { FIELD => 'id',
-                              ORDER => $SortOrder },
-                          );
-$Transactions->Next(); $Transactions->GotoFirstItem(); # actually do the search
-return ($Transactions);
-</%INIT>
-<%ARGS>
-$Ticket
-</%ARGS>

commit e9d994b1093db7a6a559701d3c3d751181599517
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 26 16:46:46 2012 -0800

    Link the Last Updated timestamp to a specific transaction
    
    Removes the #lasttrans anchor, which wasn't guaranteed to be the
    transaction which most recently set Last Updated anyway.
    
    ShowHistory no longer checks OldestTransactionsFirst itself, removing an
    assumption about the ordering of $Transactions and completing the
    abstraction of SortedTransactions.
    
    Last Updated now correctly links to the latest transaction, even if the
    history isn't sorted by Created.

diff --git a/share/html/Elements/ShowHistory b/share/html/Elements/ShowHistory
index 3c971fa..4b9258f 100644
--- a/share/html/Elements/ShowHistory
+++ b/share/html/Elements/ShowHistory
@@ -90,13 +90,6 @@ while ( my $Transaction = $Transactions->Next ) {
     );
     next if $skip;
 
-    my $IsLastTransaction = 0;
-    if ( RT->Config->Get("OldestTransactionsFirst" => $session{'CurrentUser'}) ) {
-        $IsLastTransaction = $Transactions->IsLast;
-    } else {
-        $IsLastTransaction = 1 if $i == 1;
-    }
-
     # ARGS is first because we're clobbering the "Attachments" parameter
     $m->comp( 'ShowTransaction',
         %ARGS,
@@ -106,7 +99,6 @@ while ( my $Transaction = $Transactions->Next ) {
         RowNum            => $i,
         Attachments       => $trans_attachments->{$Transaction->id} || {},
         AttachmentContent => $trans_content,
-        LastTransaction   => $IsLastTransaction
     );
 
     # manually flush the content buffer after each txn,
diff --git a/share/html/Elements/ShowTransaction b/share/html/Elements/ShowTransaction
index 677391e..271d608 100644
--- a/share/html/Elements/ShowTransaction
+++ b/share/html/Elements/ShowTransaction
@@ -53,9 +53,6 @@
       href="<% $DisplayPath %>#txn-<% $Transaction->id %>" \
 % }
       >#</a>
-% if ( $LastTransaction ) {
-      <a id="lasttrans" name="lasttrans"></a>
-% }
     </span>
 % $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterAnchor' );
     <span class="date"><% $date |n %></span>
@@ -94,7 +91,6 @@ $AttachmentContent => undef
 $ShowBody => 1
 $ShowActions => 1
 $RowNum => 1
-$LastTransaction => 0
 
 $DisplayPath => undef
 $AttachmentPath => undef
diff --git a/share/html/NoAuth/css/aileron/ticket.css b/share/html/NoAuth/css/aileron/ticket.css
index 471a492..81c14b4 100644
--- a/share/html/NoAuth/css/aileron/ticket.css
+++ b/share/html/NoAuth/css/aileron/ticket.css
@@ -103,14 +103,6 @@ div.history-container span.type a {
  display: block;
 }
 
-.history-container a#lasttrans {
-    display: inline;
-    height: 0;
-    width: 0;
-    padding: 0;
-    margin: 0;
-}
-
 
 div.history-container span.date {
  width: 10em;
diff --git a/share/html/Ticket/Elements/ShowDates b/share/html/Ticket/Elements/ShowDates
index e731b92..9e68ab1 100644
--- a/share/html/Ticket/Elements/ShowDates
+++ b/share/html/Ticket/Elements/ShowDates
@@ -79,7 +79,7 @@
     <td class="label"><&|/l&>Updated</&>:</td>\
 % my $UpdatedString = $Ticket->LastUpdated ? loc("[_1] by [_2]", $Ticket->LastUpdatedAsString, $m->scomp('/Elements/ShowUser', User => $Ticket->LastUpdatedByObj)) : loc("Never");
 % if ($UpdatedLink) {
-    <td class="value"><a href="#lasttrans"><% $UpdatedString | n %></a></td>
+    <td class="value"><a href="<% $UpdatedLink %>"><% $UpdatedString | n %></a></td>
 % } else {
     <td class="value"><% $UpdatedString | n %></td>
 % }
@@ -90,3 +90,19 @@
 $Ticket => undef
 $UpdatedLink => 1
 </%ARGS>
+<%INIT>
+if ($UpdatedLink and $Ticket) {
+    my $txns = $Ticket->Transactions;
+    $txns->OrderByCols(
+        { FIELD => "Created",   ORDER => "DESC" },
+        { FIELD => "id",        ORDER => "DESC" },
+    );
+    $txns->RowsPerPage(1);
+
+    if (my $latest = $txns->First) {
+        $UpdatedLink = "#txn-" . $latest->id;
+    } else {
+        undef $UpdatedLink;
+    }
+}
+</%INIT>

commit 5a71507001924c500a8bad4e1fb040ea8b807190
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 27 11:53:20 2012 -0800

    Move history styles into the base theme
    
    All three themes use almost exactly the same history and ticket styling,
    with the biggest intended difference being aileron's rollup/rolldown
    arrow color.  Split the individual {aileron,web2,ballard}/ticket.css
    files into base/history.css and base/ticket.css.
    
    git detects the rename from ballard into base, which causes the diff to
    look a little funny.  In reality, aileron's ticket.css was copied into
    base's history.css and ticket.css, except for the single hunk that was
    left behind.
    
    Any selector containing .titlebox-title is made more specific by adding
    .titlebox.  This ensures the styles take effect despite the unrefactored
    generic titlebox styles living in theme-specific files (which are
    evaluated after the base styles).

diff --git a/share/html/NoAuth/css/aileron/ticket.css b/share/html/NoAuth/css/aileron/ticket.css
index 81c14b4..2f16ff0 100644
--- a/share/html/NoAuth/css/aileron/ticket.css
+++ b/share/html/NoAuth/css/aileron/ticket.css
@@ -45,163 +45,6 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-div.history-container div.transaction {
-    border-top: 2px solid #ccc;
-    padding-bottom: 0.5em;
-    position: relative; /* gives us a container for position: absolute */
-}
-
-div.history-container div.odd {
- background-color: #fff;
-}
-
-div.history-container {
-
- margin-top: 0.75em;
- border-left: 1px solid #ccc;
-
- border-right: 2px solid #999;
-  border-bottom: 2px solid #999;
-
-}
-
-.transaction div.metadata span.actions {
- position: absolute;
- top: 0;
- right: 0;
- padding: 0em;
- background: #ccc;
- text-align: right;
- border-left: 1px solid #999;
- border-bottom: 1px solid #999;
- color: #ccc;
- -moz-border-radius-bottomleft: 0.5em;
- -webkit-border-bottom-left-radius: 0.5em;
- white-space: nowrap;
-
- border-radius: 0 0 0 0.5em;
-}
-
-.transaction div.metadata  span.type {
- text-align: center;
- float: left;
- margin: 0.25em 0.70em 0.25em 0.25em;
- width: 1em;
- padding: 0;
- border-right: 1px solid #999;
- border-bottom: 1px solid #999;
- -moz-border-radius-bottomright: 0.25em;
- -webkit-border-bottom-right-radius: 0.25em;
-
- border-radius: 0 0 0.25em 0;
-
-}
-
-div.history-container span.type a {
- color: #fff;
- padding-top: 0.75em;
- display: block;
-}
-
-
-div.history-container span.date {
- width: 10em;
-}
-
-
-div.history-container span.description {
- margin-left: 1em;
- font-weight: bold;
-}
-
-div.history-container span.time-taken {
- margin-left: 1em;
-}
-
-div.history-container div.content {
- padding-right: 1em;
- padding-bottom: 0.7em;
- margin-left: 1.5em;
-}
-
-
-.transaction .messagebody {
- font-size: 1em;
- padding-left: 1em;
- margin-top: 0.5em;
- padding-top: 0.5em;
- border-top: 1px solid #ccc;
- /*overflow: auto; */
- min-height: 2.5em;
- /* To avoid overlapping of "downloadattachment" by messagebody */
- clear: left;
- word-wrap: break-word;
-}
-
-.transaction .messagebody img {
- max-width: 100%;
-}
-
-div.history-container div.downloadattachment {
-float: right;
-clear: both;
-font-size: 0.9em;
-text-align: right;
-background: #ddd;
-padding: 0.5em;
-margin-left: 1em;
-
-border: 1px solid #ccc;
-border-right: 2px solid #aaa;
-border-bottom: 2px solid #aaa;
-margin-top: 0.5em;
--moz-border-radius: 0.5em;
--webkit-border-radius: 0.5em;
- border-radius: 0.5em;
-}
-
-div.history-container div.downloadattachment .downloadcontenttype{
-color: #666;
-padding-right:0.25em;
-}
-
-
-div.history-container .message-header-key {
-  width: 7em;
-  font-weight: bold;
-  color: #666;
-}
-
-
-div.history-container .messagebody .messagebody{
- font-size: 1em;
- padding: 0;
- border: 0;
- margin: 0;
-}
-
-
-
-.transaction.basics .type { background: #b32; }
-.transaction.cfs .type { background: #b32; }
-.transaction.people .type { background: #48c; }
-.transaction.links .type { background: #316531; }
-.transaction.dates .type { background: #633063; }
-.transaction.message .type { background: #069; }
-.transaction.reminders .type { background: #369; }
-.transaction.other .type { background: #abc; }
-
-
-/* Color the titlebox tabs */
-.ticket-info-cfs .titlebox-title .left { background-color: #b32; color: #fff;}
-.ticket-info-basics .titlebox-title .left { background-color: #b32;  color: #fff;}
-.ticket-info-people .titlebox-title .left { background-color: #48c;  color: #fff;}
-.ticket-info-requestor .titlebox-title .left { white-space: nowrap; background-color: #48c;  color: #fff;}
-.ticket-info-links .titlebox-title .left { background-color: #316531;  color: #fff;}
-.ticket-info-reminders .titlebox-title .left { background-color: #369;  color: #fff;}
-.ticket-info-dates .titlebox-title .left { background-color: #633063;  color: #fff;}
-.ticket-info-attachments .titlebox-title .left { background-color: #993366;  color: #fff;}
-
 /* Use a lighter colored toggle arrow */
 .ticket-info-cfs .titlebox-title .widget a { background-position: center -7px; }
 .ticket-info-basics .titlebox-title .widget a { background-position: center -7px; }
@@ -211,80 +54,3 @@ div.history-container .messagebody .messagebody{
 .ticket-info-reminders .titlebox-title .widget a { background-position: center -7px; }
 .ticket-info-dates .titlebox-title .widget a { background-position: center -7px; }
 .ticket-info-attachments .titlebox-title .widget a { background-position: center -7px; }
-
-
-.ticket-summary .titlebox-title .left a, .ticket-summary .titlebox-title .left a:visited { color: #fff;}
-
-.unread-messages .titlebox , .unread-messages .titlebox-title .left {
-  border: 1px solid #99a;
-  border-right: 2px solid #aab;
-  border-bottom: 2px solid #aab;
-
-}
-
-
-.unread-messages .titlebox {
-  background-color: #dde;
-}
-
-.unread-messages .titlebox-title .left {
-  background-color: #cce;
-}
-
-.ticket-inactive {
-  text-decoration: line-through;
-  color: #666
-}
-
-table.ticket-summary td.boxcontainer:first-child {
-  width: 50%;
-}
-div.requestor-ticket-links {
-    text-align: left;
-    font-size: 0.8em;
-    padding-top: 0.25em;
-}
-
-.more-about-requestor-extra-field .label {
-    display: inline-block;
-    width: 8em;
-    font-weight: bold;
-    text-align: right;
-}
-
-.more-about-requestor-extra-field .value {
-    display: inline-block;
-}
-
-.ticket-info-requestor .more-about-requestor-extra,
-.ticket-info-requestor .comments-about-user,
-.ticket-info-requestor .more-about-requestor-tickets,
-.ticket-info-requestor .more-about-user-groups
-{
-    margin: 1em 0;
-}
-
-.ticket-info-requestor .comments-about-user .label,
-.ticket-info-requestor .more-about-user-groups .label,
-.ticket-info-requestor .more-about-requestor-tickets .label
-{
-    display: block;
-    font-weight: bold;
-    text-align: left;
-}
-
-.ticket-info-requestor .more-about-user-groups .value ul,
-.ticket-info-requestor .more-about-requestor-tickets ul
-{
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-
-/* textareas get to be bigger when we're in a table */
-tr.edit-custom-field.cftype-Text textarea,
-tr.edit-custom-field.cftype-Freeform input,
-tr.edit-custom-field.cftype-Wikitext textarea
-{
-    width: 100%;
-}
diff --git a/share/html/NoAuth/css/ballard/main.css b/share/html/NoAuth/css/ballard/main.css
index b184894..903579b 100644
--- a/share/html/NoAuth/css/ballard/main.css
+++ b/share/html/NoAuth/css/ballard/main.css
@@ -55,7 +55,6 @@
 @import "boxes.css";
 @import "ticket-lists.css";
 @import "ticket-search.css";
- at import "ticket.css";
 @import "misc.css";
 
 % $m->callback(CallbackName => 'End');
diff --git a/share/html/NoAuth/css/ballard/ticket.css b/share/html/NoAuth/css/base/history.css
similarity index 64%
rename from share/html/NoAuth/css/ballard/ticket.css
rename to share/html/NoAuth/css/base/history.css
index cc07bd2..1c12187 100644
--- a/share/html/NoAuth/css/ballard/ticket.css
+++ b/share/html/NoAuth/css/base/history.css
@@ -60,7 +60,7 @@ div.history-container {
  margin-top: 0.75em;
  border-left: 1px solid #ccc;
 
- border-right: 2px solid #999; 
+ border-right: 2px solid #999;
   border-bottom: 2px solid #999;
 
 }
@@ -77,8 +77,9 @@ div.history-container {
  color: #ccc;
  -moz-border-radius-bottomleft: 0.5em;
  -webkit-border-bottom-left-radius: 0.5em;
- border-bottom-left-radius: 0.5em;
  white-space: nowrap;
+
+ border-radius: 0 0 0 0.5em;
 }
 
 .transaction div.metadata  span.type {
@@ -86,17 +87,20 @@ div.history-container {
  float: left;
  margin: 0.25em 0.70em 0.25em 0.25em;
  width: 1em;
- height: 1.25em;
- padding: 0.75em 0 0 0;
+ padding: 0;
  border-right: 1px solid #999;
  border-bottom: 1px solid #999;
- -moz-border-radius: 0.25em;
+ -moz-border-radius-bottomright: 0.25em;
  -webkit-border-bottom-right-radius: 0.25em;
- border-bottom-right-radius: 0.25em;
+
+ border-radius: 0 0 0.25em 0;
+
 }
 
 div.history-container span.type a {
  color: #fff;
+ padding-top: 0.75em;
+ display: block;
 }
 
 
@@ -120,6 +124,7 @@ div.history-container div.content {
  margin-left: 1.5em;
 }
 
+
 .transaction .messagebody {
  font-size: 1em;
  padding-left: 1em;
@@ -152,8 +157,7 @@ border-bottom: 2px solid #aaa;
 margin-top: 0.5em;
 -moz-border-radius: 0.5em;
 -webkit-border-radius: 0.5em;
-border-radius: 0.5em;
-
+ border-radius: 0.5em;
 }
 
 div.history-container div.downloadattachment .downloadcontenttype{
@@ -171,7 +175,7 @@ div.history-container .message-header-key {
 
 div.history-container .messagebody .messagebody{
  font-size: 1em;
- padding: 0; 
+ padding: 0;
  border: 0;
  margin: 0;
 }
@@ -186,93 +190,3 @@ div.history-container .messagebody .messagebody{
 .transaction.message .type { background: #069; }
 .transaction.reminders .type { background: #369; }
 .transaction.other .type { background: #abc; }
-
-
-
-
-.ticket-info-cfs .titlebox-title .left { background-color: #b32; color: #fff;}
-.ticket-info-basics .titlebox-title .left { background-color: #b32;  color: #fff;}
-.ticket-info-people .titlebox-title .left { background-color: #48c;  color: #fff;}
-.ticket-info-requestor .titlebox-title .left { white-space: nowrap; background-color: #48c;  color: #fff;}
-.ticket-info-links .titlebox-title .left { background-color: #316531;  color: #fff;}
-.ticket-info-reminders .titlebox-title .left { background-color: #369;  color: #fff;}
-.ticket-info-dates .titlebox-title .left { background-color: #633063;  color: #fff;}
-.ticket-info-attachments .titlebox-title .left { background-color: #993366;  color: #fff;}
-
-
-.ticket-summary .titlebox-title .left a, .ticket-summary .titlebox-title .left a:visited { color: #fff;}
-
-.unread-messages .titlebox , .unread-messages .titlebox-title .left { 
-  border: 1px solid #99a;
-  border-right: 2px solid #aab;
-  border-bottom: 2px solid #aab;
-
-}
-
-
-.unread-messages .titlebox { 
-  background-color: #dde;
-}
-
-.unread-messages .titlebox-title .left { 
-  background-color: #cce;
-}
-
-.ticket-inactive {
-  text-decoration: line-through;
-  color: #666
-}
-
-table.ticket-summary td.boxcontainer:first-child {
-  width: 50%;
-}
-
-div.requestor-ticket-links {
-    text-align: left;
-    font-size: 0.8em;
-    padding-top: 0.25em;
-}
-
-.more-about-requestor-extra-field .label {
-    display: inline-block;
-    width: 8em;
-    font-weight: bold;
-    text-align: right;
-}
-
-.more-about-requestor-extra-field .value {
-    display: inline-block;
-}
-
-.ticket-info-requestor .more-about-requestor-extra,
-.ticket-info-requestor .comments-about-user,
-.ticket-info-requestor .more-about-requestor-tickets,
-.ticket-info-requestor .more-about-user-groups
-{
-    margin: 1em 0;
-}
-
-.ticket-info-requestor .comments-about-user .label,
-.ticket-info-requestor .more-about-user-groups .label,
-.ticket-info-requestor .more-about-requestor-tickets .label
-{
-    display: block;
-    font-weight: bold;
-    text-align: left;
-}
-
-.ticket-info-requestor .more-about-user-groups .value ul,
-.ticket-info-requestor .more-about-requestor-tickets ul
-{
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-
-/* textareas get to be bigger when we're in a table */
-tr.edit-custom-field.cftype-Text textarea,
-tr.edit-custom-field.cftype-Freeform input,
-tr.edit-custom-field.cftype-Wikitext textarea
-{
-    width: 100%;
-}
diff --git a/share/html/NoAuth/css/base/main.css b/share/html/NoAuth/css/base/main.css
index dac733d..4b844f4 100644
--- a/share/html/NoAuth/css/base/main.css
+++ b/share/html/NoAuth/css/base/main.css
@@ -69,6 +69,7 @@
 @import "tools.css";
 @import "login.css";
 @import "history-folding.css";
+ at import "history.css";
 
 % $m->callback(CallbackName => 'End');
 
diff --git a/share/html/NoAuth/css/base/ticket.css b/share/html/NoAuth/css/base/ticket.css
index f6d7233..4865653 100644
--- a/share/html/NoAuth/css/base/ticket.css
+++ b/share/html/NoAuth/css/base/ticket.css
@@ -144,3 +144,88 @@
 }
 
 
+/* Color the titlebox tabs */
+.ticket-info-cfs .titlebox .titlebox-title .left { background-color: #b32; color: #fff;}
+.ticket-info-basics .titlebox .titlebox-title .left { background-color: #b32;  color: #fff;}
+.ticket-info-people .titlebox .titlebox-title .left { background-color: #48c;  color: #fff;}
+.ticket-info-requestor .titlebox .titlebox-title .left { white-space: nowrap; background-color: #48c;  color: #fff;}
+.ticket-info-links .titlebox .titlebox-title .left { background-color: #316531;  color: #fff;}
+.ticket-info-reminders .titlebox .titlebox-title .left { background-color: #369;  color: #fff;}
+.ticket-info-dates .titlebox .titlebox-title .left { background-color: #633063;  color: #fff;}
+.ticket-info-attachments .titlebox .titlebox-title .left { background-color: #993366;  color: #fff;}
+
+.ticket-summary .titlebox .titlebox-title .left a, .ticket-summary .titlebox .titlebox-title .left a:visited { color: #fff;}
+
+.unread-messages .titlebox , .unread-messages .titlebox .titlebox-title .left {
+  border: 1px solid #99a;
+  border-right: 2px solid #aab;
+  border-bottom: 2px solid #aab;
+
+}
+
+
+.unread-messages .titlebox {
+  background-color: #dde;
+}
+
+.unread-messages .titlebox .titlebox-title .left {
+  background-color: #cce;
+}
+
+.ticket-inactive {
+  text-decoration: line-through;
+  color: #666
+}
+
+table.ticket-summary td.boxcontainer:first-child {
+  width: 50%;
+}
+div.requestor-ticket-links {
+    text-align: left;
+    font-size: 0.8em;
+    padding-top: 0.25em;
+}
+
+.more-about-requestor-extra-field .label {
+    display: inline-block;
+    width: 8em;
+    font-weight: bold;
+    text-align: right;
+}
+
+.more-about-requestor-extra-field .value {
+    display: inline-block;
+}
+
+.ticket-info-requestor .more-about-requestor-extra,
+.ticket-info-requestor .comments-about-user,
+.ticket-info-requestor .more-about-requestor-tickets,
+.ticket-info-requestor .more-about-user-groups
+{
+    margin: 1em 0;
+}
+
+.ticket-info-requestor .comments-about-user .label,
+.ticket-info-requestor .more-about-user-groups .label,
+.ticket-info-requestor .more-about-requestor-tickets .label
+{
+    display: block;
+    font-weight: bold;
+    text-align: left;
+}
+
+.ticket-info-requestor .more-about-user-groups .value ul,
+.ticket-info-requestor .more-about-requestor-tickets ul
+{
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+
+/* textareas get to be bigger when we're in a table */
+tr.edit-custom-field.cftype-Text textarea,
+tr.edit-custom-field.cftype-Freeform input,
+tr.edit-custom-field.cftype-Wikitext textarea
+{
+    width: 100%;
+}
diff --git a/share/html/NoAuth/css/web2/main.css b/share/html/NoAuth/css/web2/main.css
index b184894..903579b 100644
--- a/share/html/NoAuth/css/web2/main.css
+++ b/share/html/NoAuth/css/web2/main.css
@@ -55,7 +55,6 @@
 @import "boxes.css";
 @import "ticket-lists.css";
 @import "ticket-search.css";
- at import "ticket.css";
 @import "misc.css";
 
 % $m->callback(CallbackName => 'End');
diff --git a/share/html/NoAuth/css/web2/ticket.css b/share/html/NoAuth/css/web2/ticket.css
deleted file mode 100644
index c7f765c..0000000
--- a/share/html/NoAuth/css/web2/ticket.css
+++ /dev/null
@@ -1,280 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-div.history-container div.transaction {
-    border-top: 2px solid #ccc;
-    padding-bottom: 0.5em;
-    position: relative; /* gives us a container for position: absolute */
-}
-
-div.history-container div.odd {
- background-color: #fff;
-}
-
-div.history-container {
-
- margin-top: 0.75em;
- border-left: 1px solid #ccc;
-
- border-right: 2px solid #999; 
-  border-bottom: 2px solid #999;
-
-}
-
-.transaction div.metadata span.actions {
- position: absolute;
- top: 0;
- right: 0;
- padding: 0em;
- background: #ccc;
- text-align: right;
- border-left: 1px solid #999;
- border-bottom: 1px solid #999;
- color: #ccc;
- -moz-border-radius-bottomleft: 0.5em;
- -webkit-border-bottom-left-radius: 0.5em;
- white-space: nowrap;
-
- border-radius: 0 0 0 0.5em;
-}
-
-.transaction div.metadata  span.type {
- text-align: center;
- float: left;
- margin: 0.25em 0.70em 0.25em 0.25em;
- width: 1em;
- height: 1.25em;
- padding: 0.75em 0 0 0;
- border-right: 1px solid #999;
- border-bottom: 1px solid #999;
- -moz-border-radius-bottomright: 0.25em;
- -webkit-border-bottom-right-radius: 0.25em;
-
- border-radius: 0 0 0.25em 0;
-
-}
-
-.transaction div.metadata span.type a {
- color: #fff;
-}
-
-
-.transaction div.metadata span.date {
- width: 10em;
-}
-
-
-.transaction div.metadata span.description {
- margin-left: 1em;
- font-weight: bold;
-}
-
-.transaction div.metadata span.time-taken {
- margin-left: 1em;
-}
-
-.transaction div.content {
- padding-right: 1em;
- padding-bottom: 0.7em;
- margin-left: 1.5em;
-}
-
-.transaction .messagebody {
- font-size: 1em;
- padding-left: 1em;
- margin-top: 0.5em;
- padding-top: 0.5em;
- border-top: 1px solid #ccc;
- /*overflow: auto; */
- min-height: 2.5em;
- /* To avoid overlapping of "downloadattachment" by messagebody */
- clear: left;
- word-wrap: break-word;
-}
-
-.transaction .messagebody img {
- max-width: 100%;
-}
-
-div.history-container div.downloadattachment {
-float: right;
-clear: both;
-font-size: 0.9em;
-text-align: right;
-background: #ddd;
-padding: 0.5em;
-margin-left: 1em;
-
-border: 1px solid #ccc;
-border-right: 2px solid #aaa;
-border-bottom: 2px solid #aaa;
-margin-top: 0.5em;
--moz-border-radius: 0.5em;
--webkit-border-radius: 0.5em;
- border-radius: 0.5em;
-}
-
-div.history-container div.downloadattachment .downloadcontenttype {
-color: #666;
-padding-right:0.25em;
-}
-
-
-div.history-container .message-header-key {
-  width: 7em;
-  font-weight: bold;
-  color: #666;
-}
-
-
-div.history-container .messagebody .messagebody{
- font-size: 1em;
- padding: 0; 
- border: 0;
- margin: 0;
-}
-
-
-
-.transaction.basics .type { background: #b32; }
-.transaction.cfs .type { background: #b32; }
-.transaction.people .type { background: #48c; }
-.transaction.links .type { background: #316531; }
-.transaction.dates .type { background: #633063; }
-.transaction.message .type { background: #069; }
-.transaction.reminders .type { background: #369; }
-.transaction.other .type { background: #abc; }
-
-
-
-
-.ticket-info-cfs .titlebox-title .left { background-color: #b32; color: #fff;}
-.ticket-info-basics .titlebox-title .left { background-color: #b32;  color: #fff;}
-.ticket-info-people .titlebox-title .left { background-color: #48c;  color: #fff;}
-.ticket-info-requestor .titlebox-title .left { white-space: nowrap; background-color: #48c;  color: #fff;}
-.ticket-info-links .titlebox-title .left { background-color: #316531;  color: #fff;}
-.ticket-info-reminders .titlebox-title .left { background-color: #369;  color: #fff;}
-.ticket-info-dates .titlebox-title .left { background-color: #633063;  color: #fff;}
-.ticket-info-attachments .titlebox-title .left { background-color: #993366;  color: #fff;}
-
-
-.ticket-summary .titlebox-title .left a, .ticket-summary .titlebox-title .left a:visited { color: #fff;}
-
-.unread-messages .titlebox , .unread-messages .titlebox-title .left { 
-  border: 1px solid #99a;
-  border-right: 2px solid #aab;
-  border-bottom: 2px solid #aab;
-
-}
-
-
-.unread-messages .titlebox { 
-  background-color: #dde;
-}
-
-.unread-messages .titlebox-title .left { 
-  background-color: #cce;
-}
-
-.ticket-inactive {
-  text-decoration: line-through;
-  color: #666
-}
-
-table.ticket-summary td.boxcontainer:first-child {
-  width: 50%;
-}
-
-div.requestor-ticket-links {
-    text-align: left;
-    font-size: 0.8em;
-    padding-top: 0.25em;
-}
-
-.more-about-requestor-extra-field .label {
-    display: inline-block;
-    width: 8em;
-    font-weight: bold;
-    text-align: right;
-}
-
-.more-about-requestor-extra-field .value {
-    display: inline-block;
-}
-
-.ticket-info-requestor .more-about-requestor-extra,
-.ticket-info-requestor .comments-about-user,
-.ticket-info-requestor .more-about-requestor-tickets,
-.ticket-info-requestor .more-about-user-groups
-{
-    margin: 1em 0;
-}
-
-.ticket-info-requestor .comments-about-user .label,
-.ticket-info-requestor .more-about-user-groups .label,
-.ticket-info-requestor .more-about-requestor-tickets .label
-{
-    display: block;
-    font-weight: bold;
-    text-align: left;
-}
-
-.ticket-info-requestor .more-about-user-groups .value ul,
-.ticket-info-requestor .more-about-requestor-tickets ul
-{
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-
-/* textareas get to be bigger when we're in a table */
-tr.edit-custom-field.cftype-Text textarea,
-tr.edit-custom-field.cftype-Freeform input,
-tr.edit-custom-field.cftype-Wikitext textarea
-{
-    width: 100%;
-}

commit d6a574c7858162f0c63589b0fc8ea4def26201a1
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 27 11:56:22 2012 -0800

    Use .transaction instead of div.history-container
    
    .transaction is more accurate than .history-container since we want to
    apply the rules when the transaction is rendered inside or outside the
    container.

diff --git a/share/html/NoAuth/css/base/history.css b/share/html/NoAuth/css/base/history.css
index 1c12187..acb8771 100644
--- a/share/html/NoAuth/css/base/history.css
+++ b/share/html/NoAuth/css/base/history.css
@@ -45,13 +45,13 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-div.history-container div.transaction {
+.transaction {
     border-top: 2px solid #ccc;
     padding-bottom: 0.5em;
     position: relative; /* gives us a container for position: absolute */
 }
 
-div.history-container div.odd {
+.transaction.odd {
  background-color: #fff;
 }
 
@@ -97,28 +97,28 @@ div.history-container {
 
 }
 
-div.history-container span.type a {
+.transaction span.type a {
  color: #fff;
  padding-top: 0.75em;
  display: block;
 }
 
 
-div.history-container span.date {
+.transaction span.date {
  width: 10em;
 }
 
 
-div.history-container span.description {
+.transaction span.description {
  margin-left: 1em;
  font-weight: bold;
 }
 
-div.history-container span.time-taken {
+.transaction span.time-taken {
  margin-left: 1em;
 }
 
-div.history-container div.content {
+.transaction div.content {
  padding-right: 1em;
  padding-bottom: 0.7em;
  margin-left: 1.5em;
@@ -142,7 +142,7 @@ div.history-container div.content {
  max-width: 100%;
 }
 
-div.history-container div.downloadattachment {
+.transaction div.downloadattachment {
 float: right;
 clear: both;
 font-size: 0.9em;
@@ -160,20 +160,20 @@ margin-top: 0.5em;
  border-radius: 0.5em;
 }
 
-div.history-container div.downloadattachment .downloadcontenttype{
+.transaction div.downloadattachment .downloadcontenttype{
 color: #666;
 padding-right:0.25em;
 }
 
 
-div.history-container .message-header-key {
+.transaction .message-header-key {
   width: 7em;
   font-weight: bold;
   color: #666;
 }
 
 
-div.history-container .messagebody .messagebody{
+.transaction .messagebody .messagebody{
  font-size: 1em;
  padding: 0;
  border: 0;

commit d78cf7af01cd2076cab9fbd29859d9a525ce892c
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Nov 27 17:16:16 2012 -0800

    Fix tests to follow links relative to the current page, not the server base

diff --git a/t/web/ticket_txn_content.t b/t/web/ticket_txn_content.t
index c0cae97..096d78e 100644
--- a/t/web/ticket_txn_content.t
+++ b/t/web/ticket_txn_content.t
@@ -27,14 +27,14 @@ sub follow_parent_with_headers_link {
     my $m    = shift;
     my $link = $m->find_link(@_)->url;
     $link =~ s{/(\d+)$}{"/" . ($1-1)}e;  # get the parent attach
-    $m->get_ok($baseurl . $link);
+    $m->get_ok($link);
 }
 
 sub follow_with_headers_link {
     my $m    = shift;
     my $link = $m->find_link(@_)->url;
     $link =~ s{/\d+/(\d+)/.+$}{/WithHeaders/$1};   # frob into a with headers url
-    $m->get_ok($baseurl . $link);
+    $m->get_ok($link);
 }
 
 for my $type ( 'text/plain', 'text/html' ) {

-----------------------------------------------------------------------


More information about the Rt-commit mailing list