[Rt-commit] r7372 - in rt/branches/3.7-EXPERIMENTAL-TUNIS: . html html/Elements/RT__Ticket html/Search/Elements html/Ticket html/Ticket/Elements lib/RT lib/RT/Interface lib/RT/Interface/Web

clkao at bestpractical.com clkao at bestpractical.com
Tue Mar 27 10:57:23 EDT 2007


Author: clkao
Date: Tue Mar 27 10:57:22 2007
New Revision: 7372

Modified:
   rt/branches/3.7-EXPERIMENTAL-TUNIS/   (props changed)
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Elements/RT__Ticket/ColumnMap
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/BuildFormatString
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/EditFormat
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowHistory
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html
   rt/branches/3.7-EXPERIMENTAL-TUNIS/html/autohandler
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/Notify.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachment_Overlay.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachments_Overlay.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Handler.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Request.pm
   rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Transaction_Overlay.pm

Log:
merge down.

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Elements/RT__Ticket/ColumnMap
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Elements/RT__Ticket/ColumnMap	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Elements/RT__Ticket/ColumnMap	Tue Mar 27 10:57:22 2007
@@ -60,42 +60,13 @@
     if ( $COLUMN_MAP->{$name} ) {
         return ( $COLUMN_MAP->{$name}->{$attr} );
     }
+    elsif ( (my ($mainkey, $subkey) = $name =~ /^(.*?)\.{(.+)}$/) && $COLUMN_MAP->{$1} ) {
+        return $COLUMN_MAP->{$mainkey}->{$attr}
+            unless ref $COLUMN_MAP->{$mainkey}->{$attr} eq 'CODE';
 
-    # now, let's deal with harder things, like Custom Fields
-
-    elsif ( $name =~ /^(?:CF|CustomField)\.\{(.+)\}$/ ) {
-        my $field = $1;
-
-        if ( $attr eq 'attribute' ) {
-            return (undef);
-        }
-        elsif ( $attr eq 'title' ) {
-            return ( $field );
-        }
-        elsif ( $attr eq 'value' ) {
-            # Display custom field contents, separated by newlines.
-            # For Image custom fields we also show a thumbnail here.
-            return sub {
-                my $values = $_[0]->CustomFieldValues($field);
-                my @values =  map {
-                    (
-                        ($_->CustomFieldObj->Type eq 'Image')
-                            ? \($m->scomp( '/Elements/ShowCustomFieldImage', Object => $_ ))
-                            : $_->Content
-                    ),
-                    \'<br />',
-                } @{ $values->ItemsArrayRef };
-            pop @values; # Remove that last <br />
-            return @values;
-            };
-        }
-    }
-    elsif ( $name =~ /^(WebPath|WebBaseURL|WebURL)$/ ) {
-        if ( $attr eq 'value' ) {
-            my $value = RT->Config->Get($1);
-            return sub { return \$value };
-        }
+        return sub { $COLUMN_MAP->{$mainkey}->{$attr}->( @_, $subkey ) };
     }
+
 };
 
 my $LinkCallback = sub {
@@ -309,6 +280,12 @@
         $_ => { value => $LinkCallback->( $_ ) }
     } keys %RT::Ticket::LINKTYPEMAP),
 
+    (map {
+        my $value = RT->Config->Get($_);
+        $_ => { value => sub { return \$value } };
+    
+    } qw(WebPath WebBaseURL WebURL)),
+
     '_CLASS' => {
         value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
     },
@@ -319,9 +296,33 @@
         value     => sub { return \('<input type="checkbox" class="checkbox" name="UpdateTicket'.$_[0]->id.'" value="1" checked />') }
     },
 
+    CustomField => {
+        attribute => undef,
+        title     => sub { return pop @_ },
+        value     => sub {
+            # Display custom field contents, separated by newlines.
+            # For Image custom fields we also show a thumbnail here.
+
+            my $values = $_[0]->CustomFieldValues( $_[-1] );
+            my @values = map {
+                (
+                    ($_->CustomFieldObj->Type eq 'Image')
+                        ? \($m->scomp( '/Elements/ShowCustomFieldImage', Object => $_ ))
+                        : $_->Content
+                ),
+                \'<br />',
+            } @{ $values->ItemsArrayRef };
+            pop @values; # Remove that last <br />
+            return @values;
+        },
+    },
 };
+
+$COLUMN_MAP->{'CF'} = $COLUMN_MAP->{'CustomField'};
+
 </%ONCE>
 <%init>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
+$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap' );
 return $ColumnMap->( $Name, $Attr );
 </%init>

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/BuildFormatString
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/BuildFormatString	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/BuildFormatString	Tue Mar 27 10:57:22 2007
@@ -43,69 +43,58 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
-<%args>
-$Format => undef
+<%ARGS>
+$Format => RT->Config->Get('DefaultSearchResultFormat')
+
 %cfqueues => undef
+
 $Face => undef
 $Size => undef
 $Link => undef
 $Title => undef
+
 $AddCol => undef
 $RemoveCol => undef
 $ColUp => undef
 $ColDown => undef
+
 $SelectDisplayColumns => undef
 $CurrentDisplayColumns => undef
-</%args>
-<%init>
-
-$Format ||= RT->Config->Get('DefaultSearchResultFormat');
+</%ARGS>
+<%ONCE>
 
 # All the things we can display in the format string by default
 my @fields = qw(
-  id
-  Status
-  ExtendedStatus
-  Subject
-  QueueName
-  OwnerName
-  Priority
-  InitialPriority
-  FinalPriority
-  Type
-  TimeWorked
-  TimeLeft
-  TimeEstimated
-  CreatedBy
-  LastUpdatedBy
-  Requestors
-  Cc
-  AdminCc
-  Starts
-  StartsRelative
-  Started
-  StartedRelative
-  Created
-  CreatedRelative
-  LastUpdated
-  LastUpdatedRelative
-  Told
-  ToldRelative
-  Due
-  DueRelative
-  Resolved
-  ResolvedRelative
-  RefersTo
-  ReferredToBy
-  DependsOn
-  DependedOnBy
-  MemberOf
-  Members
-  Parents
-  Children
-  NEWLINE
+    id QueueName Subject
+    Status ExtendedStatus UpdateStatus
+    Type
+
+    OwnerName Requestors Cc AdminCc CreatedBy LastUpdatedBy
+
+    Priority InitialPriority FinalPriority
+
+    TimeWorked TimeLeft TimeEstimated
+
+    Starts      StartsRelative
+    Started     StartedRelative
+    Created     CreatedRelative
+    LastUpdated LastUpdatedRelative
+    Told        ToldRelative
+    Due         DueRelative
+    Resolved    ResolvedRelative
+
+    RefersTo    ReferredToBy
+    DependsOn   DependedOnBy
+    MemberOf    Members
+    Parents     Children
+
+    NEWLINE
 );
 
