diff options
Diffstat (limited to 'rt/lib/RT/Interface')
| -rw-r--r-- | rt/lib/RT/Interface/CLI.pm | 91 | ||||
| -rwxr-xr-x | rt/lib/RT/Interface/Email.pm | 17 | ||||
| -rw-r--r-- | rt/lib/RT/Interface/Web.pm | 47 | ||||
| -rw-r--r-- | rt/lib/RT/Interface/Web_Vendor.pm | 537 |
4 files changed, 625 insertions, 67 deletions
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm index 5e1999816..ec0e877b4 100644 --- a/rt/lib/RT/Interface/CLI.pm +++ b/rt/lib/RT/Interface/CLI.pm @@ -1,51 +1,26 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC -# <sales@bestpractical.com> -# -# (Except where explicitly superseded by other copyright notices) -# -# -# LICENSE: -# +# BEGIN LICENSE BLOCK +# +# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> +# +# (Except where explictly superceded by other copyright notices) +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 or visit their web page on the internet at -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# -# CONTRIBUTION SUBMISSION POLICY: -# -# (The following paragraph is not intended to limit the rights granted -# to you to modify and distribute this software under the terms of -# the GNU General Public License and is only of importance to you if -# you choose to contribute your changes and enhancements to the -# community by submitting them to Best Practical Solutions, LLC.) -# -# By intentionally submitting any modifications, corrections or -# derivatives to this work, or any other work intended for use with -# Request Tracker, to Best Practical Solutions, LLC, you confirm that -# you are the copyright holder for those contributions and you grant -# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -# royalty-free, perpetual, license to use, copy, create derivative -# works based on those contributions, and sublicense and distribute -# those contributions and any derivatives thereof. -# -# END BPS TAGGED BLOCK }}} - +# +# Unless otherwise specified, all modifications, corrections or +# extensions to this work which alter its source code become the +# property of Best Practical Solutions, LLC when submitted for +# inclusion in the work. +# +# +# END LICENSE BLOCK use strict; use RT; @@ -54,12 +29,14 @@ package RT::Interface::CLI; BEGIN { - use base 'Exporter'; - use vars qw ($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS); + use Exporter (); + use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); # set the version for version checking - $VERSION = do { my @r = (q$Revision: 1.1.1.10 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker - + $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker + + @ISA = qw(Exporter); + # your exported package globals go here, # as well as any optionally exported functions @EXPORT_OK = qw(&CleanEnv @@ -99,6 +76,11 @@ BEGIN { =head1 METHODS +=begin testing + +ok(require RT::Interface::CLI); + +=end testing =cut @@ -200,9 +182,9 @@ sub GetMessageContent { #Load the sourcefile, if it's been handed to us if ($source) { - open( SOURCE, '<', $source ) or die $!; - @lines = (<SOURCE>) or die $!; - close (SOURCE) or die $!; + open (SOURCE, "<$source"); + @lines = (<SOURCE>); + close (SOURCE); } elsif ($args{'Content'}) { @lines = split('\n',$args{'Content'}); @@ -214,21 +196,21 @@ sub GetMessageContent { for (@lines) { print $fh $_; } - close ($fh) or die $!; + close ($fh); #Edit the file if we need to if ($edit) { unless ($ENV{'EDITOR'}) { - $RT::Logger->crit('No $EDITOR variable defined'); + $RT::Logger->crit('No $EDITOR variable defined'. "\n"); return undef; } system ($ENV{'EDITOR'}, $filename); } - open( READ, '<', $filename ) or die $!; + open (READ, "<$filename"); my @newlines = (<READ>); - close (READ) or die $!; + close (READ); unlink ($filename) unless (debug()); return(\@newlines); @@ -243,7 +225,7 @@ sub debug { my $val = shift; my ($debug); if ($val) { - $RT::Logger->debug($val); + $RT::Logger->debug($val."\n"); if ($debug) { print STDERR "$val\n"; } @@ -256,6 +238,9 @@ sub debug { # }}} -RT::Base->_ImportOverlays(); +eval "require RT::Interface::CLI_Vendor"; +die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/CLI_Vendor.pm}); +eval "require RT::Interface::CLI_Local"; +die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/CLI_Local.pm}); 1; diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm index 401e970e8..9216887cd 100755 --- a/rt/lib/RT/Interface/Email.pm +++ b/rt/lib/RT/Interface/Email.pm @@ -997,13 +997,28 @@ sub ParseCcAddressesFromHead { my $user = $args{'CurrentUser'}->UserObj; return - grep $_ ne $current_address && !RT::EmailParser->IsRTAddress( $_ ), + grep { $_ ne $current_address + && !RT::EmailParser->IsRTAddress( $_ ) + && !IgnoreCcAddress( $_ ) + } map lc $user->CanonicalizeEmailAddress( $_->address ), map Email::Address->parse( $args{'Head'}->get( $_ ) ), qw(To Cc); } +=head2 IgnoreCcAddress ADDRESS +Returns true if ADDRESS matches the $IgnoreCcRegexp config variable. + +=cut + +sub IgnoreCcAddress { + my $address = shift; + if ( my $address_re = RT->Config->Get('IgnoreCcRegexp') ) { + return 1 if $address =~ /$address_re/i; + } + return undef; +} =head2 ParseSenderAddressFromHead HEAD diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm index b3f593a9f..959c80334 100644 --- a/rt/lib/RT/Interface/Web.pm +++ b/rt/lib/RT/Interface/Web.pm @@ -438,7 +438,11 @@ sub MaybeRejectPrivateComponentRequest { autohandler | # requesting this directly is suspicious l ) # loc component ( $ | / ) # trailing slash or end of path - }xi) { + }xi + && $path !~ m{ /RTx/Statistics/\w+/Elements/Chart }xi + ) + { + warn "rejecting private component $path\n"; $m->abort(403); } @@ -657,11 +661,10 @@ sub InstantiateNewSession { sub SendSessionCookie { my $cookie = CGI::Cookie->new( - -name => _SessionCookieName(), - -value => $HTML::Mason::Commands::session{_session_id}, - -path => RT->Config->Get('WebPath'), - -secure => ( RT->Config->Get('WebSecureCookies') ? 1 : 0 ), - -httponly => ( RT->Config->Get('WebHttpOnlyCookies') ? 1 : 0 ), + -name => _SessionCookieName(), + -value => $HTML::Mason::Commands::session{_session_id}, + -path => RT->Config->Get('WebPath'), + -secure => ( RT->Config->Get('WebSecureCookies') ? 1 : 0 ) ); $HTML::Mason::Commands::r->err_headers_out->{'Set-Cookie'} = $cookie->as_string; @@ -1380,13 +1383,22 @@ sub ProcessUpdateMessage { my $bcc = $args{ARGSRef}->{'UpdateBcc'}; my $cc = $args{ARGSRef}->{'UpdateCc'}; + my %txn_customfields; + + foreach my $key ( keys %{ $args{ARGSRef} } ) { + if ( $key =~ /^(?:Object-RT::Transaction--)?CustomField-(\d+)/ ) { + $txn_customfields{$key} = $args{ARGSRef}->{$key}; + } + } + my %message_args = ( CcMessageTo => $cc, BccMessageTo => $bcc, Sign => $args{ARGSRef}->{'Sign'}, Encrypt => $args{ARGSRef}->{'Encrypt'}, MIMEObj => $Message, - TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'} + TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}, + CustomFields => \%txn_customfields, ); my @temp_squelch; @@ -1422,14 +1434,17 @@ sub ProcessUpdateMessage { } my @results; + # Do the update via the appropriate Ticket method if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) { - my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Comment(%message_args); + my ( $Transaction, $Description, $Object ) = + $args{TicketObj}->Comment(%message_args); push( @results, $Description ); - $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object; + #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object; } elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) { - my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Correspond(%message_args); + my ( $Transaction, $Description, $Object ) = + $args{TicketObj}->Correspond(%message_args); push( @results, $Description ); - $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object; + #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object; } else { push( @results, loc("Update type was neither correspondence nor comment.") . " " . loc("Update not recorded.") ); @@ -1548,8 +1563,6 @@ sub ParseDateToISO { sub ProcessACLChanges { my $ARGSref = shift; - #XXX: why don't we get ARGSref like in other Process* subs? - my @results; foreach my $arg ( keys %$ARGSref ) { @@ -1764,6 +1777,8 @@ sub ProcessTicketCustomFieldUpdates { $ARGSRef->{"Object-RT::Ticket-$1"} = delete $ARGSRef->{$arg}; } elsif ( $arg =~ /^CustomField-(\d+-.*)/ ) { $ARGSRef->{"Object-RT::Ticket--$1"} = delete $ARGSRef->{$arg}; + } elsif ( $arg =~ /^Object-RT::Transaction-(\d*)-CustomField/ ) { + delete $ARGSRef->{$arg}; # don't try to update transaction fields } } @@ -1839,6 +1854,9 @@ sub _ProcessObjectCustomFieldUpdates { # skip category argument next if $arg eq 'Category'; + # and TimeUnits + next if $arg eq 'Value-TimeUnits'; + # since http won't pass in a form element with a null value, we need # to fake it if ( $arg eq 'Values-Magic' ) { @@ -1917,6 +1935,9 @@ sub _ProcessObjectCustomFieldUpdates { $values_hash{$val} = 1 if $val; } + # For Date Cfs, @values is empty when there is no changes (no datas in form input) + return @results if ( $cf->Type eq 'Date' && ! @values ); + $cf_values->RedoSearch; while ( my $cf_value = $cf_values->Next ) { next if $values_hash{ $cf_value->id }; diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm new file mode 100644 index 000000000..ee8c34b55 --- /dev/null +++ b/rt/lib/RT/Interface/Web_Vendor.pm @@ -0,0 +1,537 @@ +# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am> +# Copyright (c) 2008 Freeside Internet Services, Inc. +# +# This work is made available to you under the terms of Version 2 of +# the GNU General Public License. A copy of that license should have +# been provided with this software, but in any event can be snarfed +# from www.gnu.org. +# +# This work is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +=head1 NAME + +RT::Interface::Web_Vendor + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +Freeside vendor overlay for RT::Interface::Web. + +=begin testing + +use_ok(RT::Interface::Web_Vendor); + +=end testing + +=cut + +#package RT::Interface::Web; +#use strict; + +package HTML::Mason::Commands; +use strict; +no warnings qw(redefine); + +=head2 ProcessTicketCustomers + +=cut + +sub ProcessTicketCustomers { + my %args = ( + TicketObj => undef, + ARGSRef => undef, + Debug => 0, + @_ + ); + my @results = (); + + my $Ticket = $args{'TicketObj'}; + my $ARGSRef = $args{'ARGSRef'}; + my $Debug = $args{'Debug'}; + my $me = 'ProcessTicketCustomers'; + + ### false laziness w/RT::Interface::Web::ProcessTicketLinks + # Delete links that are gone gone gone. + foreach my $arg ( keys %$ARGSRef ) { + if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) { + my $base = $1; + my $type = $2; + my $target = $3; + + push @results, + "Trying to delete: Base: $base Target: $target Type $type"; + my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base, + Type => $type, + Target => $target ); + + push @results, $msg; + + } + + } + ### + + ### + #find new customers + ### + + my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 } + grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } + keys %$ARGSRef; + + #my @delete_custnums = + # map { /^Ticket-AddCustomer-(\d+)$/; $1 } + # grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } + # keys %$ARGSRef; + + ### + #figure out if we're going to auto-link requestors, and find them if so + ### + + my $num_cur_cust = $Ticket->Customers->Count; + my $num_new_cust = scalar(@custnums); + warn "$me: $num_cur_cust current customers / $num_new_cust new customers\n" + if $Debug; + + #if we're linking the first ticket to one customer + my $link_requestors = ( $num_cur_cust == 0 && $num_new_cust == 1 ); + warn "$me: adding a single customer to a previously customerless". + " ticket, so linking customers to requestor too\n" + if $Debug && $link_requestors; + + my @Requestors = (); + if ( $link_requestors ) { + + #find any requestors without customers + @Requestors = + grep { ! $_->Customers->Count } + @{ $Ticket->Requestors->UserMembersObj->ItemsArrayRef }; + + warn "$me: found ". scalar(@Requestors). " requestors without". + " customers; linking them\n" + if $Debug; + + } + + ### + #remove any declared non-customer addresses + ### + + my $exclude_regexp = RT->Config->Get('NonCustomerEmailRegexp'); + @Requestors = grep { not $_->EmailAddress =~ $exclude_regexp } @Requestors + if defined $exclude_regexp; + + ### + #link ticket (and requestors) to customers + ### + + foreach my $custnum ( @custnums ) { + + my @link = ( 'Type' => 'MemberOf', + 'Target' => "freeside://freeside/cust_main/$custnum", + ); + + my( $val, $msg ) = $Ticket->AddLink(@link); + push @results, $msg; + + #add customer links to requestors + foreach my $Requestor ( @Requestors ) { + my( $val, $msg ) = $Requestor->AddLink(@link); + push @results, $msg; + warn "$me: linking requestor to custnum $custnum: $msg\n" + if $Debug > 1; + } + + } + + return @results; + +} + +#false laziness w/above... eventually it should go away in favor of this +sub ProcessObjectCustomers { + my %args = ( + Object => undef, + ARGSRef => undef, + @_ + ); + my @results = (); + + my $Object = $args{'Object'}; + my $ARGSRef = $args{'ARGSRef'}; + + ### false laziness w/RT::Interface::Web::ProcessTicketLinks + # Delete links that are gone gone gone. + foreach my $arg ( keys %$ARGSRef ) { + if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) { + my $base = $1; + my $type = $2; + my $target = $3; + + push @results, + "Trying to delete: Base: $base Target: $target Type $type"; + my ( $val, $msg ) = $Object->DeleteLink( Base => $base, + Type => $type, + Target => $target ); + + push @results, $msg; + + } + + } + ### + + #my @delete_custnums = + # map { /^Object-AddCustomer-(\d+)$/; $1 } + # grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } + # keys %$ARGSRef; + + my @custnums = map { /^Object-AddCustomer-(\d+)$/; $1 } + grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } + keys %$ARGSRef; + + foreach my $custnum ( @custnums ) { + my( $val, $msg ) = + $Object->AddLink( 'Type' => 'MemberOf', + 'Target' => "freeside://freeside/cust_main/$custnum", + ); + push @results, $msg; + } + + return @results; + +} + +=head2 ProcessTicketBasics ( TicketObj => $Ticket, ARGSRef => \%ARGS ); + +Updates all core ticket fields except Status, and returns an array of results +messages. + +=cut + +sub ProcessTicketBasics { + + my %args = ( + TicketObj => undef, + ARGSRef => undef, + @_ + ); + + my $TicketObj = $args{'TicketObj'}; + my $ARGSRef = $args{'ARGSRef'}; + + # {{{ Set basic fields + my @attribs = qw( + Subject + FinalPriority + Priority + TimeEstimated + TimeWorked + TimeLeft + Type + Queue + ); + + if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) { + my $tempqueue = RT::Queue->new($RT::SystemUser); + $tempqueue->Load( $ARGSRef->{'Queue'} ); + if ( $tempqueue->id ) { + $ARGSRef->{'Queue'} = $tempqueue->id; + } + } + + my @results = UpdateRecordObject( + AttributesRef => \@attribs, + Object => $TicketObj, + ARGSRef => $ARGSRef, + ); + + # We special case owner changing, so we can use ForceOwnerChange + if ( $ARGSRef->{'Owner'} && ( $TicketObj->Owner != $ARGSRef->{'Owner'} ) ) { + my ($ChownType); + if ( $ARGSRef->{'ForceOwnerChange'} ) { + $ChownType = "Force"; + } else { + $ChownType = "Give"; + } + + my ( $val, $msg ) = $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType ); + push( @results, $msg ); + } + + return (@results); +} + +=head2 ProcessTicketDates (TicketObj => RT::Ticket, ARGSRef => {}) + +Process updates to the Starts, Started, Told, Resolved, and WillResolve +fields. + +=cut + +sub ProcessTicketDates { + my %args = ( + TicketObj => undef, + ARGSRef => undef, + @_ + ); + + my $Ticket = $args{'TicketObj'}; + my $ARGSRef = $args{'ARGSRef'}; + + my (@results); + + # {{{ Set date fields + my @date_fields = qw( + Told + Resolved + Starts + Started + Due + WillResolve + ); + + #Run through each field in this list. update the value if apropriate + foreach my $field (@date_fields) { + next unless exists $ARGSRef->{ $field . '_Date' }; + next if $ARGSRef->{ $field . '_Date' } eq ''; + + my ( $code, $msg ); + + my $DateObj = RT::Date->new( $session{'CurrentUser'} ); + $DateObj->Set( + Format => 'unknown', + Value => $ARGSRef->{ $field . '_Date' } + ); + + my $obj = $field . "Obj"; + if ( ( defined $DateObj->Unix ) + and ( $DateObj->Unix != $Ticket->$obj()->Unix() ) ) + { + my $method = "Set$field"; + my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO ); + push @results, "$msg"; + } + } + + # }}} + return (@results); +} + +=head2 ProcessTicketStatus (TicketObj => RT::Ticket, ARGSRef => {}) + +Process updates to the 'Status' field of the ticket. If the new value +of Status is 'resolved', this will check required custom fields before +allowing the update. + +=cut + +sub ProcessTicketStatus { + my %args = ( + TicketObj => undef, + ARGSRef => undef, + @_ + ); + + my $TicketObj = $args{'TicketObj'}; + my $ARGSRef = $args{'ARGSRef'}; + my @results; + + return () if !$ARGSRef->{'Status'}; + + if ( lc( $ARGSRef->{'Status'} ) eq 'resolved' ) { + foreach my $field ( $TicketObj->MissingRequiredFields ) { + push @results, loc('Missing required field: [_1]', $field->Name); + } + } + if ( @results ) { + $m->notes('RedirectToBasics' => 1); + return @results; + } + + return UpdateRecordObject( + AttributesRef => [ 'Status' ], + Object => $TicketObj, + ARGSRef => $ARGSRef, + ); +} + +=head2 ProcessUpdateMessage + +Takes paramhash with fields ARGSRef, TicketObj and SkipSignatureOnly. + +Don't write message if it only contains current user's signature and +SkipSignatureOnly argument is true. Function anyway adds attachments +and updates time worked field even if skips message. The default value +is true. + +=cut + +# change from stock: if txn custom fields are set but there's no content +# or attachment, create a Touch txn instead of doing nothing + +sub ProcessUpdateMessage { + + my %args = ( + ARGSRef => undef, + TicketObj => undef, + SkipSignatureOnly => 1, + @_ + ); + + if ( $args{ARGSRef}->{'UpdateAttachments'} + && !keys %{ $args{ARGSRef}->{'UpdateAttachments'} } ) + { + delete $args{ARGSRef}->{'UpdateAttachments'}; + } + + # Strip the signature + $args{ARGSRef}->{UpdateContent} = RT::Interface::Web::StripContent( + Content => $args{ARGSRef}->{UpdateContent}, + ContentType => $args{ARGSRef}->{UpdateContentType}, + StripSignature => $args{SkipSignatureOnly}, + CurrentUser => $args{'TicketObj'}->CurrentUser, + ); + + my %txn_customfields; + + foreach my $key ( keys %{ $args{ARGSRef} } ) { + if ( $key =~ /^(?:Object-RT::Transaction--)?CustomField-(\d+)/ ) { + next if $key =~ /(TimeUnits|Magic)$/; + $txn_customfields{$key} = $args{ARGSRef}->{$key}; + } + } + + # If, after stripping the signature, we have no message, create a + # Touch transaction if necessary + if ( not $args{ARGSRef}->{'UpdateAttachments'} + and not length $args{ARGSRef}->{'UpdateContent'} ) + { + #if ( $args{ARGSRef}->{'UpdateTimeWorked'} ) { + # $args{ARGSRef}->{TimeWorked} = $args{TicketObj}->TimeWorked + + # delete $args{ARGSRef}->{'UpdateTimeWorked'}; + # } + + my $timetaken = $args{ARGSRef}->{'UpdateTimeWorked'}; + if ( $timetaken or grep {length $_} values %txn_customfields ) { + my ( $Transaction, $Description, $Object ) = + $args{TicketObj}->Touch( + CustomFields => \%txn_customfields, + TimeTaken => $timetaken + ); + return $Description; + } + + return; + } + + if ( $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject ) { + $args{ARGSRef}->{'UpdateSubject'} = undef; + } + + my $Message = MakeMIMEEntity( + Subject => $args{ARGSRef}->{'UpdateSubject'}, + Body => $args{ARGSRef}->{'UpdateContent'}, + Type => $args{ARGSRef}->{'UpdateContentType'}, + ); + + $Message->head->add( 'Message-ID' => Encode::encode_utf8( + RT::Interface::Email::GenMessageId( Ticket => $args{'TicketObj'} ) + ) ); + my $old_txn = RT::Transaction->new( $session{'CurrentUser'} ); + if ( $args{ARGSRef}->{'QuoteTransaction'} ) { + $old_txn->Load( $args{ARGSRef}->{'QuoteTransaction'} ); + } else { + $old_txn = $args{TicketObj}->Transactions->First(); + } + + if ( my $msg = $old_txn->Message->First ) { + RT::Interface::Email::SetInReplyTo( + Message => $Message, + InReplyTo => $msg + ); + } + + if ( $args{ARGSRef}->{'UpdateAttachments'} ) { + $Message->make_multipart; + $Message->add_part($_) 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'} ) ); + } + + my $bcc = $args{ARGSRef}->{'UpdateBcc'}; + my $cc = $args{ARGSRef}->{'UpdateCc'}; + + my %message_args = ( + CcMessageTo => $cc, + BccMessageTo => $bcc, + Sign => $args{ARGSRef}->{'Sign'}, + Encrypt => $args{ARGSRef}->{'Encrypt'}, + MIMEObj => $Message, + TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}, + CustomFields => \%txn_customfields, + ); + + my @temp_squelch; + foreach my $type (qw(Cc AdminCc)) { + if (grep $_ eq $type || $_ eq ( $type . 's' ), @{ $args{ARGSRef}->{'SkipNotification'} || [] }) { + push @temp_squelch, map $_->address, Email::Address->parse( $message_args{$type} ); + push @temp_squelch, $args{TicketObj}->$type->MemberEmailAddresses; + push @temp_squelch, $args{TicketObj}->QueueObj->$type->MemberEmailAddresses; + } + } + if (grep $_ eq 'Requestor' || $_ eq 'Requestors', @{ $args{ARGSRef}->{'SkipNotification'} || [] }) { + push @temp_squelch, map $_->address, Email::Address->parse( $message_args{Requestor} ); + push @temp_squelch, $args{TicketObj}->Requestors->MemberEmailAddresses; + } + + if (@temp_squelch) { + require RT::Action::SendEmail; + RT::Action::SendEmail->SquelchMailTo( RT::Action::SendEmail->SquelchMailTo, @temp_squelch ); + } + + unless ( $args{'ARGSRef'}->{'UpdateIgnoreAddressCheckboxes'} ) { + foreach my $key ( keys %{ $args{ARGSRef} } ) { + next unless $key =~ /^Update(Cc|Bcc)-(.*)$/; + + my $var = ucfirst($1) . 'MessageTo'; + my $value = $2; + if ( $message_args{$var} ) { + $message_args{$var} .= ", $value"; + } else { + $message_args{$var} = $value; + } + } + } + + my @results; + # Do the update via the appropriate Ticket method + if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) { + my ( $Transaction, $Description, $Object ) = + $args{TicketObj}->Comment(%message_args); + push( @results, $Description ); + #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object; + } elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) { + my ( $Transaction, $Description, $Object ) = + $args{TicketObj}->Correspond(%message_args); + push( @results, $Description ); + #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object; + } else { + push( @results, + loc("Update type was neither correspondence nor comment.") . " " . loc("Update not recorded.") ); + } + return @results; +} + +1; + |
