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 #link ticket (and requestors) to customers
124 foreach my $custnum ( @custnums ) {
126 my @link = ( 'Type' => 'MemberOf',
127 'Target' => "freeside://freeside/cust_main/$custnum",
130 my( $val, $msg ) = $Ticket->AddLink(@link);
133 #add customer links to requestors
134 foreach my $Requestor ( @Requestors ) {
135 my( $val, $msg ) = $Requestor->AddLink(@link);
137 warn "$me: linking requestor to custnum $custnum: $msg\n"
147 #false laziness w/above... eventually it should go away in favor of this
148 sub ProcessObjectCustomers {
156 my $Object = $args{'Object'};
157 my $ARGSRef = $args{'ARGSRef'};
159 ### false laziness w/RT::Interface::Web::ProcessTicketLinks
160 # Delete links that are gone gone gone.
161 foreach my $arg ( keys %$ARGSRef ) {
162 if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
168 "Trying to delete: Base: $base Target: $target Type $type";
169 my ( $val, $msg ) = $Object->DeleteLink( Base => $base,
180 #my @delete_custnums =
181 # map { /^Object-AddCustomer-(\d+)$/; $1 }
182 # grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
185 my @custnums = map { /^Object-AddCustomer-(\d+)$/; $1 }
186 grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
189 foreach my $custnum ( @custnums ) {
191 $Object->AddLink( 'Type' => 'MemberOf',
192 'Target' => "freeside://freeside/cust_main/$custnum",
201 =head2 ProcessTicketBasics ( TicketObj => $Ticket, ARGSRef => \%ARGS );
203 Updates all core ticket fields except Status, and returns an array of results
208 sub ProcessTicketBasics {
216 my $TicketObj = $args{'TicketObj'};
217 my $ARGSRef = $args{'ARGSRef'};
219 # {{{ Set basic fields
231 if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) {
232 my $tempqueue = RT::Queue->new($RT::SystemUser);
233 $tempqueue->Load( $ARGSRef->{'Queue'} );
234 if ( $tempqueue->id ) {
235 $ARGSRef->{'Queue'} = $tempqueue->id;
239 my @results = UpdateRecordObject(
240 AttributesRef => \@attribs,
241 Object => $TicketObj,
245 # We special case owner changing, so we can use ForceOwnerChange
246 if ( $ARGSRef->{'Owner'} && ( $TicketObj->Owner != $ARGSRef->{'Owner'} ) ) {
248 if ( $ARGSRef->{'ForceOwnerChange'} ) {
249 $ChownType = "Force";
254 my ( $val, $msg ) = $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
255 push( @results, $msg );
261 =head2 ProcessTicketDates (TicketObj => RT::Ticket, ARGSRef => {})
263 Process updates to the Starts, Started, Told, Resolved, and WillResolve
268 sub ProcessTicketDates {
275 my $Ticket = $args{'TicketObj'};
276 my $ARGSRef = $args{'ARGSRef'};
280 # {{{ Set date fields
281 my @date_fields = qw(
290 #Run through each field in this list. update the value if apropriate
291 foreach my $field (@date_fields) {
292 next unless exists $ARGSRef->{ $field . '_Date' };
293 next if $ARGSRef->{ $field . '_Date' } eq '';
297 my $DateObj = RT::Date->new( $session{'CurrentUser'} );
300 Value => $ARGSRef->{ $field . '_Date' }
303 my $obj = $field . "Obj";
304 if ( ( defined $DateObj->Unix )
305 and ( $DateObj->Unix != $Ticket->$obj()->Unix() ) )
307 my $method = "Set$field";
308 my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
309 push @results, "$msg";
317 =head2 ProcessTicketStatus (TicketObj => RT::Ticket, ARGSRef => {})
319 Process updates to the 'Status' field of the ticket. If the new value
320 of Status is 'resolved', this will check required custom fields before
325 sub ProcessTicketStatus {
332 my $TicketObj = $args{'TicketObj'};
333 my $ARGSRef = $args{'ARGSRef'};
336 return () if !$ARGSRef->{'Status'};
338 if ( lc( $ARGSRef->{'Status'} ) eq 'resolved' ) {
339 foreach my $field ( $TicketObj->MissingRequiredFields ) {
340 push @results, loc('Missing required field: [_1]', $field->Name);
344 $m->notes('RedirectToBasics' => 1);
348 return UpdateRecordObject(
349 AttributesRef => [ 'Status' ],
350 Object => $TicketObj,
355 =head2 ProcessUpdateMessage
357 Takes paramhash with fields ARGSRef, TicketObj and SkipSignatureOnly.
359 Don't write message if it only contains current user's signature and
360 SkipSignatureOnly argument is true. Function anyway adds attachments
361 and updates time worked field even if skips message. The default value
366 # change from stock: if txn custom fields are set but there's no content
367 # or attachment, create a Touch txn instead of doing nothing
369 sub ProcessUpdateMessage {
374 SkipSignatureOnly => 1,
378 if ( $args{ARGSRef}->{'UpdateAttachments'}
379 && !keys %{ $args{ARGSRef}->{'UpdateAttachments'} } )
381 delete $args{ARGSRef}->{'UpdateAttachments'};
384 # Strip the signature
385 $args{ARGSRef}->{UpdateContent} = RT::Interface::Web::StripContent(
386 Content => $args{ARGSRef}->{UpdateContent},
387 ContentType => $args{ARGSRef}->{UpdateContentType},
388 StripSignature => $args{SkipSignatureOnly},
389 CurrentUser => $args{'TicketObj'}->CurrentUser,
392 my %txn_customfields;
394 foreach my $key ( keys %{ $args{ARGSRef} } ) {
395 if ( $key =~ /^(?:Object-RT::Transaction--)?CustomField-(\d+)/ ) {
396 next if $key =~ /(TimeUnits|Magic)$/;
397 $txn_customfields{$key} = $args{ARGSRef}->{$key};
401 # If, after stripping the signature, we have no message, create a
402 # Touch transaction if necessary
403 if ( not $args{ARGSRef}->{'UpdateAttachments'}
404 and not length $args{ARGSRef}->{'UpdateContent'} )
406 #if ( $args{ARGSRef}->{'UpdateTimeWorked'} ) {
407 # $args{ARGSRef}->{TimeWorked} = $args{TicketObj}->TimeWorked +
408 # delete $args{ARGSRef}->{'UpdateTimeWorked'};
411 my $timetaken = $args{ARGSRef}->{'UpdateTimeWorked'};
412 if ( $timetaken or grep {length $_} values %txn_customfields ) {
413 my ( $Transaction, $Description, $Object ) =
414 $args{TicketObj}->Touch(
415 CustomFields => \%txn_customfields,
416 TimeTaken => $timetaken
424 if ( $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject ) {
425 $args{ARGSRef}->{'UpdateSubject'} = undef;
428 my $Message = MakeMIMEEntity(
429 Subject => $args{ARGSRef}->{'UpdateSubject'},
430 Body => $args{ARGSRef}->{'UpdateContent'},
431 Type => $args{ARGSRef}->{'UpdateContentType'},
434 $Message->head->add( 'Message-ID' => Encode::encode_utf8(
435 RT::Interface::Email::GenMessageId( Ticket => $args{'TicketObj'} )
437 my $old_txn = RT::Transaction->new( $session{'CurrentUser'} );
438 if ( $args{ARGSRef}->{'QuoteTransaction'} ) {
439 $old_txn->Load( $args{ARGSRef}->{'QuoteTransaction'} );
441 $old_txn = $args{TicketObj}->Transactions->First();
444 if ( my $msg = $old_txn->Message->First ) {
445 RT::Interface::Email::SetInReplyTo(
451 if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
452 $Message->make_multipart;
453 $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
456 if ( $args{ARGSRef}->{'AttachTickets'} ) {
457 require RT::Action::SendEmail;
458 RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
459 ref $args{ARGSRef}->{'AttachTickets'}
460 ? @{ $args{ARGSRef}->{'AttachTickets'} }
461 : ( $args{ARGSRef}->{'AttachTickets'} ) );
464 my $bcc = $args{ARGSRef}->{'UpdateBcc'};
465 my $cc = $args{ARGSRef}->{'UpdateCc'};
469 BccMessageTo => $bcc,
470 Sign => $args{ARGSRef}->{'Sign'},
471 Encrypt => $args{ARGSRef}->{'Encrypt'},
473 TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'},
474 CustomFields => \%txn_customfields,
478 foreach my $type (qw(Cc AdminCc)) {
479 if (grep $_ eq $type || $_ eq ( $type . 's' ), @{ $args{ARGSRef}->{'SkipNotification'} || [] }) {
480 push @temp_squelch, map $_->address, Email::Address->parse( $message_args{$type} );
481 push @temp_squelch, $args{TicketObj}->$type->MemberEmailAddresses;
482 push @temp_squelch, $args{TicketObj}->QueueObj->$type->MemberEmailAddresses;
485 if (grep $_ eq 'Requestor' || $_ eq 'Requestors', @{ $args{ARGSRef}->{'SkipNotification'} || [] }) {
486 push @temp_squelch, map $_->address, Email::Address->parse( $message_args{Requestor} );
487 push @temp_squelch, $args{TicketObj}->Requestors->MemberEmailAddresses;
491 require RT::Action::SendEmail;
492 RT::Action::SendEmail->SquelchMailTo( RT::Action::SendEmail->SquelchMailTo, @temp_squelch );
495 unless ( $args{'ARGSRef'}->{'UpdateIgnoreAddressCheckboxes'} ) {
496 foreach my $key ( keys %{ $args{ARGSRef} } ) {
497 next unless $key =~ /^Update(Cc|Bcc)-(.*)$/;
499 my $var = ucfirst($1) . 'MessageTo';
501 if ( $message_args{$var} ) {
502 $message_args{$var} .= ", $value";
504 $message_args{$var} = $value;
510 # Do the update via the appropriate Ticket method
511 if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
512 my ( $Transaction, $Description, $Object ) =
513 $args{TicketObj}->Comment(%message_args);
514 push( @results, $Description );
515 #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
516 } elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
517 my ( $Transaction, $Description, $Object ) =
518 $args{TicketObj}->Correspond(%message_args);
519 push( @results, $Description );
520 #$Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
523 loc("Update type was neither correspondence nor comment.") . " " . loc("Update not recorded.") );