+</%ONCE>
+<%init>
+$m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fields );
+
 my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
 foreach my $id (keys %cfqueues) {
     # Gotta load up the $queue object, since queues get stored by name now. my $id
@@ -126,6 +115,8 @@
     push @fields, "CustomField.{" . $CustomField->Name . "}";
 }
 
+$m->callback( Fields => \@fields, ARGSRef => \%ARGS );
+
 my ( @seen);
 
 my @format = split( /,\s*/, $Format );
@@ -161,44 +152,44 @@
 }
 elsif ( $AddCol ) {
     if ( defined $SelectDisplayColumns ) {
-	my $selected = $SelectDisplayColumns;
-	my @columns;
-	if (ref($selected) eq 'ARRAY') {
-	    @columns = @$selected;
-	} else {
-	    push @columns, $selected;
-	}
-	foreach my $col (@columns) {
-	    my %column = ();
-	    $column{Column} = $col;
-
-    if ( $Face eq "Bold" ) {
-        $column{Prefix} .= "<b>";
-        $column{Suffix} .= "</b>";
-    }
-    if ( $Face eq "Italic" ) {
-        $column{Prefix} .= "<i>";
-        $column{Suffix} .= "</i>";
-    }
-    if ($Size) {
-        $column{Prefix} .= "<" . $m->interp->apply_escapes( $Size,  'h' ) . ">";
-        $column{Suffix} .= "</" . $m->interp->apply_escapes( $Size, 'h' ) . ">";
-    }
-    if ( $Link eq "Display" ) {
-        $column{Prefix} .= q{<a HREF="__WebPath__/Ticket/Display.html?id=__id__">};
-        $column{Suffix} .= "</a>";
-    }
-    elsif ( $Link eq "Take" ) {
-        $column{Prefix} .= q{<a HREF="__WebPath__/Ticket/Display.html?Action=Take&id=__id__">};
-        $column{Suffix} .= "</a>";
-    }
-
-    if ($Title) {
-        $column{Suffix} .= "/TITLE:" . $m->interp->apply_escapes( $Title, 'h' );
+        my $selected = $SelectDisplayColumns;
+        my @columns;
+        if (ref($selected) eq 'ARRAY') {
+            @columns = @$selected;
+        } else {
+            push @columns, $selected;
+        }
+        foreach my $col (@columns) {
+            my %column = ();
+            $column{Column} = $col;
+
+            if ( $Face eq "Bold" ) {
+                $column{Prefix} .= "<b>";
+                $column{Suffix} .= "</b>";
+            }
+            if ( $Face eq "Italic" ) {
+                $column{Prefix} .= "<i>";
+                $column{Suffix} .= "</i>";
+            }
+            if ($Size) {
+                $column{Prefix} .= "<" . $m->interp->apply_escapes( $Size,  'h' ) . ">";
+                $column{Suffix} .= "</" . $m->interp->apply_escapes( $Size, 'h' ) . ">";
+            }
+            if ( $Link eq "Display" ) {
+                $column{Prefix} .= q{<a HREF="__WebPath__/Ticket/Display.html?id=__id__">};
+                $column{Suffix} .= "</a>";
+            }
+            elsif ( $Link eq "Take" ) {
+                $column{Prefix} .= q{<a HREF="__WebPath__/Ticket/Display.html?Action=Take&id=__id__">};
+                $column{Suffix} .= "</a>";
+            }
+
+            if ($Title) {
+                $column{Suffix} .= "/TITLE:" . $m->interp->apply_escapes( $Title, 'h' );
+            }
+            push @seen, \%column;
+        }
     }
-    push @seen, \%column;
-}
-}
 }
 elsif ( $ColUp ) {
     my $index = $CurrentDisplayColumns;

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/EditFormat
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/EditFormat	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Search/Elements/EditFormat	Tue Mar 27 10:57:22 2007
@@ -44,28 +44,23 @@
 %# 
 %# END BPS TAGGED BLOCK }}}
 <table>
+
 <tr>
-<td>
-<&|/l&>Add Columns</&>:
-</td>
-<td>
-<&|/l&>Format</&>:
-</td>
-<td></td>
-<td>
-<&|/l&>Show Columns</&>:
-</td>
+<th><&|/l&>Add Columns</&>:</th>
+<th><&|/l&>Format</&>:</th>
+<th></th>
+<th><&|/l&>Show Columns</&>:</th>
+</tr>
+
 <tr>
-<td valign="top">
-<select size="6" name="SelectDisplayColumns" multiple>
+
+<td valign="top"><select size="6" name="SelectDisplayColumns" multiple>
 % foreach my $field ( @$AvailableColumns) {
-<option value="<%$field%>"><% loc( $field) %></option>
-%# $m->comp( '/Elements/RT__Ticket/ColumnMap', Name => $field, Attr => 'title') || 
+<option value="<% $field %>"><% loc($field) %></option>
 % }
-</select>
-</td>
-<td>
-<&|/l&>Link</&>:
+</select></td>
+
+<td><&|/l&>Link</&>:
 <select name="Link">
 <option value="None">-</option>
 <option value="Display"><&|/l&>Display</&></option>
@@ -85,15 +80,14 @@
 <option value="Italic"><&|/l&>Italic</&></option>
 </select>
 </td>
-<td>
-<input type="submit" class="button" name="AddCol" value=" &rarr; " />
-</td>
+
+<td><input type="submit" class="button" name="AddCol" value=" &rarr; " /></td>
+
 <td valign="top">
 <select size="4" name="CurrentDisplayColumns">
 % my $i=0;
-% foreach my $field (@$CurrentFormat) {
-<option value="<%$i++%>><%$field->{Column}%>">
-<%loc( $field->{Column}) %></option>
+% foreach my $field ( @$CurrentFormat ) {
+<option value="<% $i++ %>><% $field->{Column} %>"><% loc( $field->{Column} ) %></option>
 % }
 </select>
 <br />
@@ -103,8 +97,7 @@
 <input type="submit" class="button" name="RemoveCol" value="<%loc('Delete')%>" />
 </center>
 </td>
-<td colspan="3" align="center">
-</td>
+
 </tr>
 </table>
 

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowHistory
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowHistory	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Elements/ShowHistory	Tue Mar 27 10:57:22 2007
@@ -104,14 +104,10 @@
     $m->comp( 'ShowTransaction',
             %ARGS,
 
-              AttachPath           => $AttachPath,
-              UpdatePath           => $UpdatePath,
               Ticket               => $Ticket,
               Transaction          => $Transaction,
               ShowHeaders          => $ShowHeaders,
-              Collapsed            => $Collapsed,
               RowNum               => $i,
-              ShowTitleBarCommands => $ShowTitleBarCommands,
               Attachments          => \@trans_attachments,
               AttachmentContent    => $trans_content,
               LastTransaction      => $Transactions->IsLast
@@ -158,10 +154,6 @@
 $Attachments => undef
 $AttachmentContent => undef
 $ShowHeaders => undef
-$Collapsed => undef
 $ShowTitle => 1
 $ShowDisplayModes => 1
-$ShowTitleBarCommands => 1
-$AttachPath => RT->Config->Get('WebPath')."/Ticket/Attachment"
-$UpdatePath => RT->Config->Get('WebPath')."/Ticket/Update.html"
 </%ARGS>

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/Ticket/Forward.html	Tue Mar 27 10:57:22 2007
@@ -39,75 +39,9 @@
 Abort( loc("Couldn't load transaction #[_1]", $QuoteTransaction) )
     unless $txn->id;
 
-my $create_entity = sub {
-    my $attachment = shift;
-
-    my $entity = new MIME::Entity;
-    $entity->head->add( split /:/, $_, 2 )
-        foreach $attachment->SplitHeaders;
-
-    use MIME::Body;
-    $entity->bodyhandle(
-        MIME::Body::Scalar->new( $attachment->OriginalContent )
-    );
-
-    return $entity;
-};
-
-
 if ( $ARGS{'Forward'} ) {
-    my $main_content = $txn->ContentObj;
-
-    my $entity = $create_entity->( $main_content );
-    if ( $main_content->Parent ) {
-        # main content is not top most entity, we shouldn't loose
-        # From/To/Cc headers that are on a top part
-        my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
-        $attachments->Columns(qw(id Parent TransactionId Headers));
-        $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
-        $attachments->Limit( FIELD => 'Parent', VALUE => 0 );
-        $attachments->Limit( FIELD => 'Parent', OERATOR => 'IS', VALUE => 'NULL', QUOTEVALUE => 0 );
-        $attachments->OrderBy( FIELD => 'id', ORDER => 'ASC' );
-        my $tmp = $attachments->First;
-        if ( $tmp && $tmp->id ne $main_content->id ) {
-            $entity->make_multipart;
-            $entity->head->add( split /:/, $_, 2 ) foreach $tmp->SplitHeaders;
-            $entity->make_singlepart;
-        }
-    }
-
-    my $attachments = RT::Attachments->new( $session{'CurrentUser'} );
-    $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
-    $attachments->Limit(
-        FIELD => 'id',
-        OPERATOR => '!=',
-        VALUE => $main_content->id,
-    );
-    $attachments->Limit(
-        FIELD => 'ContentType',
-        OPERATOR => 'NOT STARTSWITH',
-        VALUE => 'multipart/',
-    );
-    $attachments->Limit(
-        FIELD => 'Content',
-        OPERATOR => '!=',
-        VALUE => '',
-    );
-    while ( my $a = $attachments->Next ) {
-        $entity->make_multipart;
-        $entity->add_part( $create_entity->( $a ) );
-    }
-
-    my $mail = MIME::Entity->build(
-        To => $ARGS{'To'},
-        Cc => $ARGS{'Cc'},
-        Bcc => $ARGS{'Bcc'},
-        Subject => 'Fwd: '. ($txn->Subject || $TicketObj->Subject ),
-        Type => 'message/rfc822',
-        Encoding => '8bit',
-        Data => $entity->as_string,
-    );
-    RT::Interface::Email::SendEmail( entity => $mail );
+    require RT::Interface::Email;
+    RT::Interface::Email::ForwardTransaction( $txn, %ARGS );
 }
 
 my $Title = loc('Forward message');

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/html/autohandler
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/html/autohandler	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/html/autohandler	Tue Mar 27 10:57:22 2007
@@ -61,6 +61,12 @@
     $m->autoflush( $m->request_comp->attr('AutoFlush') );
 }
 
+# XXX: on a cold server (just after restart) people could have a principal object
+# in the session, as we deserialize it so we never call constructor of the principal
+# class, so the list of accessible fields is empty and we die with "Method xxx is
+# not implemented in RT::Principal"
+{ my $tmp = RT::Principal->new( $RT::SystemUser ) }
+
 %ARGS = map {
 
     # if they've passed multiple values, they'll be an array. if they've

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/Notify.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/Notify.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/Notify.pm	Tue Mar 27 10:57:22 2007
@@ -45,12 +45,13 @@
 # END BPS TAGGED BLOCK }}}
 #
 package RT::Action::Notify;
-require RT::Action::SendEmail;
-use Mail::Address;
+
 use strict;
-use vars qw/@ISA/;
- at ISA = qw(RT::Action::SendEmail);
+use warnings;
+
+use base qw(RT::Action::SendEmail);
 
+use Mail::Address;
 
 =head2 Prepare
 
@@ -65,8 +66,6 @@
     $self->SUPER::Prepare();
 }
 
-# {{{ sub SetRecipients
-
 =head2 SetRecipients
 
 Sets the recipients of this meesage to Owner, Requestor, AdminCc, Cc or All. 
@@ -77,8 +76,9 @@
 sub SetRecipients {
     my $self = shift;
 
-    my $arg = $self->Argument;
+    my $ticket = $self->TicketObj;
 
+    my $arg = $self->Argument;
     $arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
 
     my ( @To, @PseudoTo, @Cc, @Bcc );
@@ -86,64 +86,53 @@
 
     if ( $arg =~ /\bOtherRecipients\b/ ) {
         if ( my $attachment = $self->TransactionObj->Attachments->First ) {
-            my @cc_addresses = Mail::Address->parse($attachment->GetHeader('RT-Send-Cc'));
-            foreach my $addr (@cc_addresses) {
-                  push @Cc, $addr->address;
-            }
-            my @bcc_addresses = Mail::Address->parse($attachment->GetHeader('RT-Send-Bcc'));
-
-            foreach my $addr (@bcc_addresses) {
-                  push @Bcc, $addr->address;
-            }
-
+            push @Cc, map { $_->address } Mail::Address->parse(
+                $attachment->GetHeader('RT-Send-Cc')
+            );
+            push @Bcc, map { $_->address } Mail::Address->parse(
+                $attachment->GetHeader('RT-Send-Bcc')
+            );
         }
     }
 
     if ( $arg =~ /\bRequestor\b/ ) {
-        push ( @To, $self->TicketObj->Requestors->MemberEmailAddresses  );
+        push @To, $ticket->Requestors->MemberEmailAddresses;
     }
 
-    
-
     if ( $arg =~ /\bCc\b/ ) {
 
         #If we have a To, make the Ccs, Ccs, otherwise, promote them to To
         if (@To) {
-            push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
-            push ( @Cc, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses  );
+            push ( @Cc, $ticket->Cc->MemberEmailAddresses );
+            push ( @Cc, $ticket->QueueObj->Cc->MemberEmailAddresses  );
         }
         else {
-            push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses  );
-            push ( @To, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses  );
+            push ( @Cc, $ticket->Cc->MemberEmailAddresses  );
+            push ( @To, $ticket->QueueObj->Cc->MemberEmailAddresses  );
         }
     }
 
-    if ( ( $arg =~ /\bOwner\b/ )
-        && ( $self->TicketObj->OwnerObj->id != $RT::Nobody->id ) )
-    {
-
-        # If we're not sending to Ccs or requestors, 
+    if ( $arg =~ /\bOwner\b/ && $ticket->OwnerObj->id != $RT::Nobody->id ) {
+        # If we're not sending to Ccs or requestors,
         # then the Owner can be the To.
         if (@To) {
-            push ( @Bcc, $self->TicketObj->OwnerObj->EmailAddress );
+            push ( @Bcc, $ticket->OwnerObj->EmailAddress );
         }
         else {
-            push ( @To, $self->TicketObj->OwnerObj->EmailAddress );
+            push ( @To, $ticket->OwnerObj->EmailAddress );
         }
 
     }
 
     if ( $arg =~ /\bAdminCc\b/ ) {
-        push ( @Bcc, $self->TicketObj->AdminCc->MemberEmailAddresses  );
-        push ( @Bcc, $self->TicketObj->QueueObj->AdminCc->MemberEmailAddresses  );
+        push ( @Bcc, $ticket->AdminCc->MemberEmailAddresses  );
+        push ( @Bcc, $ticket->QueueObj->AdminCc->MemberEmailAddresses  );
     }
 
-    if (RT->Config->Get('UseFriendlyToLine')) {
+    if ( RT->Config->Get('UseFriendlyToLine') ) {
         unless (@To) {
-            push (
-		@PseudoTo,
-		sprintf(RT->Config->Get('FriendlyToLineFormat'), $arg, $self->TicketObj->id),
-	    );
+            push @PseudoTo,
+                sprintf RT->Config->Get('FriendlyToLineFormat'), $arg, $ticket->id;
         }
     }
 
@@ -167,8 +156,6 @@
 
 }
 
-# }}}
-
 eval "require RT::Action::Notify_Vendor";
 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Vendor.pm});
 eval "require RT::Action::Notify_Local";

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Action/SendEmail.pm	Tue Mar 27 10:57:22 2007
@@ -75,22 +75,26 @@
 Basically, you create another module RT::Action::YourAction which ISA
 RT::Action::SendEmail.
 
+=head1 METHODS
 
+=head2 CleanSlate
 
-=head1 AUTHOR
-
-Jesse Vincent <jesse at bestpractical.com> and Tobias Brox <tobix at cpan.org>
+Cleans class-wide options, like L</SquelchMailTo> or L</AttachTickets>.
 
-=head1 SEE ALSO
 
-perl(1).
 
-=cut
+sub CleanSlate {
+    my $self = shift;
+    $self->SquelchMailTo( undef );
+    $self->AttachTickets( undef );
+}
 
-# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
+=head2 Commit
 
+Sends the prepared message and writes outgoing record into DB if the feature is
+activated in the config.
 
-# {{{ sub Commit
+=cut
 
 sub Commit {
     my $self = shift;
@@ -102,9 +106,11 @@
     return (abs $ret);
 }
 
-# }}}
+=head2 Prepare
 
-# {{{ sub Prepare
+Builds an outgoing email we're going to send using scrip's template.
+
+=cut
 
 sub Prepare {
     my $self = shift;
@@ -141,8 +147,8 @@
     # If we don't have any 'To' header (but do have other recipients), drop in
     # the pseudo-to header.
     $self->SetHeader( 'To', join ( ', ', @{ $self->{'PseudoTo'} } ) )
-      if ( $self->{'PseudoTo'} && ( @{ $self->{'PseudoTo'} } )
-        and ( !$MIMEObj->head->get('To') ) ) and ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc'));
+        if $self->{'PseudoTo'} && @{ $self->{'PseudoTo'} } && !$MIMEObj->head->get('To') 
+           && ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc') );
 
     # We should never have to set the MIME-Version header
     $self->SetHeader( 'MIME-Version', '1.0' );
@@ -158,21 +164,17 @@
     $self->SetHeader( 'Content-Type', 'text/plain; charset="'. $output_enc .'"' );
 
     # Build up a MIME::Entity that looks like the original message.
-    $self->AddAttachments() if ( $MIMEObj->head->get('RT-Attach-Message') );
+    $self->AddAttachments if $MIMEObj->head->get('RT-Attach-Message');
+
+    $self->AddTickets;
 
     return $result;
 
 }
 
-# }}}
-
-# }}}
-
-
-
 =head2 To
 
-Returns an array of Mail::Address objects containing all the To: recipients for this notification
+Returns an array of L<Mail::Address> objects containing all the To: recipients for this notification
 
 =cut
 
@@ -183,7 +185,7 @@
 
 =head2 Cc
 
-Returns an array of Mail::Address objects containing all the Cc: recipients for this notification
+Returns an array of L<Mail::Address> objects containing all the Cc: recipients for this notification
 
 =cut
 
@@ -194,7 +196,7 @@
 
 =head2 Bcc
 
-Returns an array of Mail::Address objects containing all the Bcc: recipients for this notification
+Returns an array of L<Mail::Address> objects containing all the Bcc: recipients for this notification
 
 =cut
 
@@ -215,8 +217,6 @@
 }
 
 
