1 # Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
2 # Copyright (c) 2008 Freeside Internet Services, Inc.
4 # This work is made available to you under the terms of Version 2 of
5 # the GNU General Public License. A copy of that license should have
6 # been provided with this software, but in any event can be snarfed
9 # This work is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
16 RT::Interface::Web_Vendor
22 Freeside vendor overlay for RT::Interface::Web.
26 use_ok(RT::Interface::Web_Vendor);
32 #package RT::Interface::Web;
35 package HTML::Mason::Commands;
37 no warnings qw(redefine);
39 =head2 ProcessTicketCustomers
43 sub ProcessTicketCustomers {
52 my $Ticket = $args{'TicketObj'};
53 my $ARGSRef = $args{'ARGSRef'};
54 my $Debug = $args{'Debug'};
55 my $me = 'ProcessTicketCustomers';
57 ### false laziness w/RT::Interface::Web::ProcessTicketLinks
58 # Delete links that are gone gone gone.
59 foreach my $arg ( keys %$ARGSRef ) {
60 if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
66 "Trying to delete: Base: $base Target: $target Type $type";
67 my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base,
82 my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 }
83 grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
86 #my @delete_custnums =
87 # map { /^Ticket-AddCustomer-(\d+)$/; $1 }
88 # grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
92 #figure out if we're going to auto-link requestors, and find them if so
95 my $num_cur_cust = $Ticket->Customers->Count;
96 my $num_new_cust = scalar(@custnums);
97 warn "$me: $num_cur_cust current customers / $num_new_cust new customers\n"
100 #if we're linking the first ticket to one customer
101 my $link_requestors = ( $num_cur_cust == 0 && $num_new_cust == 1 );
102 warn "$me: adding a single customer to a previously customerless".
103 " ticket, so linking customers to requestor too\n"
104 if $Debug && $link_requestors;
107 if ( $link_requestors ) {
109 #find any requestors without customers
111 grep { ! $_->Customers->Count }
112 @{ $Ticket->Requestors->UserMembersObj->ItemsArrayRef };
114 warn "$me: found ". scalar(@Requestors). " requestors without".
115 " customers; linking them\n"
121 #remove any declared non-customer addresses
124 my $exclude_regexp = RT->Config->Get('NonCustomerEmailRegexp');
125 @Requestors = grep { not $_->EmailAddress =~ $exclude_regexp } @Requestors
126 if defined $exclude_regexp;
129 #link ticket (and requestors) to customers
132 foreach my $custnum ( @custnums ) {
134 my @link = ( 'Type' => 'MemberOf',
135 'Target' => "freeside://freeside/cust_main/$custnum",
138 my( $val, $msg ) = $Ticket->AddLink(@link);
141 #add customer links to requestors
142 foreach my $Requestor ( @Requestors ) {
143 my( $val, $msg ) = $Requestor->AddLink(@link);
145 warn "$me: linking requestor to custnum $custnum: $msg\n"
155 #false laziness w/above... eventually it should go away in favor of this
156 sub ProcessObjectCustomers {
164 my $Object = $args{'Object'};
165 my $ARGSRef = $args{'ARGSRef'};
167 ### false laziness w/RT::Interface::Web::ProcessTicketLinks
168 # Delete links that are gone gone gone.
169 foreach my $arg ( keys %$ARGSRef ) {
170 if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
176 "Trying to delete: Base: $base Target: $target Type $type";
177 my ( $val, $msg ) = $Object->DeleteLink( Base => $base,
188 #my @delete_custnums =
189 # map { /^Object-AddCustomer-(\d+)$/; $1 }
190 # grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
193 my @custnums = map { /^Object-AddCustomer-(\d+)$/; $1 }
194 grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
197 foreach my $custnum ( @custnums ) {
199 $Object->AddLink( 'Type' => 'MemberOf',
200 'Target' => "freeside://freeside/cust_main/$custnum",
209 =head2 ProcessTicketBasics ( TicketObj => $Ticket, ARGSRef => \%ARGS );
211 Updates all core ticket fields except Status, and returns an array of results
216 sub ProcessTicketBasics {
224 my $TicketObj = $args{'TicketObj'};
225 my $ARGSRef = $args{'ARGSRef'};
227 # {{{ Set basic fields
239 if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) {
240 my $tempqueue = RT::Queue->new($RT::SystemUser);
241 $tempqueue->Load( $ARGSRef->{'Queue'} );
242 if ( $tempqueue->id ) {
243 $ARGSRef->{'Queue'} = $tempqueue->id;
247 my @results = UpdateRecordObject(
248 AttributesRef => \@attribs,
249 Object => $TicketObj,
253 # We special case owner changing, so we can use ForceOwnerChange
254 if ( $ARGSRef->{'Owner'} && ( $TicketObj->Owner != $ARGSRef->{'Owner'} ) ) {
256 if ( $ARGSRef->{'ForceOwnerChange'} ) {
257 $ChownType = "Force";
262 my ( $val, $msg ) = $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
263 push( @results, $msg );
269 =head2 ProcessTicketDates (TicketObj => RT::Ticket, ARGSRef => {})
271 Process updates to the Starts, Started, Told, Resolved, and WillResolve
276 sub ProcessTicketDates {
283 my $Ticket = $args{'TicketObj'};
284 my $ARGSRef = $args{'ARGSRef'};
288 # {{{ Set date fields
289 my @date_fields = qw(
298 #Run through each field in this list. update the value if apropriate
299 foreach my $field (@date_fields) {
300 next unless exists $ARGSRef->{ $field . '_Date' };
301 next if $ARGSRef->{ $field . '_Date' } eq '';
305 my $DateObj = RT::Date->new( $session{'CurrentUser'} );
308 Value => $ARGSRef->{ $field . '_Date' }
311 my $obj = $field . "Obj";
312 if ( ( defined $DateObj->Unix )
313 and ( $DateObj->Unix != $Ticket->$obj()->Unix() ) )
315 my $method = "Set$field";
316 my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
317 push @results, "$msg";
325 =head2 ProcessTicketStatus (TicketObj => RT::Ticket, ARGSRef => {})
327 Process updates to the 'Status' field of the ticket. If the new value
328 of Status is 'resolved', this will check required custom fields before
333 sub ProcessTicketStatus {
340 my $TicketObj = $args{'TicketObj'};
341 my $ARGSRef = $args{'ARGSRef'};
344 return () if !$ARGSRef->{'Status'};
346 if ( lc( $ARGSRef->{'Status'} ) eq 'resolved' ) {
347 foreach my $field ( $TicketObj->MissingRequiredFields ) {
348 push @results, loc('Missing required field: [_1]', $field->Name);
352 $m->notes('RedirectToBasics' => 1);
356 return UpdateRecordObject(
357 AttributesRef => [ 'Status' ],
358 Object => $TicketObj,
363 =head2 ProcessUpdateMessage
365 Takes paramhash with fields ARGSRef, TicketObj and SkipSignatureOnly.
367 Don't write message if it only contains current user's signature and
368 SkipSignatureOnly argument is true. Function anyway adds attachments
369 and updates time worked field even if skips message. The default value
374 # change from stock: if txn custom fields are set but there's no content
375 # or attachment, create a Touch txn instead of doing nothing
377 sub ProcessUpdateMessage {
382 SkipSignatureOnly => 1,
386 if ( $args{ARGSRef}->{'UpdateAttachments'}
387 && !keys %{ $args{ARGSRef}->{'UpdateAttachments'} } )
389 delete $args{ARGSRef}->{'UpdateAttachments'};
392 # Strip the signature
393 $args{ARGSRef}->{UpdateContent} = RT::Interface::Web::StripContent(
394 Content => $args{ARGSRef}->{UpdateContent},
395 ContentType => $args{ARGSRef}->{UpdateContentType},
396 StripSignature => $args{SkipSignatureOnly},
397 CurrentUser => $args{'TicketObj'}->CurrentUser,
400 my %txn_customfields;
402 foreach my $key ( keys %{ $args{ARGSRef} } ) {
403 if ( $key =~ /^(?:Object-RT::Transaction--)?CustomField-(\d+)/ ) {
404 next if $key =~ /(TimeUnits|Magic)$/;
405 $txn_customfields{$key} = $args{ARGSRef}->{$key};
409 # If, after stripping the signature, we have no message, create a
410 # Touch transaction if necessary
411 if ( not $args{ARGSRef}->{'UpdateAttachments'}
412 and not length $args{ARGSRef}->{'UpdateContent'} )
414 #if ( $args{ARGSRef}->{'UpdateTimeWorked'} ) {
415 # $args{ARGSRef}->{TimeWorked} = $args{TicketObj}->TimeWorked +
416 # delete $args{ARGSRef}->{'UpdateTimeWorked'};
419 my $timetaken = $args{ARGSRef}->{'UpdateTimeWorked'};
420 if ( $timetaken or grep {length $_} values %txn_customfields ) {
421 my ( $Transaction, $Description, $Object ) =
422 $args{TicketObj}->Touch(
423 CustomFields => \%txn_customfields,
424 TimeTaken => $timetaken
432 if ( $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject ) {
433 $args{ARGSRef}->{'UpdateSubject'} = undef;
436 my $Message = MakeMIMEEntity(
437 Subject => $args{ARGSRef}->{'UpdateSubject'},
438 Body => $args{ARGSRef}->{'UpdateContent'},
439 Type => $args{ARGSRef}->{'UpdateContentType'},
442 $Message->head->add( 'Message-ID' => Encode::encode_utf8(
443 RT::Interface::Email::GenMessageId( Ticket => $args{'TicketObj'} )
445 my $old_txn = RT::Transaction->new( $session{'CurrentUser'} );
446 if ( $args{ARGSRef}->{'QuoteTransaction'} ) {
447 $old_txn->Load( $args{ARGSRef}->{'QuoteTransaction'} );
449 $old_txn = $args{TicketObj}->Transactions->First();
452 if ( my $msg = $old_txn->Message->First ) {
453 RT::Interface::Email::SetInReplyTo(
459 if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
460 $Message->make_multipart;
461 $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
464 if ( $args{ARGSRef}->{'AttachTickets'} ) {
465 require RT::Action::SendEmail;
466 RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
467 ref $args{ARGSRef}->{'AttachTickets'}
468 ? @{ $args{ARGSRef}->{'AttachTickets'} }
469 : ( $args{ARGSRef}->{'AttachTickets'} ) );
472 my $bcc = $args{ARGSRef}->{'UpdateBcc'};
473 my $cc = $args{ARGSRef}->{'UpdateCc'};
477 BccMessageTo => $bcc,
478 Sign => $args{ARGSRef}->{'Sign'},
479 Encrypt => $args{ARGSRef}->{'Encrypt'},
481 TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'},
482 CustomFields => \%txn_customfields,
486 foreach my $type (qw(Cc AdminCc)) {
487 if (grep $_ eq $type || $_ eq ( $type . 's' ), @{ $args{ARGSRef}->{'SkipNotification'} || [] }) {
488 push @temp_squelch, map $_->address, Email::Address->parse( $message_args{$type} );
489 push @temp_squelch, $args{TicketObj}->$type->MemberEmailAddresses;
490 push @temp_squelch, $args{TicketObj}->QueueObj->$type->MemberEmailAddresses;
493 if (grep $_ eq 'Requestor' || $_ eq 'Requestors', @{ $args{ARGSRef}->{'SkipNotification'} || [] }) {
494 push @temp_squelch, map $_->address, Email::Address->parse( $message_args{Requestor} );
495 push @temp_squelch, $args{TicketObj}->Requestors->MemberEmailAddresses;
499 require RT::Action::SendEmail;
500 RT::Action::SendEmail->SquelchMailTo( RT::Action::SendEmail->SquelchMailTo, @temp_squelch );
503 unless ( $args{'ARGSRef'}->{'UpdateIgnoreAddressCheckboxes'} ) {
504 foreach my $key ( keys %{ $args{ARGSRef} } ) {
505 next unless $key =~ /^Update(Cc|Bcc)-(.*)$/;
507 my $var = ucfirst($1) . 'MessageTo';
509 if ( $message_args{$var} ) {
510 $message_args{$var} .= ", $value";
512 $message_args{$var} = $value;
518 # Do the update via the appropriate Ticket method
519 if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
520 my ( $Transaction, $Description, $Object ) =
521 $args{TicketObj}->Comment(%message_args);
522 push( @results, $Description );
523 #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
524 } elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
525 my ( $Transaction, $Description, $Object ) =
526 $args{TicketObj}->Correspond(%message_args);
527 push( @results, $Description );
528 #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
531 loc("Update type was neither correspondence nor comment.") . " " . loc("Update not recorded.") );