[Rt-commit] rt branch, 4.2/web-attachs-processing, created. rt-4.1.6-401-g030919c

Ruslan Zakirov ruz at bestpractical.com
Sat Mar 30 14:39:23 EDT 2013


The branch, 4.2/web-attachs-processing has been created
        at  030919c77c7ebf9ca6ea9441cefb9f025935e283 (commit)

- Log -----------------------------------------------------------------
commit 0fda7623ad3ff4d48c2046f22aae75565db6c9d7
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Jan 22 03:15:04 2011 +0300

    correct indentation in SelfService/Display.html

diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 23d1c91..0a24c7e 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -111,9 +111,7 @@ if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
     };
 }
 
-if ( defined ($id[0]) && $id[0] eq 'new' ) {
-
-    # {{{ Create a new ticket
+if ( ($id[0]||'') eq 'new' ) {
 
     my $Queue = RT::Queue->new( $session{'CurrentUser'} );
     unless ( $Queue->Load( $ARGS{'Queue'} ) ) {
@@ -134,22 +132,22 @@ if ( defined ($id[0]) && $id[0] eq 'new' ) {
 
     unless ( $Ticket->id ) {
         $m->comp( 'Error.html', Why => join( "\n", @results ));
-              $m->abort();
-        }
+        $m->abort();
+    }
 
 
-        # delete temporary storage entry to make WebUI clean
-        unless ( keys %{ $session{'Attachments'} } and $ARGS{'UpdateAttach'} ) {
-            delete $session{'Attachments'};
-        }
+    # delete temporary storage entry to make WebUI clean
+    unless ( keys %{ $session{'Attachments'} } and $ARGS{'UpdateAttach'} ) {
+        delete $session{'Attachments'};
+    }
 
+}
+else {
+    unless ( $Ticket->Load( $id[0] ) ) {
+        $m->comp( 'Error.html',
+            Why => loc( "Couldn't load ticket '[_1]'", $id ) );
+        $m->abort();
     }
-    else {
-        unless ( $Ticket->Load( $id[0] ) ) {
-            $m->comp( 'Error.html',
-                Why => loc( "Couldn't load ticket '[_1]'", $id ) );
-            $m->abort();
-        }
 
     my ( $code, $msg );
 
@@ -189,31 +187,31 @@ if ( defined ($id[0]) && $id[0] eq 'new' ) {
     }
 
 
-    }
+}
 
-    # This code does automatic redirection if any updates happen.
+# This code does automatic redirection if any updates happen.
 
-    unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
-        $m->comp( 'Error.html',
-            Why => loc("No permission to display that ticket") );
-        # XXX: Why abort? then we loose footer //ruz
-        $m->abort();
-    }
+unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
+    $m->comp( 'Error.html',
+        Why => loc("No permission to display that ticket") );
+    # XXX: Why abort? then we loose footer //ruz
+    $m->abort();
+}
 
-    if (@results) {
-        # We've done something, so we need to clear the decks to avoid
-        # resubmission on refresh.
-        # But we need to store Actions somewhere too, so we don't lose them.
-        my $key = Digest::MD5::md5_hex(rand(1024));
-        push @{ $session{"Actions"}->{$key}  ||= [] }, @results;
-        $session{'i'}++;
-        RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."SelfService/Display.html?id=". $Ticket->id."&results=".$key);
-    }
+if (@results) {
+    # We've done something, so we need to clear the decks to avoid
+    # resubmission on refresh.
+    # But we need to store Actions somewhere too, so we don't lose them.
+    my $key = Digest::MD5::md5_hex(rand(1024));
+    push @{ $session{"Actions"}->{$key}  ||= [] }, @results;
+    $session{'i'}++;
+    RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."SelfService/Display.html?id=". $Ticket->id."&results=".$key);
+}
 
-    my $LinkBasicsTitle = $Ticket->CurrentUserHasRight('ModifyTicket')
-                          || $Ticket->CurrentUserHasRight('ReplyToTicket');
-    my $title_box_link = RT->Config->Get('WebPath')."/SelfService/Update.html?id=".$Ticket->Id;
-    $m->callback(CallbackName => 'BeforeDisplay', Ticket => \$Ticket, ARGSRef => \%ARGS, title_box_link => \$title_box_link);
+my $LinkBasicsTitle = $Ticket->CurrentUserHasRight('ModifyTicket')
+                      || $Ticket->CurrentUserHasRight('ReplyToTicket');
+my $title_box_link = RT->Config->Get('WebPath')."/SelfService/Update.html?id=".$Ticket->Id;
+$m->callback(CallbackName => 'BeforeDisplay', Ticket => \$Ticket, ARGSRef => \%ARGS, title_box_link => \$title_box_link);
 </%INIT>
 
 

commit 3cd634935ad73dfa0d181896e6c3f0df9b211627
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Jan 22 03:26:30 2011 +0300

    use may be redirect in SelfService

diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 0a24c7e..4922823 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -198,15 +198,11 @@ unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
     $m->abort();
 }
 
-if (@results) {
-    # We've done something, so we need to clear the decks to avoid
-    # resubmission on refresh.
-    # But we need to store Actions somewhere too, so we don't lose them.
-    my $key = Digest::MD5::md5_hex(rand(1024));
-    push @{ $session{"Actions"}->{$key}  ||= [] }, @results;
-    $session{'i'}++;
-    RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."SelfService/Display.html?id=". $Ticket->id."&results=".$key);
-}
+MaybeRedirectForResults(
+    Actions   => \@results,
+    Path      => '/SelfService/Display.html',
+    Arguments => { 'id' => $Ticket->id },
+);
 
 my $LinkBasicsTitle = $Ticket->CurrentUserHasRight('ModifyTicket')
                       || $Ticket->CurrentUserHasRight('ReplyToTicket');

commit 97add05fa3b09fd9fc7e08b2146b1b1e6c7ae1e1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Jan 22 16:05:59 2011 +0300

    switch over Abort() instead of manual $m->abort

diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 4922823..e084850 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -115,15 +115,11 @@ if ( ($id[0]||'') eq 'new' ) {
 
     my $Queue = RT::Queue->new( $session{'CurrentUser'} );
     unless ( $Queue->Load( $ARGS{'Queue'} ) ) {
-        $m->comp( 'Error.html', Why => loc('Queue not found') );
-        $m->abort;
+        Abort( loc('Queue not found') );
     }
 
     unless ( $Queue->CurrentUserHasRight('CreateTicket') ) {
-        $m->comp( 'Error.html',
-            Why =>
-              loc('You have no permission to create tickets in that queue.') );
-        $m->abort;
+        Abort( loc('You have no permission to create tickets in that queue.') );
     }
 
 
@@ -131,8 +127,7 @@ if ( ($id[0]||'') eq 'new' ) {
     CreateTicket( Attachments => $session{'Attachments'}, %ARGS );
 
     unless ( $Ticket->id ) {
-        $m->comp( 'Error.html', Why => join( "\n", @results ));
-        $m->abort();
+        Abort( join "\n", @results );
     }
 
 
@@ -144,9 +139,7 @@ if ( ($id[0]||'') eq 'new' ) {
 }
 else {
     unless ( $Ticket->Load( $id[0] ) ) {
-        $m->comp( 'Error.html',
-            Why => loc( "Couldn't load ticket '[_1]'", $id ) );
-        $m->abort();
+        Abort( loc( "Couldn't load ticket '[_1]'", $id ) );
     }
 
     my ( $code, $msg );
@@ -192,10 +185,7 @@ else {
 # This code does automatic redirection if any updates happen.
 
 unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
-    $m->comp( 'Error.html',
-        Why => loc("No permission to display that ticket") );
-    # XXX: Why abort? then we loose footer //ruz
-    $m->abort();
+    Abort( loc("No permission to display that ticket") );
 }
 
 MaybeRedirectForResults(

commit ed73ef0e1f0583e4c84b68d2668c1a17a8efb1cc
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Mar 4 11:36:00 2011 +0300

    name a few forms to easier test writing

diff --git a/share/html/SelfService/Create.html b/share/html/SelfService/Create.html
index df2c45d..e688dd8 100644
--- a/share/html/SelfService/Create.html
+++ b/share/html/SelfService/Create.html
@@ -48,7 +48,7 @@
 <& Elements/Header, Title => loc("Create a ticket") &>
 
 <& /Elements/ListActions, actions => \@results &>
-<form action="Create.html" method="post" enctype="multipart/form-data">
+<form action="Create.html" method="post" enctype="multipart/form-data" name="TicketCreate">
 <input type="hidden" class="hidden" name="id" value="new" />
 
 <table width="100%">
diff --git a/share/html/SelfService/Update.html b/share/html/SelfService/Update.html
index 4391114..5b3cfb1 100644
--- a/share/html/SelfService/Update.html
+++ b/share/html/SelfService/Update.html
@@ -51,7 +51,7 @@
 
 % $m->callback(CallbackName => 'BeforeForm', %ARGS, ARGSRef => \%ARGS, Ticket => $Ticket );
 
-<form action="Display.html" method="post" enctype="multipart/form-data">
+<form action="Display.html" method="post" enctype="multipart/form-data" name="TicketUpdate">
 <input type="hidden" class="hidden" name="UpdateType" value="response" />
 <input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
 <table width="100%">

commit 00412d0211e4684e2578c360adefa8e435438142
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Mar 4 11:39:02 2011 +0300

    get rid of unint warning (self service hits the case)

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 0e1a70e..84569b3 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -2017,7 +2017,7 @@ sub ProcessUpdateMessage {
         return;
     }
 
-    if ( $args{ARGSRef}->{'UpdateSubject'} eq ($args{'TicketObj'}->Subject || '') ) {
+    if ( ($args{ARGSRef}->{'UpdateSubject'}||'') eq ($args{'TicketObj'}->Subject || '') ) {
         $args{ARGSRef}->{'UpdateSubject'} = undef;
     }
 

commit 33a236faaa831527881ad167047a6ade3c99ec11
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Mar 30 18:40:51 2013 +0400

    ProcessAttachments in RT::Interface::Web
    
    * shrink code
    * move some of repeated code into CreateTicket
      and ProcessUpdateMessage

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 84569b3..d75ea4c 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1864,18 +1864,21 @@ sub CreateTicket {
         Interface => RT::Interface::Web::MobileClient() ? 'Mobile' : 'Web',
     );
 
-    if ( $ARGS{'Attachments'} ) {
-        my $rv = $MIMEObj->make_multipart;
-        $RT::Logger->error("Couldn't make multipart message")
-            if !$rv || $rv !~ /^(?:DONE|ALREADY)$/;
+    my @attachments;
+    if ( my $tmp = $session{'Attachments'} ) {
+        push @attachments, grep $_, values %$tmp;
 
-        foreach ( values %{ $ARGS{'Attachments'} } ) {
-            unless ($_) {
-                $RT::Logger->error("Couldn't add empty attachemnt");
-                next;
-            }
-            $MIMEObj->add_part($_);
-        }
+        delete $session{'Attachments'}
+            unless $ARGS{'KeepAttachments'};
+        $session{'Attachments'} = $session{'Attachments'}
+            if @attachments;
+    }
+    if ( $ARGS{'Attachments'} ) {
+        push @attachments, grep $_, values %{ $ARGS{'Attachments'} };
+    }
+    if ( @attachments ) {
+        $MIMEObj->make_multipart;
+        $MIMEObj->add_part( $_ ) foreach @attachments;
     }
 
     for my $argument (qw(Encrypt Sign)) {
@@ -1991,10 +1994,17 @@ sub ProcessUpdateMessage {
         @_
     );
 
-    if ( $args{ARGSRef}->{'UpdateAttachments'}
-        && !keys %{ $args{ARGSRef}->{'UpdateAttachments'} } )
-    {
-        delete $args{ARGSRef}->{'UpdateAttachments'};
+    my @attachments;
+    if ( my $tmp = $session{'Attachments'} ) {
+        push @attachments, grep $_, values %$tmp;
+
+        delete $session{'Attachments'}
+            unless $args{'KeepAttachments'};
+        $session{'Attachments'} = $session{'Attachments'}
+            if @attachments;
+    }
+    if ( $args{ARGSRef}{'UpdateAttachments'} ) {
+        push @attachments, grep $_, values %{ $args{ARGSRef}{'UpdateAttachments'} };
     }
 
     # Strip the signature
@@ -2008,7 +2018,7 @@ sub ProcessUpdateMessage {
     # If, after stripping the signature, we have no message, move the
     # UpdateTimeWorked into adjusted TimeWorked, so that a later
     # ProcessBasics can deal -- then bail out.
-    if (    not $args{ARGSRef}->{'UpdateAttachments'}
+    if (    not @attachments
         and not length $args{ARGSRef}->{'UpdateContent'} )
     {
         if ( $args{ARGSRef}->{'UpdateTimeWorked'} ) {
@@ -2045,9 +2055,9 @@ sub ProcessUpdateMessage {
         );
     }
 
-    if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
+    if ( @attachments ) {
         $Message->make_multipart;
-        $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
+        $Message->add_part( $_ ) foreach @attachments;
     }
 
     if ( $args{ARGSRef}->{'AttachTickets'} ) {
@@ -2134,7 +2144,36 @@ sub _ProcessUpdateMessageRecipients {
     }
 }
 
+sub ProcessAttachments {
+    my %args = (
+        ARGSRef => {},
+        @_
+    );
+
+    my $update_session = 0;
+
+    # deal with deleting uploaded attachments
+    if ( my $del = $args{'ARGSRef'}{'DeleteAttach'} ) {
+        delete $session{'Attachments'}{ $_ }
+            foreach ref $del? @$del : ($del);
+
+        $update_session = 1;
+    }
 
+    # store the uploaded attachment in session
+    my $new = $args{'ARGSRef'}{'Attach'};
+    if ( defined $new && length $new ) {
+        my $attachment = MakeMIMEEntity(
+            AttachmentFieldName => 'Attach'
+        );
+
+        my $file_path = Encode::decode_utf8("$new");
+        $session{'Attachments'}{ $file_path } = $attachment;
+
+        $update_session = 1;
+    }
+    $session{'Attachments'} = $session{'Attachments'} if $update_session;
+}
 
 =head2 MakeMIMEEntity PARAMHASH
 
diff --git a/share/html/Search/Bulk.html b/share/html/Search/Bulk.html
index aa5a725..a45cfab 100644
--- a/share/html/Search/Bulk.html
+++ b/share/html/Search/Bulk.html
@@ -226,31 +226,8 @@ map ( $ARGS{$_} =~ /^$/ && ( delete $ARGS{$_} ), keys %ARGS );
 
 my (@results);
 
-# deal with deleting uploaded attachments
-foreach my $key (keys %ARGS) {
-    if ($key =~ m/^DeleteAttach-(.+)$/) {
-        delete $session{'Attachments'}{$1};
-    }
-    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
-}
 
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
-
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{$session{'Attachments'} || {}},
-        $file_path => $attachment,
-    };
-}
-
-# delete temporary storage entry to make WebUI clean
-unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) {
-    delete $session{'Attachments'};
-}
+ProcessAttachments( ARGSRef => \%ARGS );
 
 $Page ||= 1;
 
@@ -300,9 +277,6 @@ my %queues;
 $Tickets->RedoSearch();
 
 unless ( $ARGS{'AddMoreAttach'} ) {
-    # Add session attachments if any to be processed by ProcessUpdateMessage
-    $ARGS{'UpdateAttachments'} = $session{'Attachments'} if ( $session{'Attachments'} );
-
     while ( my $Ticket = $Tickets->Next ) {
         next unless ( $ARGS{ "UpdateTicket" . $Ticket->Id } );
 
@@ -311,9 +285,10 @@ unless ( $ARGS{'AddMoreAttach'} ) {
         $queues{ $Ticket->QueueObj->Id }++;
 
         my @updateresults = ProcessUpdateMessage(
-                TicketObj => $Ticket,
-                ARGSRef   => \%ARGS,
-            );
+            TicketObj       => $Ticket,
+            ARGSRef         => \%ARGS,
+            KeepAttachments => 1,
+        );
 
         #Update the basics.
         my @basicresults =
@@ -383,7 +358,6 @@ unless ( $ARGS{'AddMoreAttach'} ) {
         @results = ( @results, @tempresults );
     }
 
-    # Cleanup WebUI
     delete $session{'Attachments'};
 }
 
diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index e084850..81ffadf 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -96,20 +96,7 @@ my @id = ( ref $id eq 'ARRAY' ) ? @{$id} : ($id);
 
 my $Ticket = RT::Ticket->new( $session{'CurrentUser'} );
 
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    $session{'Attachments'} = {} unless defined $session{'Attachments'};
-
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
-
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{ $session{'Attachments'} || {} },
-        $file_path => $attachment,
-    };
-}
+ProcessAttachments( ARGSRef => \%ARGS );
 
 if ( ($id[0]||'') eq 'new' ) {
 
@@ -123,63 +110,34 @@ if ( ($id[0]||'') eq 'new' ) {
     }
 
 
-    ( $Ticket, @results ) =
-    CreateTicket( Attachments => $session{'Attachments'}, %ARGS );
+    ( $Ticket, @results ) = CreateTicket( %ARGS );
 
     unless ( $Ticket->id ) {
         Abort( join "\n", @results );
     }
-
-
-    # delete temporary storage entry to make WebUI clean
-    unless ( keys %{ $session{'Attachments'} } and $ARGS{'UpdateAttach'} ) {
-        delete $session{'Attachments'};
-    }
-
 }
 else {
     unless ( $Ticket->Load( $id[0] ) ) {
         Abort( loc( "Couldn't load ticket '[_1]'", $id ) );
     }
 
-    my ( $code, $msg );
-
-
-    if (
-        $session{'Attachments'}
-        || ( defined $ARGS{'UpdateContent'}
-            && $ARGS{'UpdateContent'} ne ''
-            && $ARGS{'UpdateContent'} ne "-- \n"
-            . $session{'CurrentUser'}->UserObj->Signature )
-      )
-    {
-        $ARGS{UpdateAttachments} = $session{'Attachments'};
-    }
     push @results, ProcessUpdateMessage(
         ARGSRef   => \%ARGS,
         TicketObj => $Ticket
     );
-            delete $session{'Attachments'};
-
-    # delete temporary storage entry to make WebUI clean
-    unless ( keys %{ $session{'Attachments'} } and $ARGS{'UpdateAttach'} ) {
-        delete $session{'Attachments'};
-    }
 
     my @cfupdates = ProcessObjectCustomFieldUpdates(Object => $Ticket, ARGSRef => \%ARGS);
     push (@results, @cfupdates);
 
-
     #Update the status
     if (    ( defined $ARGS{'Status'} )
         and $ARGS{'Status'}
         and ( $ARGS{'Status'} ne $Ticket->Status ) )
     {
-        ( $code, $msg ) = $Ticket->SetStatus( $ARGS{'Status'} );
+        my ($code, $msg) = $Ticket->SetStatus( $ARGS{'Status'} );
         push @results, "$msg";
     }
 
-
 }
 
 # This code does automatic redirection if any updates happen.
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 1c78de4..8d4cec8 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -375,31 +375,8 @@ $QueueObj->Disabled && Abort(loc("Cannot create tickets in a disabled queue."));
 
 my $ticket = RT::Ticket->new($session{'CurrentUser'}); # empty ticket object
 
-# deal with deleting uploaded attachments
-foreach my $key (keys %ARGS) {
-    if ($key =~ m/^DeleteAttach-(.+)$/) {
-        delete $session{'Attachments'}{$1};
-    }
-    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
-}
-
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
 
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{$session{'Attachments'} || {}},
-        $file_path => $attachment,
-    };
-}
-
-# delete temporary storage entry to make WebUI clean
-unless (keys %{$session{'Attachments'}} and $ARGS{'id'} eq 'new') {
-    delete $session{'Attachments'};
-}
+ProcessAttachments( ARGSRef => \%ARGS );
 
 my $checks_failure = 0;
 
diff --git a/share/html/Ticket/Display.html b/share/html/Ticket/Display.html
index 205f637..2711549 100644
--- a/share/html/Ticket/Display.html
+++ b/share/html/Ticket/Display.html
@@ -132,10 +132,7 @@ if ($ARGS{'id'} eq 'new') {
         Abort('You have no permission to create tickets in that queue.');
     }
 
-    ($TicketObj, @Actions) = CreateTicket(
-        Attachments => delete $session{'Attachments'},
-        %ARGS,
-    );
+    ($TicketObj, @Actions) = CreateTicket( %ARGS );
     unless ( $TicketObj->CurrentUserHasRight('ShowTicket') ) {
         Abort("No permission to view newly created ticket #".$TicketObj->id.".");
     }
@@ -165,14 +162,11 @@ if ($ARGS{'id'} eq 'new') {
                 ARGSRef => \%ARGS, 
                 Actions => \@Actions);
         
-        $ARGS{UpdateAttachments} = $session{'Attachments'};
-        push @Actions,
-            ProcessUpdateMessage(
+        push @Actions, ProcessUpdateMessage(
             ARGSRef   => \%ARGS,
             Actions   => \@Actions,
             TicketObj => $TicketObj,
-            );
-        delete $session{'Attachments'};
+        );
 
         #Process status updates
         push @Actions, ProcessTicketWatchers(ARGSRef => \%ARGS, TicketObj => $TicketObj );
diff --git a/share/html/Ticket/Elements/AddAttachments b/share/html/Ticket/Elements/AddAttachments
index eefa38b..55a693d 100644
--- a/share/html/Ticket/Elements/AddAttachments
+++ b/share/html/Ticket/Elements/AddAttachments
@@ -45,13 +45,13 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-% if (exists $session{'Attachments'}) {
+% if ( $attachments ) {
 <tr><td class="label"><&|/l&>Attached file</&>:</td>
 <td>
 <&|/l&>Check box to delete</&><br />
-% foreach my $attach_name (keys %{$session{'Attachments'}}) {
-<input type="checkbox" class="checkbox" id="DeleteAttach-<%$attach_name%>" name="DeleteAttach-<%$attach_name%>" value="1" />
-<label for="DeleteAttach-<%$attach_name%>"><%$attach_name%></label>
+% foreach my $attach_name ( keys %$attachments ) {
+<input type="checkbox" class="checkbox" name="DeleteAttach" value="<% $attach_name %>" id="DeleteAttach-<%$attach_name%>" />
+<label for="DeleteAttach-<%$attach_name%>"><% $attach_name %></label>
 <br />
 % } # end of foreach
 </td>
@@ -61,3 +61,9 @@
 <tr><td class="label"><&|/l&>Attach</&>:</td><td><input name="Attach" type="file" /><input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" /><input type="hidden" class="hidden" name="UpdateAttach" value="1" />
 </td></tr>
 % $m->callback( %ARGS, CallbackName => 'End' );
+<%INIT>
+my $attachments;
+if ( exists $session{'Attachments'} && keys %{ $session{'Attachments'} } ) {
+    $attachments = $session{'Attachments'};
+}
+</%INIT>
diff --git a/share/html/Ticket/ModifyAll.html b/share/html/Ticket/ModifyAll.html
index cb2c063..433baa8 100644
--- a/share/html/Ticket/ModifyAll.html
+++ b/share/html/Ticket/ModifyAll.html
@@ -146,32 +146,8 @@ $CanRespond = 1 if ( $Ticket->CurrentUserHasRight('ReplyToTicket') or
 $CanComment = 1 if ( $Ticket->CurrentUserHasRight('CommentOnTicket') or
                      $Ticket->CurrentUserHasRight('ModifyTicket') );
 
-# deal with deleting uploaded attachments
-foreach my $key (keys %ARGS) {
-    if ($key =~ m/^DeleteAttach-(.+)$/) {
-        delete $session{'Attachments'}{$1};
-    }
-    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
-}
-
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
-
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{$session{'Attachments'} || {}},
-        $file_path => $attachment,
-    };
-}
-
-# delete temporary storage entry to make WebUI clean
-unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) {
-    delete $session{'Attachments'};
-}
 
+ProcessAttachments( ARGSRef => \%ARGS );
 
 my @results;
 my $skip_update = 0;
@@ -208,13 +184,7 @@ unless ($skip_update or $OnlySearchForPeople or $OnlySearchForGroup or $ARGS{'Ad
     push @results, ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS);
     push @results, ProcessObjectCustomFieldUpdates( Object => $Ticket, ARGSRef => \%ARGS);
     push @results, ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS);
-    
-    # Add session attachments if any to be processed by ProcessUpdateMessage
-    $ARGS{'UpdateAttachments'} = $session{'Attachments'} if ( $session{'Attachments'} );
     push @results, ProcessUpdateMessage( TicketObj => $Ticket, ARGSRef=>\%ARGS );
-    # Cleanup WebUI
-    delete $session{'Attachments'};
-
     push @results, ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS );
     push @results, ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => \%ARGS);
 
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index 706a62a..ffce694 100644
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -241,31 +241,7 @@ $CanComment = 1 if ( $TicketObj->CurrentUserHasRight('CommentOnTicket') or
                      $TicketObj->CurrentUserHasRight('ModifyTicket') ); 
 
 
-# deal with deleting uploaded attachments
-foreach my $key (keys %ARGS) {
-    if ($key =~ m/^DeleteAttach-(.+)$/) {
-        delete $session{'Attachments'}{$1};
-    }
-    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
-}
-
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
-
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{$session{'Attachments'} || {}},
-        $file_path => $attachment,
-    };
-}
-
-# delete temporary storage entry to make WebUI clean
-unless (keys %{$session{'Attachments'}} and $ARGS{'UpdateAttach'}) {
-    delete $session{'Attachments'};
-}
+ProcessAttachments( ARGSRef => \%ARGS );
 
 my $gnupg_widget = $m->comp('/Elements/GnuPG/SignEncryptWidget:new', Arguments => \%ARGS );
 $m->comp( '/Elements/GnuPG/SignEncryptWidget:Process',
diff --git a/share/html/m/ticket/create b/share/html/m/ticket/create
index 4c0fa9e..ff1f1e9 100644
--- a/share/html/m/ticket/create
+++ b/share/html/m/ticket/create
@@ -146,31 +146,8 @@ $m->callback( QueueObj => $QueueObj, title => \$title, results => \@results, ARG
 
 $QueueObj->Disabled && Abort(loc("Cannot create tickets in a disabled queue."));
 
-# deal with deleting uploaded attachments
-foreach my $key (keys %ARGS) {
-    if ($key =~ m/^DeleteAttach-(.+)$/) {
-        delete $session{'Attachments'}{$1};
-    }
-    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
-}
-
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
 
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{$session{'Attachments'} || {}},
-        $file_path => $attachment,
-    };
-}
-
-# delete temporary storage entry to make WebUI clean
-unless (keys %{$session{'Attachments'}} and $ARGS{'id'} eq 'new') {
-    delete $session{'Attachments'};
-}
+ProcessAttachments( ARGSRef => \%ARGS );
 
 my $checks_failure = 0;
 
@@ -326,14 +303,14 @@ $showrows->(
     &>
 <& /Ticket/Elements/EditTransactionCustomFields, %ARGS, QueueObj => $QueueObj &>
 
-% if (exists $session{'Attachments'}) {
+% if ( my $attachments = $session{'Attachments'} ) {
 
 <%loc("Attached file") %>
 
 <%loc("Check box to delete")%><br />
-% foreach my $attach_name (keys %{$session{'Attachments'}}) {
-<input type="checkbox" class="checkbox" id="DeleteAttach-<%$attach_name%>" name="DeleteAttach-<%$attach_name%>" value="1" />
-<label for="DeleteAttach-<%$attach_name%>"><%$attach_name%></label><br />
+% foreach my $attach_name ( keys %$attachments ) {
+<input type="checkbox" class="checkbox" name="DeleteAttach" value="<% $attach_name %>" id="DeleteAttach-<% $attach_name %>" />
+<label for="DeleteAttach-<% $attach_name %>"><% $attach_name %></label><br />
 % } # end of foreach
 
 
diff --git a/share/html/m/ticket/reply b/share/html/m/ticket/reply
index 290b2d1..2a6eff6 100644
--- a/share/html/m/ticket/reply
+++ b/share/html/m/ticket/reply
@@ -194,33 +194,7 @@ $CanComment = 1 if ( $t->CurrentUserHasRight('CommentOnTicket') or
                      $t->CurrentUserHasRight('ModifyTicket') ); 
 
 
-# deal with deleting uploaded attachments
-foreach my $key (keys %ARGS) {
-    if ($key =~ m/^DeleteAttach-(.+)$/) {
-        delete $session{'Attachments'}{$1};
-    }
-    $session{'Attachments'} = { %{$session{'Attachments'} || {}} };
-}
-
-# store the uploaded attachment in session
-if ( defined $ARGS{'Attach'} && length $ARGS{'Attach'} ) { # attachment?
-    my $attachment = MakeMIMEEntity(
-        AttachmentFieldName => 'Attach'
-    );
-
-    my $file_path = Encode::decode_utf8("$ARGS{'Attach'}");
-    $session{'Attachments'} = {
-        %{$session{'Attachments'} || {}},
-        $file_path => $attachment,
-    };
-}
-
-# delete temporary storage entry to make WebUI clean
-unless ( keys %{ $session{'Attachments'} }
-    && ( exists $ARGS{'AddMoreAttach'} || exists $ARGS{'SubmitTicket'} ) )
-{
-    delete $session{'Attachments'};
-}
+ProcessAttachments( ARGSRef => \%ARGS );
 
 # check email addresses for RT's
 {
diff --git a/share/html/m/ticket/show b/share/html/m/ticket/show
index d8d5bc2..480664e 100644
--- a/share/html/m/ticket/show
+++ b/share/html/m/ticket/show
@@ -69,10 +69,7 @@ if ($ARGS{'id'} eq 'new') {
         Abort('You have no permission to create tickets in that queue.');
     }
 
-    ($Ticket, @Actions) = CreateTicket(
-        Attachments => delete $session{'Attachments'},
-        %ARGS,
-    );
+    ($Ticket, @Actions) = CreateTicket( %ARGS );
     unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
         Abort("No permission to view newly created ticket #".$Ticket->id.".");
     }
@@ -95,14 +92,12 @@ if ($ARGS{'id'} eq 'new') {
             ARGSRef => \%ARGS, 
             Actions => \@Actions);
     
-    $ARGS{UpdateAttachments} = $session{'Attachments'};
     push @Actions,
         ProcessUpdateMessage(
         ARGSRef   => \%ARGS,
         Actions   => \@Actions,
         TicketObj => $Ticket,
         );
-    delete $session{'Attachments'};
 
     #Process status updates
     push @Actions, ProcessTicketWatchers(ARGSRef => \%ARGS, TicketObj => $Ticket );

commit 82aeea174c25e61ca8f442ad73d0b697f13710ec
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Mar 30 22:22:25 2013 +0400

    add token to action pages to differ them
    
    allows us to isolate attachments from different
    actions, so people can do multiple things at the
    same time in different browser's windows.

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index d75ea4c..31a43b2 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -1488,7 +1488,7 @@ sub ExpandCSRFToken {
     if ($data->{attach}) {
         my $filename = $data->{attach}{filename};
         my $mime     = $data->{attach}{mime};
-        $HTML::Mason::Commands::session{'Attachments'}{$filename}
+        $HTML::Mason::Commands::session{'Attachments'}{$ARGS->{'Token'}||''}{$filename}
             = $mime;
     }
 
@@ -1865,10 +1865,10 @@ sub CreateTicket {
     );
 
     my @attachments;
-    if ( my $tmp = $session{'Attachments'} ) {
+    if ( my $tmp = $session{'Attachments'}{ $ARGS{'Token'} || '' } ) {
         push @attachments, grep $_, values %$tmp;
 
-        delete $session{'Attachments'}
+        delete $session{'Attachments'}{ $ARGS{'Token'} || '' }
             unless $ARGS{'KeepAttachments'};
         $session{'Attachments'} = $session{'Attachments'}
             if @attachments;
@@ -1995,10 +1995,10 @@ sub ProcessUpdateMessage {
     );
 
     my @attachments;
-    if ( my $tmp = $session{'Attachments'} ) {
+    if ( my $tmp = $session{'Attachments'}{ $args{'ARGSRef'}{'Token'} || '' } ) {
         push @attachments, grep $_, values %$tmp;
 
-        delete $session{'Attachments'}
+        delete $session{'Attachments'}{ $args{'ARGSRef'}{'Token'} || '' }
             unless $args{'KeepAttachments'};
         $session{'Attachments'} = $session{'Attachments'}
             if @attachments;
@@ -2147,14 +2147,17 @@ sub _ProcessUpdateMessageRecipients {
 sub ProcessAttachments {
     my %args = (
         ARGSRef => {},
+        Token   => '',
         @_
     );
 
+    my $token = $args{'Token'};
+
     my $update_session = 0;
 
     # deal with deleting uploaded attachments
     if ( my $del = $args{'ARGSRef'}{'DeleteAttach'} ) {
-        delete $session{'Attachments'}{ $_ }
+        delete $session{'Attachments'}{ $token }{ $_ }
             foreach ref $del? @$del : ($del);
 
         $update_session = 1;
@@ -2168,7 +2171,7 @@ sub ProcessAttachments {
         );
 
         my $file_path = Encode::decode_utf8("$new");
-        $session{'Attachments'}{ $file_path } = $attachment;
+        $session{'Attachments'}{ $token }{ $file_path } = $attachment;
 
         $update_session = 1;
     }
diff --git a/share/html/Search/Bulk.html b/share/html/Search/Bulk.html
index a45cfab..8bb4198 100644
--- a/share/html/Search/Bulk.html
+++ b/share/html/Search/Bulk.html
@@ -50,7 +50,7 @@
 
 <& /Elements/ListActions, actions => \@results &>
 <form method="post" action="<% RT->Config->Get('WebPath') %>/Search/Bulk.html" enctype="multipart/form-data" name="BulkUpdate" id="BulkUpdate">
-% foreach my $var (qw(Query Format OrderBy Order Rows Page SavedChartSearchId)) {
+% foreach my $var (qw(Query Format OrderBy Order Rows Page SavedChartSearchId Token)) {
 <input type="hidden" class="hidden" name="<%$var%>" value="<%$ARGS{$var} || ''%>" />
 %}
 <& /Elements/CollectionList, 
@@ -226,8 +226,9 @@ map ( $ARGS{$_} =~ /^$/ && ( delete $ARGS{$_} ), keys %ARGS );
 
 my (@results);
 
+$ARGS{'Token'} ||= $Token ||= Digest::MD5::md5_hex( rand(1024) );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( ARGSRef => \%ARGS, Token => $Token );
 
 $Page ||= 1;
 
@@ -358,7 +359,7 @@ unless ( $ARGS{'AddMoreAttach'} ) {
         @results = ( @results, @tempresults );
     }
 
-    delete $session{'Attachments'};
+    delete $session{'Attachments'}{ $Token };
 }
 
 my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );
@@ -367,6 +368,7 @@ $TxnCFs->LimitToGlobalOrObjectId( sort keys %queues );
 
 </%INIT>
 <%args>
+$Token => undef
 $Format => undef
 $Page => 1
 $Rows => undef
diff --git a/share/html/SelfService/Display.html b/share/html/SelfService/Display.html
index 81ffadf..3a5ee5a 100644
--- a/share/html/SelfService/Display.html
+++ b/share/html/SelfService/Display.html
@@ -96,7 +96,7 @@ my @id = ( ref $id eq 'ARRAY' ) ? @{$id} : ($id);
 
 my $Ticket = RT::Ticket->new( $session{'CurrentUser'} );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( Token => $Token, ARGSRef => \%ARGS );
 
 if ( ($id[0]||'') eq 'new' ) {
 
@@ -161,4 +161,5 @@ $m->callback(CallbackName => 'BeforeDisplay', Ticket => \$Ticket, ARGSRef => \%A
 
 <%ARGS>
 $id => undef
+$Token => ''
 </%ARGS>
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 8d4cec8..b75f65a 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -54,6 +54,7 @@
 
 <form action="<% RT->Config->Get('WebPath') %>/Ticket/Create.html" method="post" enctype="multipart/form-data" name="TicketCreate">
   <input type="hidden" class="hidden" name="id" value="new" />
+  <input type="hidden" class="hidden" name="Token" value="<% $Token %>" />
   
 % $m->callback( CallbackName => 'FormStart', QueueObj => $QueueObj, ARGSRef => \%ARGS );
 
@@ -375,8 +376,9 @@ $QueueObj->Disabled && Abort(loc("Cannot create tickets in a disabled queue."));
 
 my $ticket = RT::Ticket->new($session{'CurrentUser'}); # empty ticket object
 
+$ARGS{'Token'} ||= $Token ||= Digest::MD5::md5_hex( rand(1024) );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( ARGSRef => \%ARGS, Token => $Token );
 
 my $checks_failure = 0;
 
@@ -443,6 +445,7 @@ PageMenu->child( details => raw_html =>  q[<a href="#details" onclick="return sw
 </%INIT>
 
 <%ARGS>
+$Token => undef
 $DependsOn => undef
 $DependedOnBy => undef
 $MemberOf => undef
diff --git a/share/html/Ticket/Elements/AddAttachments b/share/html/Ticket/Elements/AddAttachments
index 55a693d..d86ef5b 100644
--- a/share/html/Ticket/Elements/AddAttachments
+++ b/share/html/Ticket/Elements/AddAttachments
@@ -61,9 +61,12 @@
 <tr><td class="label"><&|/l&>Attach</&>:</td><td><input name="Attach" type="file" /><input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" /><input type="hidden" class="hidden" name="UpdateAttach" value="1" />
 </td></tr>
 % $m->callback( %ARGS, CallbackName => 'End' );
+<%ARGS>
+$Token => ''
+</%ARGS>
 <%INIT>
 my $attachments;
-if ( exists $session{'Attachments'} && keys %{ $session{'Attachments'} } ) {
-    $attachments = $session{'Attachments'};
+if ( exists $session{'Attachments'}{ $Token } && keys %{ $session{'Attachments'}{ $Token } } ) {
+    $attachments = $session{'Attachments'}{ $Token };
 }
 </%INIT>
diff --git a/share/html/Ticket/ModifyAll.html b/share/html/Ticket/ModifyAll.html
index 433baa8..5e93c3b 100644
--- a/share/html/Ticket/ModifyAll.html
+++ b/share/html/Ticket/ModifyAll.html
@@ -54,6 +54,7 @@
 <form method="post" action="ModifyAll.html" name="TicketModifyAll" enctype="multipart/form-data">
 % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS );
 <input type="hidden" class="hidden" name="id" value="<%$Ticket->Id%>" />
+<input type="hidden" class="hidden" name="Token" value="<% $Token %>" />
 
 <&| /Widgets/TitleBox, title => loc('Modify ticket # [_1]', $Ticket->Id), class=>'ticket-info-basics' &>
 <& Elements/EditBasics, TicketObj => $Ticket, defaults => \%ARGS &>
@@ -146,8 +147,9 @@ $CanRespond = 1 if ( $Ticket->CurrentUserHasRight('ReplyToTicket') or
 $CanComment = 1 if ( $Ticket->CurrentUserHasRight('CommentOnTicket') or
                      $Ticket->CurrentUserHasRight('ModifyTicket') );
 
+$ARGS{'Token'} ||= $Token ||= Digest::MD5::md5_hex( rand(1024) );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( ARGSRef => \%ARGS, Token => $Token );
 
 my @results;
 my $skip_update = 0;
@@ -212,6 +214,7 @@ unless ($Ticket->CurrentUserHasRight('ShowTicket')) {
 
 
 <%ARGS>
+$Token => undef
 $OnlySearchForPeople => undef
 $OnlySearchForGroup => undef
 $UserField => undef
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index ffce694..56fbdc1 100644
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -57,6 +57,7 @@
 <input type="hidden" class="hidden" name="QuoteTransaction" value="<% $ARGS{QuoteTransaction}||'' %>" />
 <input type="hidden" class="hidden" name="DefaultStatus" value="<% $DefaultStatus ||''%>" />
 <input type="hidden" class="hidden" name="Action" value="<% $ARGS{Action}||'' %>" />
+<input type="hidden" class="hidden" name="Token" value="<% $Token %>" />
 
 <& /Elements/GnuPG/SignEncryptWidget:ShowIssues, self => $gnupg_widget &>
 
@@ -240,8 +241,9 @@ $CanRespond = 1 if ( $TicketObj->CurrentUserHasRight('ReplyToTicket') or
 $CanComment = 1 if ( $TicketObj->CurrentUserHasRight('CommentOnTicket') or
                      $TicketObj->CurrentUserHasRight('ModifyTicket') ); 
 
+$ARGS{'Token'} ||= $Token ||= Digest::MD5::md5_hex( rand(1024) );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( ARGSRef => \%ARGS, Token => $Token );
 
 my $gnupg_widget = $m->comp('/Elements/GnuPG/SignEncryptWidget:new', Arguments => \%ARGS );
 $m->comp( '/Elements/GnuPG/SignEncryptWidget:Process',
@@ -304,4 +306,5 @@ if ( !$checks_failure && !$skip_update && exists $ARGS{SubmitTicket} ) {
 $id => undef
 $Action => undef
 $DefaultStatus => undef
+$Token => undef
 </%ARGS>
diff --git a/share/html/m/ticket/create b/share/html/m/ticket/create
index ff1f1e9..261b159 100644
--- a/share/html/m/ticket/create
+++ b/share/html/m/ticket/create
@@ -46,6 +46,7 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%ARGS>
+$Token => ''
 $QuoteTransaction => undef
 $CloneTicket => undef
 </%ARGS>
@@ -146,8 +147,9 @@ $m->callback( QueueObj => $QueueObj, title => \$title, results => \@results, ARG
 
 $QueueObj->Disabled && Abort(loc("Cannot create tickets in a disabled queue."));
 
+$ARGS{'Token'} ||= $Token ||= Digest::MD5::md5_hex( rand(1024) );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( ARGSRef => \%ARGS, Token => $Token );
 
 my $checks_failure = 0;
 
@@ -215,6 +217,7 @@ if ((!exists $ARGS{'AddMoreAttach'}) and (defined($ARGS{'id'}) and $ARGS{'id'} e
 <& /Elements/ListActions, actions => \@results  &>
 <form action="<% RT->Config->Get('WebPath') %>/m/ticket/create" method="post" enctype="multipart/form-data" name="TicketCreate" id="ticket-create">
 <input type="hidden" class="hidden" name="id" value="new" />
+<input type="hidden" class="hidden" name="Token" value="<% $Token %>" />
 % $m->callback( CallbackName => 'FormStart', QueueObj => $QueueObj, ARGSRef => \%ARGS );
 % if ($gnupg_widget) {
 <& /Elements/GnuPG/SignEncryptWidget:ShowIssues, self => $gnupg_widget &>
@@ -303,7 +306,7 @@ $showrows->(
     &>
 <& /Ticket/Elements/EditTransactionCustomFields, %ARGS, QueueObj => $QueueObj &>
 
-% if ( my $attachments = $session{'Attachments'} ) {
+% if ( my $attachments = $session{'Attachments'}{'Token'}) {
 
 <%loc("Attached file") %>
 
diff --git a/share/html/m/ticket/reply b/share/html/m/ticket/reply
index 2a6eff6..0d3929d 100644
--- a/share/html/m/ticket/reply
+++ b/share/html/m/ticket/reply
@@ -193,8 +193,9 @@ $CanRespond = 1 if ( $t->CurrentUserHasRight('ReplyToTicket') or
 $CanComment = 1 if ( $t->CurrentUserHasRight('CommentOnTicket') or
                      $t->CurrentUserHasRight('ModifyTicket') ); 
 
+$ARGS{'Token'} ||= $Token ||= Digest::MD5::md5_hex( rand(1024) );
 
-ProcessAttachments( ARGSRef => \%ARGS );
+ProcessAttachments( ARGSRef => \%ARGS, Token => $Token );
 
 # check email addresses for RT's
 {
@@ -235,6 +236,7 @@ if ( !$checks_failure && !$skip_update && exists $ARGS{SubmitTicket} ) {
 
 <%ARGS>
 $id => undef
+$Token => ''
 $Action => 'Respond'
 $DefaultStatus => undef
 </%ARGS>

commit f58dbfb01ef977d250772dd8774be36cf8568996
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Mar 4 11:37:52 2011 +0300

    SingleFile argument in AddAttachments for SelfService

diff --git a/share/html/SelfService/Update.html b/share/html/SelfService/Update.html
index 5b3cfb1..4467862 100644
--- a/share/html/SelfService/Update.html
+++ b/share/html/SelfService/Update.html
@@ -75,7 +75,7 @@
         </td>
 
     </tr>
-    <& /Ticket/Elements/AddAttachments, %ARGS, TicketObj => $Ticket &>
+    <& /Ticket/Elements/AddAttachments, %ARGS, TicketObj => $Ticket, SingleFile => 1 &>
     <tr><td colspan="2"><& /Elements/EditCustomFields, Object => $Ticket, AsTable => 0 &></td></tr>
 </table>
 <& /Elements/MessageBox, 
diff --git a/share/html/Ticket/Elements/AddAttachments b/share/html/Ticket/Elements/AddAttachments
index d86ef5b..7985581 100644
--- a/share/html/Ticket/Elements/AddAttachments
+++ b/share/html/Ticket/Elements/AddAttachments
@@ -58,11 +58,16 @@
 </tr>
 % } # end of if
 
-<tr><td class="label"><&|/l&>Attach</&>:</td><td><input name="Attach" type="file" /><input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" /><input type="hidden" class="hidden" name="UpdateAttach" value="1" />
+<tr><td class="label"><&|/l&>Attach</&>:</td><td><input name="Attach" type="file" />
+% unless ( $SingleFile ) {
+<input type="submit" class="button" name="AddMoreAttach" value="<&|/l&>Add More Files</&>" />
+% }
+<input type="hidden" class="hidden" name="UpdateAttach" value="1" />
 </td></tr>
 % $m->callback( %ARGS, CallbackName => 'End' );
 <%ARGS>
 $Token => ''
+$SingleFile => 0
 </%ARGS>
 <%INIT>
 my $attachments;

commit 030919c77c7ebf9ca6ea9441cefb9f025935e283
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Mar 4 11:39:51 2011 +0300

    more tests to cover attachments uploading through the web UI

diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 99b996b..4e91b60 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -804,7 +804,10 @@ sub create_ticket {
     my $self = shift;
     my %args = @_;
 
-    if ($args{Queue} && $args{Queue} =~ /\D/) {
+    if ( blessed $args{'Queue'} ) {
+        $args{Queue} = $args{'Queue'}->id;
+    }
+    elsif ($args{Queue} && $args{Queue} =~ /\D/) {
         my $queue = RT::Queue->new(RT->SystemUser);
         if (my $id = $queue->Load($args{Queue}) ) {
             $args{Queue} = $id;
diff --git a/t/web/attachments.t b/t/web/attachments.t
index ba5c712..312cb1e 100644
--- a/t/web/attachments.t
+++ b/t/web/attachments.t
@@ -1,94 +1,505 @@
 use strict;
 use warnings;
 
-use RT::Test tests => 33;
+use RT::Test tests => 159;
 
 use constant LogoFile => $RT::StaticPath .'/images/bpslogo.png';
 use constant FaviconFile => $RT::StaticPath .'/images/favicon.png';
 use constant TextFile => $RT::MasonComponentRoot .'/NoAuth/css/print.css';
 
-my ($baseurl, $m) = RT::Test->started_ok;
+my ($url, $m) = RT::Test->started_ok;
 ok $m->login, 'logged in';
 
-my $queue = RT::Queue->new(RT->Nobody);
-my $qid = $queue->Load('General');
-ok( $qid, "Loaded General queue" );
-
-$m->form_name('CreateTicketInQueue');
-$m->field('Queue', $qid);
-$m->submit;
-is($m->status, 200, "request successful");
-$m->content_contains("Create a new ticket", 'ticket create page');
-
-$m->form_name('TicketCreate');
-$m->field('Subject', 'Attachments test');
-$m->field('Attach',  LogoFile);
-$m->field('Content', 'Some content');
-$m->submit;
-is($m->status, 200, "request successful");
-
-$m->content_contains('Attachments test', 'we have subject on the page');
-$m->content_contains('Some content', 'and content');
-$m->content_contains('Download bpslogo.png', 'page has file name');
-
-open LOGO, "<", LogoFile or die "Can't open logo file: $!";
-binmode LOGO;
-my $logo_contents = do {local $/; <LOGO>};
-close LOGO;
-$m->follow_link_ok({text => "Download bpslogo.png"});
-is($m->content_type, "image/png");
-is($m->content, $logo_contents, "Binary content matches");
-
-$m->back;
-$m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
-$m->form_name('TicketUpdate');
-$m->field('Attach',  TextFile);
-$m->click('AddMoreAttach');
-is($m->status, 200, "request successful");
-
-$m->form_name('TicketUpdate');
-$m->field('Attach',  FaviconFile);
-$m->field('UpdateContent', 'Message');
-$m->click('SubmitTicket');
-is($m->status, 200, "request successful");
-
-$m->content_contains('Download bpslogo.png', 'page has file name');
-$m->content_contains('Download favicon.png', 'page has file name');
-$m->content_contains('Download print.css', 'page has file name');
-
-$m->follow_link_ok( { text => 'Download bpslogo.png' } );
-is( $m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' );
-
-$m->back;
-
-$m->follow_link_ok( { text => 'Download print.css' } );
-is( $m->response->header('Content-Type'),
-    'text/css;charset=UTF-8', 'Content-Type of text has charset' );
-
-diag "test mobile ui";
-$m->get_ok( $baseurl . '/m/ticket/create?Queue=' . $qid );
-
-$m->form_name('TicketCreate');
-$m->field('Subject', 'Attachments test');
-$m->field('Attach',  LogoFile);
-$m->field('Content', 'Some content');
-$m->submit;
-is($m->status, 200, "request successful");
-
-$m->content_contains('Attachments test', 'we have subject on the page');
-$m->content_contains('bpslogo.png', 'page has file name');
-
-$m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
-$m->form_name('TicketUpdate');
-$m->field('Attach',  LogoFile);
-$m->click('AddMoreAttach');
-is($m->status, 200, "request successful");
-
-$m->form_name('TicketUpdate');
-$m->field('Attach',  FaviconFile);
-$m->field('UpdateContent', 'Message');
-$m->click('SubmitTicket');
-is($m->status, 200, "request successful");
-
-$m->content_contains('bpslogo.png', 'page has file name');
-$m->content_contains('favicon.png', 'page has file name');
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok( $queue && $queue->id, "Loaded General queue" );
+
+diag "create a ticket in full interface";
+diag "w/o attachments";
+{
+    $m->goto_create_ticket( $queue );
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->content_contains("Create a new ticket", 'ticket create page');
+    $m->submit;
+    is($m->status, 200, "request successful");
+}
+
+diag "with one attachment";
+{
+    $m->goto_create_ticket( $queue );
+
+    $m->form_name('TicketCreate');
+    $m->field('Subject', 'Attachments test');
+    $m->field('Attach',  LogoFile);
+    $m->field('Content', 'Some content');
+
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Attachments test', 'we have subject on the page');
+    $m->content_contains('Some content', 'and content');
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+}
+
+diag "with two attachments";
+{
+    $m->goto_create_ticket( $queue );
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('Subject', 'Attachments test');
+    $m->field('Content', 'Some content');
+
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Attachments test', 'we have subject on the page');
+    $m->content_contains('Some content', 'and content');
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, but delete one along the way";
+{
+    $m->goto_create_ticket( $queue );
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  FaviconFile);
+    $m->tick( 'DeleteAttach', LogoFile );
+    $m->field('Subject', 'Attachments test');
+    $m->field('Content', 'Some content');
+
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Attachments test', 'we have subject on the page');
+    $m->content_contains('Some content', 'and content');
+    $m->content_lacks('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, but delete one along the way";
+{
+    $m->goto_create_ticket( $queue );
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->tick( 'DeleteAttach', LogoFile );
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  FaviconFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->field('Subject', 'Attachments test');
+    $m->field('Content', 'Some content');
+
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Attachments test', 'we have subject on the page');
+    $m->content_contains('Some content', 'and content');
+    $m->content_lacks('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "reply to a ticket in full interface";
+diag "with one attachment";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->goto_ticket( $ticket->id );
+    $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  LogoFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+}
+
+diag "with two attachments";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->goto_ticket( $ticket->id );
+    $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, delete one along the way";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->goto_ticket( $ticket->id );
+    $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketUpdate');
+    $m->tick('DeleteAttach',  LogoFile);
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->content_lacks('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "jumbo interface";
+diag "with one attachment";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->goto_ticket( $ticket->id );
+    $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket");
+    $m->form_name('TicketModifyAll');
+    $m->field('Attach',  LogoFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->goto_ticket( $ticket->id );
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+}
+
+diag "with two attachments";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->goto_ticket( $ticket->id );
+    $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket");
+    $m->form_name('TicketModifyAll');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketModifyAll');
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->goto_ticket( $ticket->id );
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "with one attachment, delete one along the way";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->goto_ticket( $ticket->id );
+    $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket");
+    $m->form_name('TicketModifyAll');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketModifyAll');
+    $m->tick('DeleteAttach',  LogoFile);
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->goto_ticket( $ticket->id );
+    $m->content_lacks('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "bulk update";
+diag "one attachment";
+{
+    my @tickets = RT::Test->create_tickets(
+        {
+            Queue   => $queue,
+            Subject => 'Attachments test',
+            Content => 'Some content',
+        },
+        {},
+        {},
+    );
+    my $query = join ' OR ', map "id=$_", map $_->id, @tickets;
+    $query =~ s/ /%20/g;
+    $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" );
+
+    $m->form_name('BulkUpdate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    foreach my $ticket ( @tickets ) {
+        $m->goto_ticket( $ticket->id );
+        $m->content_lacks('Download bpslogo.png', 'page has file name');
+        $m->content_contains('Download favicon.png', 'page has file name');
+    }
+}
+
+diag "two attachments";
+{
+    my @tickets = RT::Test->create_tickets(
+        {
+            Queue   => $queue,
+            Subject => 'Attachments test',
+            Content => 'Some content',
+        },
+        {},
+        {},
+    );
+    my $query = join ' OR ', map "id=$_", map $_->id, @tickets;
+    $query =~ s/ /%20/g;
+    $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" );
+
+    $m->form_name('BulkUpdate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('BulkUpdate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    foreach my $ticket ( @tickets ) {
+        $m->goto_ticket( $ticket->id );
+        $m->content_contains('Download bpslogo.png', 'page has file name');
+        $m->content_contains('Download favicon.png', 'page has file name');
+    }
+}
+
+diag "one attachment, delete one along the way";
+{
+    my @tickets = RT::Test->create_tickets(
+        {
+            Queue   => $queue,
+            Subject => 'Attachments test',
+            Content => 'Some content',
+        },
+        {},
+        {},
+    );
+    my $query = join ' OR ', map "id=$_", map $_->id, @tickets;
+    $query =~ s/ /%20/g;
+    $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" );
+
+    $m->form_name('BulkUpdate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('BulkUpdate');
+    $m->tick('DeleteAttach',  LogoFile);
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    foreach my $ticket ( @tickets ) {
+        $m->goto_ticket( $ticket->id );
+        $m->content_lacks('Download bpslogo.png', 'page has file name');
+        $m->content_contains('Download favicon.png', 'page has file name');
+    }
+}
+
+diag "self service";
+diag "create with attachment";
+{
+    $m->get_ok( $url . "/SelfService/Create.html?Queue=". $queue->id );
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('Content', 'Message');
+    ok(!$m->current_form->find_input('AddMoreAttach'), "one attachment only");
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "update with attachment";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m->get_ok( $url . "/SelfService/Update.html?id=". $ticket->id );
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    ok(!$m->current_form->find_input('AddMoreAttach'), "one attachment only");
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Download favicon.png', 'page has file name');
+}
+
+diag "mobile ui";
+
+diag "simple create + reply";
+{
+    $m->get_ok( $url . '/m/ticket/create?Queue=' . $queue->id );
+
+    $m->form_name('TicketCreate');
+    $m->field('Subject', 'Attachments test');
+    $m->field('Attach',  LogoFile);
+    $m->field('Content', 'Some content');
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Attachments test', 'we have subject on the page');
+    $m->content_contains('bpslogo.png', 'page has file name');
+
+    $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketUpdate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('UpdateContent', 'Message');
+    $m->click('SubmitTicket');
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('bpslogo.png', 'page has file name');
+    $m->content_contains('favicon.png', 'page has file name');
+}
+
+
+diag "check content type and content";
+{
+    $m->goto_create_ticket( $queue );
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  LogoFile);
+    $m->click('AddMoreAttach');
+    is($m->status, 200, "request successful");
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  TextFile);
+    $m->field('Subject', 'Attachments test');
+    $m->field('Content', 'Some content');
+
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_contains('Attachments test', 'we have subject on the page');
+    $m->content_contains('Some content', 'and content');
+    $m->content_contains('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download print.css', 'page has file name');
+
+    $m->follow_link_ok({text => "Download bpslogo.png"});
+    is($m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' );
+    is($m->content_type, "image/png");
+    is($m->content, RT::Test->file_content(LogoFile), "Binary content matches");
+    $m->back;
+
+    $m->follow_link_ok( { text => 'Download print.css' } );
+    is( $m->response->header('Content-Type'),
+        'text/css;charset=UTF-8',
+        'Content-Type of text has charset',
+    );
+    is($m->content_type, "text/css");
+    is($m->content, RT::Test->file_content(TextFile), "Text content matches");
+}
+
+diag "concurent actions";
+my $m2 = RT::Test::Web->new;
+ok $m2->login, 'second login';
+
+diag "update and create";
+{
+    my $ticket = RT::Test->create_ticket(
+        Queue   => $queue,
+        Subject => 'Attachments test',
+        Content => 'Some content',
+    );
+
+    $m2->goto_ticket( $ticket->id );
+    $m2->follow_link_ok({text => 'Reply'}, "reply to the ticket");
+    $m2->form_name('TicketUpdate');
+    $m2->field('Attach',  LogoFile);
+    $m2->click('AddMoreAttach');
+    is($m2->status, 200, "request successful");
+
+    $m->goto_create_ticket( $queue );
+
+    $m->form_name('TicketCreate');
+    $m->field('Attach',  FaviconFile);
+    $m->field('Subject', 'Attachments test');
+    $m->field('Content', 'Some content');
+    $m->submit;
+    is($m->status, 200, "request successful");
+
+    $m->content_lacks('Download bpslogo.png', 'page has file name');
+    $m->content_contains('Download favicon.png', 'page has file name');
+
+    $m2->form_name('TicketUpdate');
+    $m2->click('SubmitTicket');
+    $m2->content_contains('Download bpslogo.png', 'page has file name');
+    $m2->content_lacks('Download favicon.png', 'page has no file name');
+}
+

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


More information about the Rt-commit mailing list