-# {{{ SendMessage
-
 =head2 SendMessage MIMEObj
 
 sends the message using RT's preferred API.
@@ -266,8 +266,6 @@
     return (1);
 }
 
-# {{{ AddAttachments 
-
 =head2 AddAttachments
 
 Takes any attachments to this transaction and attaches them to the message
@@ -288,38 +286,126 @@
         FIELD => 'TransactionId',
         VALUE => $self->TransactionObj->Id
     );
+    # Don't attach anything blank
+    $attachments->LimitNotEmpty;
     $attachments->OrderBy( FIELD => 'id');
 
+    # We want to make sure that we don't include the attachment that's
+    # being sued as the "Content" of this message"
     my $transaction_content_obj = $self->TransactionObj->ContentObj;
+    # XXX: this is legacy check of content type looks quite incorrect
+    # to me //ruz
+    if ( $transaction_content_obj && $transaction_content_obj->id
+         && $transaction_content_obj->ContentType =~ m{text/plain}i )
+    {
+        $attachments->Limit(
+            ENTRYAGGREGATOR => 'AND',
+            FIELD           => 'id',
+            OPERATOR        => '!=',
+            VALUE           => $transaction_content_obj->id,
+        );
+    }
 
     # attach any of this transaction's attachments
     while ( my $attach = $attachments->Next ) {
+        $MIMEObj->make_multipart('mixed');
+        $self->AddAttachment( $attach );
+    }
 
-        # Don't attach anything blank
-        next unless ( $attach->ContentLength );
+}
 
-# We want to make sure that we don't include the attachment that's being sued as the "Content" of this message"
-        next
-          if ( $transaction_content_obj
-            && $transaction_content_obj->Id == $attach->Id
-            && $transaction_content_obj->ContentType =~ qr{text/plain}i );
-        $MIMEObj->make_multipart('mixed');
-        $MIMEObj->attach(
-            Type     => $attach->ContentType,
-            Charset  => $attach->OriginalEncoding,
-            Data     => $attach->OriginalContent,
-            Filename => $self->MIMEEncodeString( $attach->Filename,
-                RT->Config->Get('EmailOutputEncoding') ),
-            'RT-Attachment:' => $self->TicketObj->Id."/".$self->TransactionObj->Id."/".$attach->id,
-            Encoding => '-SUGGEST'
-        );
+=head2 AddAttachment $attachment
+
+Takes one attachment object of L<RT::Attachmment> class and attaches it to the message
+we're building.
+
+=cut
+
+sub AddAttachment {
+    my $self = shift;
+    my $attach = shift;
+    my $MIMEObj = shift || $self->TemplateObj->MIMEObj;
+
+    $MIMEObj->attach(
+        Type     => $attach->ContentType,
+        Charset  => $attach->OriginalEncoding,
+        Data     => $attach->OriginalContent,
+        Filename => $self->MIMEEncodeString( $attach->Filename,
+            RT->Config->Get('EmailOutputEncoding') ),
+        'RT-Attachment:' => $self->TicketObj->Id."/".$self->TransactionObj->Id."/".$attach->id,
+        Encoding => '-SUGGEST',
+    );
+}
+
+=head2 AttachTickets [@IDs]
+
+Returns or set list of ticket's IDs that should be attached to an outgoing message.
+
+B<Note> this method works as a class method and setup things global, so you have to
+clean list by passing undef as argument.
+
+=cut
+
+{
+    my $list = [];
+    sub AttachTickets {
+        my $self = shift;
+        $list = [ grep defined, @_ ] if @_;
+        return @$list;
     }
+}
 
+=head2 AddTickets
+
+Attaches tickets to the current message, list of tickets' ids get from
+L</AttachTickets> method.
+
+=cut
+
+sub AddTickets {
+    my $self = shift;
+    $self->AddTicket($_) foreach $self->AttachTickets;
+    return;
 }
 
-# }}}
+=head2 AddTicket $ID
+
+Attaches a ticket with ID to the message.
+
+Each ticket is attached as multipart entity and all its messages and attachments
+are attached as sub entities in order of creation, but only if transaction type
+is Create or Correspond.
+
+=cut
+
+sub AddTicket {
+    my $self = shift;
+    my $tid = shift;
 
-# {{{ RecordOutgoingMailTransaction
+    # XXX: we need a current user here, but who is current user?
+    my $attachs = RT::Attachments->new( $RT::SystemUser );
+    my $txn_alias = $attachs->TransactionAlias;
+    $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' );
+    $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Correspond' );
+    $attachs->LimitByTicket( $tid );
+    $attachs->LimitNotEmpty;
+    $attachs->OrderBy( FIELD => 'Created' );
+
+    my $ticket_mime = MIME::Entity->build(
+        Type => 'multipart/mixed',
+        Top => 0,
+        Description => "ticket #$tid",
+    );
+    while ( my $attachment = $attachs->Next ) {
+        $self->AddAttachment( $attachment, $ticket_mime );
+    }
+    if ( $ticket_mime->parts ) {
+        my $email_mime = $self->TemplateObj->MIMEObj;
+        $email_mime->make_multipart;
+        $email_mime->add_part( $ticket_mime );
+    }
+    return;
+}
 
 =head2 RecordOutgoingMailTransaction MIMEObj
 
@@ -378,11 +464,6 @@
 
 }
 
-# }}}
-#
-
-# {{{ sub SetRTSpecialHeaders
-
 =head2 SetRTSpecialHeaders 
 
 This routine adds all the random headers that RT wants in a mail message
@@ -445,15 +526,13 @@
 
 }
 
-# }}}
-
-=head2 SquelchMailTo 
+=head2 SquelchMailTo [@ADDRESSES]
 
-Mark address to be removed from list of the recipients. Returns list of the addresses.
+Mark ADDRESSES to be removed from list of the recipients. Returns list of the addresses.
 To empty list pass undefined argument.
 
 B<Note> that this method can be called as class method and works globaly. Don't forget to
-clean this list when blocking is not required anymore.
+clean this list when blocking is not required anymore, pass undef to do this.
 
 =cut
 
@@ -462,14 +541,12 @@
     sub SquelchMailTo {
         my $self = shift;
         if ( @_ ) {
-            $squelch = [ @_ ];
+            $squelch = [ grep defined, @_ ];
         }
-        return grep defined, @{ $squelch };
+        return @$squelch;
     }
 }
 
-# {{{ RemoveInappropriateRecipients
-
 =head2 RemoveInappropriateRecipients
 
 Remove addresses that are RT addresses or that are on this transaction's blacklist
@@ -549,9 +626,6 @@
     }
 }
 
-# }}}
-# {{{ sub SetReturnAddress
-
 =head2 SetReturnAddress is_comment => BOOLEAN
 
 Calculate and set From and Reply-To headers based on the is_comment flag.
@@ -608,10 +682,6 @@
 
 }
 
-# }}}
-
-# {{{ sub SetHeader
-
 =head2 SetHeader FIELD, VALUE
 
 Set the FIELD of the current MIME object into VALUE.
@@ -630,11 +700,6 @@
     return $self->TemplateObj->MIMEObj->head->get($field);
 }
 
-# }}}
-
-
-# {{{ sub SetSubject
-
 =head2 SetSubject
 
 This routine sets the subject. it does not add the rt tag. that gets done elsewhere
@@ -671,10 +736,6 @@
 
 }
 
-# }}}
-
-# {{{ sub SetSubjectToken
-
 =head2 SetSubjectToken
 
 This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
@@ -700,8 +761,6 @@
     );
 }
 
-# }}}
-
 =head2 SetReferencesHeaders
 
 Set References and In-Reply-To headers for this message.
@@ -773,8 +832,6 @@
 
 }
 
-# }}}
-
 =head2 PseudoReference
 
 Returns a fake Message-ID: header for the ticket to allow a base level of threading
@@ -789,8 +846,6 @@
 }
 
 
-# {{{ SetHeadingAsEncoding
-
 =head2 SetHeaderAsEncoding($field_name, $charset_encoding)
 
 This routine converts the field into specified charset encoding.
@@ -818,9 +873,6 @@
 
 
 } 
-# }}}
-
-# {{{ MIMEEncodeString
 
 =head2 MIMEEncodeString STRING ENCODING
 
@@ -868,12 +920,20 @@
     }
 }
 
-# }}}
-
 eval "require RT::Action::SendEmail_Vendor";
 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm});
 eval "require RT::Action::SendEmail_Local";
 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm});
 
+=head1 AUTHOR
+
+Jesse Vincent <jesse at bestpractical.com> and Tobias Brox <tobix at cpan.org>
+
+=head1 SEE ALSO
+
+L<RT::Action::Notify>, L<RT::Action::NotifyAsComment> and L<RT::Action::Autoreply>
+
+=cut
+
 1;
 

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachment_Overlay.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachment_Overlay.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachment_Overlay.pm	Tue Mar 27 10:57:22 2007
@@ -46,8 +46,7 @@
 
 =head1 SYNOPSIS
 
-  use RT::Attachment;
-
+    use RT::Attachment;
 
 =head1 DESCRIPTION
 
@@ -55,7 +54,6 @@
 module which should only be instantiated through exported APIs in Ticket, Queue and other 
 similar objects.
 
-
 =head1 METHODS
 
 
@@ -69,12 +67,9 @@
 no warnings qw(redefine);
 
 use RT::Transaction;
-
 use MIME::Base64;
 use MIME::QuotedPrint;
 
-
-# {{{ sub _OverlayAccessible 
 sub _OverlayAccessible {
   {
     TransactionId   => { 'read'=>1, 'public'=>1, 'write' => 0 },
@@ -90,35 +85,6 @@
     Created         => { 'read'=>1, 'auto'=>1, },
   };
 }
-# }}}
-
-# {{{ sub TransactionObj 
-
-=head2 TransactionObj
-
-Returns the transaction object asscoiated with this attachment.
-
-=cut
-
-sub TransactionObj {
-    my $self = shift;
-
-    unless ( $self->{_TransactionObj} ) {
-        $self->{_TransactionObj} = RT::Transaction->new( $self->CurrentUser );
-        $self->{_TransactionObj}->Load( $self->TransactionId );
-    }
-
-    unless ($self->{_TransactionObj}->Id) {
-        $RT::Logger->crit(  "Attachment ". $self->id
-                           ." can't find transaction ". $self->TransactionId
-                           ." which it is ostensibly part of. That's bad");
-    }
-    return $self->{_TransactionObj};
-}
-
-# }}}
-
-# {{{ sub Create 
 
 =head2 Create
 
@@ -221,9 +187,6 @@
     }
 }
 
-# }}}
-
-
 =head2 Import
 
 Create an attachment exactly as specified in the named parameters.
@@ -240,7 +203,59 @@
     return ( $self->SUPER::Create(%args) );
 }
 
-# {{{ sub Content
+=head2 TransactionObj
+
+Returns the transaction object asscoiated with this attachment.
+
+=cut
+
+sub TransactionObj {
+    my $self = shift;
+
+    unless ( $self->{_TransactionObj} ) {
+        $self->{_TransactionObj} = RT::Transaction->new( $self->CurrentUser );
+        $self->{_TransactionObj}->Load( $self->TransactionId );
+    }
+
+    unless ($self->{_TransactionObj}->Id) {
+        $RT::Logger->crit(  "Attachment ". $self->id
+                           ." can't find transaction ". $self->TransactionId
+                           ." which it is ostensibly part of. That's bad");
+    }
+    return $self->{_TransactionObj};
+}
+
+=head2 ParentObj
+
+Returns a parent's L<RT::Attachment> object if this attachment
+has a parent, otherwise returns undef.
+
+=cut
+
+sub ParentObj {
+    my $self = shift;
+    return undef unless $self->Parent;
+
+    my $parent = RT::Attachment->new( $self->CurrentUser );
+    $parent->LoadById( $self->Parent );
+    return $parent;
+}
+
+=head2 Children
+
+Returns an L<RT::Attachments> object which is preloaded with
+all attachments objects with this attachment\'s Id as their
+C<Parent>.
+
+=cut
+
+sub Children {
+    my $self = shift;
+    
+    my $kids = RT::Attachments->new( $self->CurrentUser );
+    $kids->ChildrenOf( $self->Id );
+    return($kids);
+}
 
 =head2 Content
 
@@ -251,17 +266,13 @@
 
 sub Content {
     my $self = shift;
-    return $self->_DecodeLOB( $self->ContentType,
-                              $self->ContentEncoding,
-                              $self->_Value('Content', decode_utf8 => 0),
-                            );
+    return $self->_DecodeLOB(
+        $self->ContentType,
+        $self->ContentEncoding,
+        $self->_Value('Content', decode_utf8 => 0),
+    );
 }
 
-# }}}
-
-
-# {{{ sub OriginalContent
-
 =head2 OriginalContent
 
 Returns the attachment's content as octets before RT's mangling.
@@ -301,11 +312,6 @@
     return $content;
 }
 
-# }}}
-
-
-# {{{ sub OriginalEncoding
-
 =head2 OriginalEncoding
 
 Returns the attachment's original encoding.
@@ -317,49 +323,30 @@
     return $self->GetHeader('X-RT-Original-Encoding');
 }
 
-# }}}
+=head2 ContentLength
 
-=head2 ParentObj
-
-Returns a parent's L<RT::Attachment> object if this attachment
-has a parent, otherwise returns undef.
+Returns length of L</Content> in bytes.
 
 =cut
 
-sub ParentObj {
+sub ContentLength {
     my $self = shift;
-    return undef unless $self->Parent;
-
-    my $parent = RT::Attachment->new( $self->CurrentUser );
-    $parent->LoadById( $self->Parent );
-    return $parent;
-}
-
-# {{{ sub Children
-
-=head2 Children
-
-Returns an L<RT::Attachments> object which is preloaded with
-all attachments objects with this attachment\'s Id as their
-C<Parent>.
 
-=cut
+    return undef unless $self->TransactionObj->CurrentUserCanSee;
 
-sub Children {
-    my $self = shift;
-    
-    my $kids = RT::Attachments->new( $self->CurrentUser );
-    $kids->ChildrenOf( $self->Id );
-    return($kids);
+    my $len = $self->GetHeader('Content-Length');
+    unless ( defined $len ) {
+        use bytes;
+        no warnings 'uninitialized';
+        $len = length($self->Content);
+        $self->SetHeader('Content-Length' => $len);
+    }
+    return $len;
 }
 
-# }}}
-
-# {{{ UTILITIES
-
-# {{{ sub Quote 
-
+=head2 Quote
 
+=cut
 
 sub Quote {
     my $self=shift;
@@ -411,9 +398,27 @@
 
     return (\$body, $max);
 }
-# }}}
 
-# {{{ sub NiceHeaders - pulls out only the most relevant headers
+=head2 ContentAsMIME
+
+Returns MIME entity built from this attachment.
+
+=cut
+
+sub ContentAsMIME {
+    my $self = shift;
+
+    my $entity = new MIME::Entity;
+    $entity->head->add( split /:/, $_, 2 )
+        foreach $self->SplitHeaders;
+
+    use MIME::Body;
+    $entity->bodyhandle(
+        MIME::Body::Scalar->new( $self->OriginalContent )
+    );
+
+    return $entity;
+}
 
 =head2 NiceHeaders
 
@@ -432,9 +437,6 @@
     }
     return $hdrs;
 }
-# }}}
-
-# {{{ sub Headers
 
 =head2 Headers
 
@@ -450,12 +452,7 @@
     return join("\n", $_[0]->SplitHeaders);
 }
 
-
-# }}}
-
-# {{{ sub GetHeader
-
-=head2 GetHeader ( 'Tag')
+=head2 GetHeader $TAG
 
 Returns the value of the header Tag as a string. This bypasses the weeding out
 done in Headers() above.
@@ -475,9 +472,6 @@
     # we found no header. return an empty string
     return undef;
 }
-# }}}
-
-# {{{ sub SetHeader
 
 =head2 SetHeader ( 'Tag', 'Value' )
 
@@ -503,31 +497,6 @@
     $newheader .= "$tag: $_[0]\n" if defined $tag;
     $self->__Set( Field => 'Headers', Value => $newheader);
 }
-# }}}
-
-# {{{ sub _Value 
-
-=head2 _Value
-
-Takes the name of a table column.
-Returns its value as a string, if the user passes an ACL check
-
-=cut
-
-sub _Value {
-    my $self  = shift;
-    my $field = shift;
-
-    #if the field is public, return it.
-    if ( $self->_Accessible( $field, 'public' ) ) {
-        return ( $self->__Value( $field, @_ ) );
-    }
-
-    return undef unless $self->TransactionObj->CurrentUserCanSee;
-    return $self->__Value( $field, @_ );
-}
-
-# }}}
 
 =head2 SplitHeaders
 
@@ -563,24 +532,28 @@
 }
 
 
-sub ContentLength {
-    my $self = shift;
+=head2 _Value
 
-    return undef unless $self->TransactionObj->CurrentUserCanSee;
+Takes the name of a table column.
+Returns its value as a string, if the user passes an ACL check
 
-    my $len = $self->GetHeader('Content-Length');
-    unless ( defined $len ) {
-        use bytes;
-        no warnings 'uninitialized';
-        $len = length($self->Content);
-        $self->SetHeader('Content-Length' => $len);
+=cut
+
+sub _Value {
+    my $self  = shift;
+    my $field = shift;
+
+    #if the field is public, return it.
+    if ( $self->_Accessible( $field, 'public' ) ) {
+        return ( $self->__Value( $field, @_ ) );
     }
-    return $len;
-}
 
-# }}}
+    return undef unless $self->TransactionObj->CurrentUserCanSee;
+    return $self->__Value( $field, @_ );
+}
 
-# Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets.
+# Transactions don't change. by adding this cache congif directiove,
+# we don't lose pathalogically on long tickets.
 sub _CacheConfig {
     {
         'cache_p'       => 1,

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachments_Overlay.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachments_Overlay.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Attachments_Overlay.pm	Tue Mar 27 10:57:22 2007
@@ -72,7 +72,6 @@
 
 use RT::Attachment;
 
-# {{{ sub _Init  
 sub _Init   {
     my $self = shift;
     $self->{'table'} = "Attachments";
@@ -83,10 +82,36 @@
     );
     return $self->SUPER::_Init( @_ );
 }
-# }}}
 
+sub CleanSlate {
+    my $self = shift;
+    delete $self->{_sql_transaction_alias};
+    return $self->SUPER::CleanSlate( @_ );
+}
+
+
+=head2 TransactionAlias
+
+Returns alias for transactions table with applied join condition.
+Always return the same alias, so if you want to build some complex
+or recursive joining then you have to create new alias youself.
+
+=cut
+
+sub TransactionAlias {
+    my $self = shift;
+    return $self->{'_sql_transaction_alias'}
+        if $self->{'_sql_transaction_alias'};
 
-# {{{ sub ContentType
+    my $res = $self->NewAlias('Transactions');
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        FIELD           => 'TransactionId',
+        VALUE           => $res . '.id',
+        QUOTEVALUE      => 0,
+    );
+    return $self->{'_sql_transaction_alias'} = $res;
+}
 
 =head2 ContentType (VALUE => 'text/plain', ENTRYAGGREGATOR => 'OR', OPERATOR => '=' ) 
 
@@ -96,20 +121,16 @@
 
 
 sub ContentType  {
-  my $self = shift;
-  my %args = ( VALUE => 'text/plain',
-	       OPERATOR => '=',
-	       ENTRYAGGREGATOR => 'OR',
-	       @_);
-
-  $self->Limit ( FIELD => 'ContentType',
-		 VALUE => $args{'VALUE'},
-		 OPERATOR => $args{'OPERATOR'},
-		 ENTRYAGGREGATOR => $args{'ENTRYAGGREGATOR'});
-}
-# }}}
+    my $self = shift;
+    my %args = (
+        VALUE           => 'text/plain',
+	    OPERATOR        => '=',
+	    ENTRYAGGREGATOR => 'OR',
+	    @_
+    );
 
-# {{{ sub ChildrenOf 
+    return $self->Limit ( %args, FIELD => 'ContentType' );
+}
 
 =head2 ChildrenOf ID
 
@@ -126,7 +147,65 @@
         VALUE => $attachment
     );
 }
-# }}}
+
+=head2 LimitNotEmpty
+
+Limit result set to attachments with not empty content.
+
+=cut
+
+sub LimitNotEmpty {
+    my $self = shift;
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        FIELD           => 'Content',
+        OPERATOR        => 'IS NOT',
+        VALUE           => 'NULL',
+        QUOTEVALUE      => 0,
+    );
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        FIELD           => 'Content',
+        OPERATOR        => '!=',
+        VALUE           => '',
+    );
+    return;
+}
+
+=head2 LimitByTicket $ticket_id
+
+Limit result set to attachments of a ticket.
+
+=cut
+
+sub LimitByTicket {
+    my $self = shift;
+    my $tid = shift;
+
+    my $transactions = $self->TransactionAlias;
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        ALIAS           => $transactions,
+        FIELD           => 'ObjectType',
+        VALUE           => 'RT::Ticket',
+    );
+
+    my $tickets = $self->NewAlias('Tickets');
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        ALIAS           => $tickets,
+        FIELD           => 'id',
+        VALUE           => $transactions . '.ObjectId',
+        QUOTEVALUE      => 0,
+    );
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        ALIAS           => $tickets,
+        FIELD           => 'EffectiveId',
+        VALUE           => $tid,
+    );
+    return;
+}
 
 # {{{ sub NewItem 
 sub NewItem  {

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Email.pm	Tue Mar 27 10:57:22 2007
@@ -386,7 +386,63 @@
     return 1;
 }
 
+sub ForwardTransaction {
+    my $txn = shift;
+    my %args = ( To => '', Cc => '', Bcc => '', @_ );
+
+    my $main_content = $txn->ContentObj;
+    my $entity = $main_content->ContentAsMIME;
+
+    if ( $main_content->Parent ) {
+        # main content is not top most entity, we shouldn't loose
+        # From/To/Cc headers that are on a top part
+        my $attachments = RT::Attachments->new( $txn->CurrentUser );
+        $attachments->Columns(qw(id Parent TransactionId Headers));
+        $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
+        $attachments->Limit( FIELD => 'Parent', VALUE => 0 );
+        $attachments->Limit( FIELD => 'Parent', OPERATOR => 'IS', VALUE => 'NULL', QUOTEVALUE => 0 );
+        $attachments->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+        my $tmp = $attachments->First;
+        if ( $tmp && $tmp->id ne $main_content->id ) {
+            $entity->make_multipart;
+            $entity->head->add( split /:/, $_, 2 ) foreach $tmp->SplitHeaders;
+            $entity->make_singlepart;
+        }
+    }
 
+    my $attachments = RT::Attachments->new( $txn->CurrentUser );
+    $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
+    $attachments->Limit(
+        FIELD => 'id',
+        OPERATOR => '!=',
+        VALUE => $main_content->id,
+    );
+    $attachments->Limit(
+        FIELD => 'ContentType',
+        OPERATOR => 'NOT STARTSWITH',
+        VALUE => 'multipart/',
+    );
+    $attachments->Limit(
+        FIELD => 'Content',
+        OPERATOR => '!=',
+        VALUE => '',
+    );
+    while ( my $a = $attachments->Next ) {
+        $entity->make_multipart unless $entity->is_multipart;
+        $entity->add_part( $a->ContentAsMIME );
+    }
+
+    my $mail = MIME::Entity->build(
+        To => $args{'To'},
+        Cc => $args{'Cc'},
+        Bcc => $args{'Bcc'},
+        Subject => 'Fwd: '. ($txn->Subject || $txn->Object->Subject ),
+        Type => 'message/rfc822',
+        Encoding => '8bit',
+        Data => $entity->as_string,
+    );
+    SendEmail( entity => $mail );
+}
 
 sub CreateUser {
     my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web.pm	Tue Mar 27 10:57:22 2007
@@ -378,6 +378,16 @@
         RT::Action::SendEmail->SquelchMailTo( RT::Action::SendEmail->SquelchMailTo, @temp_squelch );
     }
 
+    if ( $ARGS{'AttachTickets'} ) {
+        require RT::Action::SendEmail;
+        RT::Action::SendEmail->AttachTickets(
+            RT::Action::SendEmail->AttachTickets,
+            ref $ARGS{'AttachTickets'}?
+                @{ $ARGS{'AttachTickets'} }
+                :( $ARGS{'AttachTickets'} )
+        );
+    }
+
     foreach my $arg (keys %ARGS) {
         next if $arg =~ /-(?:Magic|Category)$/;
 
@@ -520,7 +530,7 @@
 
     # skip updates if the content contains only user's signature
     # and we don't update other fields
-    if( $args{'SkipSignatureOnly'} ) {
+    if ( $args{'SkipSignatureOnly'} ) {
         my $sig = $args{'TicketObj'}->CurrentUser->UserObj->Signature || '';
         $sig =~ s/^\s*|\s*$//g;
         if( $args{ARGSRef}->{'UpdateContent'} =~ /^\s*(--)?\s*\Q$sig\E\s*$/ ) {
@@ -578,6 +588,16 @@
            foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
     }
 
+    if ( $args{ARGSRef}->{'AttachTickets'} ) {
+        require RT::Action::SendEmail;
+        RT::Action::SendEmail->AttachTickets(
+            RT::Action::SendEmail->AttachTickets,
+            ref $args{ARGSRef}->{'AttachTickets'}?
+                @{ $args{ARGSRef}->{'AttachTickets'} }
+                :( $args{ARGSRef}->{'AttachTickets'} )
+        );
+    }
+
     ## TODO: Implement public comments
     if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
         my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Comment(

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Handler.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Handler.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Handler.pm	Tue Mar 27 10:57:22 2007
@@ -190,7 +190,7 @@
 
     # cleanup global squelching of the mails
     require RT::Action::SendEmail;
-    RT::Action::SendEmail->SquelchMailTo( undef );
+    RT::Action::SendEmail->CleanSlate;
 }
 # }}}
 

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Request.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Request.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Interface/Web/Request.pm	Tue Mar 27 10:57:22 2007
@@ -17,9 +17,7 @@
 
     $class->alter_superclass( $new_class );
     $class->valid_params( %{ $new_class->valid_params } );
-    my $self = $class->SUPER::new(@_);
-    return if $self->is_subrequest;
-    return $self;
+    return $class->SUPER::new(@_);
 }
 
 =head2 callback

Modified: rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Transaction_Overlay.pm
==============================================================================
--- rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Transaction_Overlay.pm	(original)
+++ rt/branches/3.7-EXPERIMENTAL-TUNIS/lib/RT/Transaction_Overlay.pm	Tue Mar 27 10:57:22 2007
@@ -358,18 +358,7 @@
     elsif ( $Attachment->ContentType =~ '^multipart/' ) {
         my $plain_parts = $Attachment->Children;
         $plain_parts->ContentType( VALUE => 'text/plain' );
-        $plain_parts->Limit(
-            FIELD => 'Content',
-            OPERATOR => 'IS NOT',
-            VALUE => 'NULL',
-            QUOTEVALUE => 0,
-        );
-        $plain_parts->Limit(
-            ENTRYAGGREGATOR => 'AND',
-            FIELD => 'Content',
-            OPERATOR => '!=',
-            VALUE => '',
-        );
+        $plain_parts->LimitNotEmpty;
 
         # If we actully found a part, return its content
         if ( my $first = $plain_parts->First ) {


More information about the Rt-commit mailing list