%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# 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 }}} <& /RTIR/Elements/Header, Title => $title, Refresh => $session{'tickets_refresh_interval'} &> <& /Elements/Tabs, SearchArgs => { Query => $Query, Format => $Format, RowsPerPage => $Rows, Page => $Page, Order => $Order, OrderBy => $OrderBy } &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Ticket => $IncidentObj); <& /Elements/ListActions, actions => \@results &>
<& /Elements/Crypt/SignEncryptWidget:ShowIssues, self => $gnupg_widget &>

<&|/l&>Reporters

<& /RTIR/Search/Elements/ShowResults, Queue => 'Incident Reports', BaseURL => $BaseURL, BaseQuery => RT::IR->Query(Queue => 'Incident Reports', MemberOf => $id), Query => $Query, DisplayFormat => "__CheckBox.{SelectedReports}__, $Format", Format => $Format, Rows => $Rows, Page => $Page, OrderBy => $OrderBy, Order => $Order, &> % if ( $All ) {

<&|/l&>Investigation Correspondents

<& /RTIR/Search/Elements/ShowResults, Queue => 'Investigations', BaseURL => $BaseURL, BaseQuery => RT::IR->Query(Queue => 'Investigations', MemberOf => $id), Query => $Query, DisplayFormat => "__CheckBox.{SelectedInvestigations}__, $Format", Format => $Format, Rows => $Rows, Page => $Page, OrderBy => $OrderBy, Order => $Order, &> % unless( RT->Config->Get('RTIR_DisableBlocksQueue') ) {

<&|/l&>Blocks Correspondents

<& /RTIR/Search/Elements/ShowResults, Queue => 'Investigations', BaseURL => $BaseURL, BaseQuery => RT::IR->Query(Queue => 'Blocks', MemberOf => $id), Query => $Query, DisplayFormat => "__CheckBox.{SelectedBlocks}__, $Format", Format => $Format, Rows => $Rows, Page => $Page, OrderBy => $OrderBy, Order => $Order, &> % } % } % unless ( $candidates ) {

<&|/l&>Warning: no recipients!

% } <& ../Elements/ReplyForm, %ARGS, TicketObj => $IncidentObj, Status => $Status, GnuPGWidget => $gnupg_widget, &>

<&|/l&>REMEMBER!!! You have to document incident's resolution as a comment in its history

<& /Elements/Submit, Name => "SubmitTicket", Label => loc("Update Incident") &>
<%INIT> my (@results); my $IncidentObj = LoadTicket( $id ); $id = $ARGS{'id'} = $IncidentObj->id; $m->callback( %ARGS, CallbackName => 'Initial', Ticket => $IncidentObj ); $Status = '' if $Status && $Status eq $IncidentObj->Status; ProcessAttachments( ARGSRef => \%ARGS ); my $checks_failure = 0; $m->callback( CallbackName => 'BeforeReply', ARGSRef => \%ARGS, checks_failure => \$checks_failure, results => \@results, Ticket => $IncidentObj ); # ----------------------------------------------------------------------- IDD START if( $ARGS{'Status'} eq 'resolved' ){ $RT::Logger->debug( "\t ARGS: " . Data::Dumper::Dumper(\%ARGS) ); # Check if Mandatory and Prohibited fields have been filled for the resolution my %MandatoryFields = RT->Config->Get('LuciaMandatoryFieldsOnResolution'); my %ProhibitedFields = RT->Config->Get('LuciaProhibitedFieldsOnResolution'); for my $field ( keys %MandatoryFields ){ if( $MandatoryFields{$field} ){ # It is a Custom Field my $cf = RT::CustomField->new( $RT::SystemUser ); $cf->Load( $field ); my $cf_arg = "Object-RT::Ticket-". $id ."-CustomField-". $cf->id ."-Values"; if( defined $ARGS{$cf_arg} || defined $ARGS{$cf_arg ."-Magic"} ){ unless( $ARGS{$cf_arg} ){ $RT::Logger->debug( "\t\t Empty mandatory CF: ". $field ); push @results, loc( "Invalid value(s) of the custom fields" ) unless $checks_failure; push @results, loc( "[_1]: Mandatory custom field in resolution", loc( $field ) ); $checks_failure = 1; } } } else { #It is a RT field if( defined $ARGS{$field} ){ unless( $ARGS{$field} ){ $RT::Logger->debug( "\t\t Empty mandatory Field: ". $field ); push @results, loc( "Invalid value(s) of the fields" ) unless $checks_failure; push @results, loc( "[_1]: Mandatory field in resolution", loc( $field ) ); $checks_failure = 1; } } } } for my $field ( keys %ProhibitedFields ){ if( $ProhibitedFields{$field} ){ # It is a Custom Field my $cf = RT::CustomField->new( $RT::SystemUser ); $cf->Load( $field ); my $cf_arg = "Object-RT::Ticket-". $id ."-CustomField-". $cf->id ."-Values"; if( defined $ARGS{$cf_arg} || defined $ARGS{$cf_arg ."-Magic"} ){ if( $ARGS{$cf_arg} ){ $RT::Logger->debug( "\t\t Filled prohibited CF: ". $field ." -> ". delete $ARGS{$cf_arg} ); push @results, loc( "Invalid value(s) of the custom fields" ) unless $checks_failure; push @results, loc( "[_1]: Prohibited custom field in resolution", loc( $field ) ); $checks_failure = 1; } } } else { #It is a RT field if( defined $ARGS{$field} ){ if( $ARGS{$field} ){ $RT::Logger->debug( "\t\t Filled prohibited Field: ". $field ." -> ". delete $ARGS{$field} ); push @results, loc( "Invalid value(s) of the fields" ) unless $checks_failure; push @results, loc( "[_1]: Prohibited field in resolution", loc( $field ) ); $checks_failure = 1; } } } } # Check whether Impact value is appropriate for current Resolution value my $ResolutionCF = RT::CustomField->new( $RT::SystemUser ); $ResolutionCF->Load( 'Resolution' ); my $resolutioncf_arg = "Object-RT::Ticket-". $id ."-CustomField-". $ResolutionCF->id ."-Values"; my $ImpactCFName = 'Impact'; my $ImpactCF = RT::CustomField->new( $RT::SystemUser ); $ImpactCF->Load( $ImpactCFName ); my $impactcf_arg = "Object-RT::Ticket-". $id ."-CustomField-". $ImpactCF->id ."-Values"; if( $ARGS{$resolutioncf_arg} && $ARGS{$impactcf_arg} ){ my %ResolutionImpactCorrelation = RT->Config->Get('LuciaResolutionImpactCorrelation'); my %ValidImpactValues = %{ $ResolutionImpactCorrelation{ $ARGS{$resolutioncf_arg} } }; if( %ValidImpactValues ){ # Resolution value has a list of accepted values unless( exists( $ValidImpactValues{ $ARGS{$impactcf_arg} } ) ){ my $valid_values = ''; my $first_value = 1; foreach my $key (sort { $a cmp $b } keys %ValidImpactValues) { $valid_values .= ", " unless $first_value; $valid_values .= "'". loc( $key ) ."'"; $first_value = 0 if $first_value; } $RT::Logger->debug( "\t\t Invalid Field: $ImpactCFName -> ". delete $ARGS{$impactcf_arg} ); push @results, loc( "Invalid value(s) of the fields" ) unless $checks_failure; push @results, loc('[_1]: its value is not appropriate for current Resolution value. Valid values are: [_2]', loc( $ImpactCFName ), $valid_values); $checks_failure = 1; } } else { # Resolution value has no list of accepted values: check Priority my $PriorityCFName = 'Priority'; my %PRIORITY_VALUES = RT->Config->Get('PriorityAsString'); my %PRIORITY_NAMES = reverse %PRIORITY_VALUES; my $TicketPriority; # Get the Priority value from the ticket or, if it's being changed, from the value hashmap if( defined $ARGS{$PriorityCFName} ){ $TicketPriority = $PRIORITY_NAMES{ $ARGS{$PriorityCFName} }; } else { $TicketPriority = $PRIORITY_NAMES{ $IncidentObj->Priority }; } unless( $ARGS{$impactcf_arg} =~ m/ - $TicketPriority$/ ){ $RT::Logger->debug( "\t\t Invalid Field: $ImpactCFName -> ". delete $ARGS{$PriorityCFName} ); push @results, loc( "Invalid value(s) of the fields" ) unless $checks_failure; push @results, loc('[_1]: its value is not appropriate for current Resolution value. Valid values are: [_2]', loc( $ImpactCFName ), loc( "[_1] must match [_2] value", $ImpactCFName, $PriorityCFName )); $checks_failure = 1; } } } } # ----------------------------------------------------------------------- IDD END my $gnupg_widget = $m->comp('/Elements/Crypt/SignEncryptWidget:new', Arguments => \%ARGS ); $m->comp( '/Elements/Crypt/SignEncryptWidget:Process', self => $gnupg_widget, QueueObj => $IncidentObj->QueueObj, ); my @selected_children; foreach my $id( @SelectedReports, @SelectedInvestigations, @SelectedBlocks ) { my $ticket = RT::Ticket->new( $session{'CurrentUser'} ); $ticket->Load( $id ); unless( $ticket->id ) { push @results, loc( "Ticket [_1]: Couldn't load ticket.", $id ); next; } push @selected_children, $ticket; } if ( $SubmitTicket && $gnupg_widget ) { foreach my $Ticket ( @selected_children ) { my $status = $m->comp('/Elements/Crypt/SignEncryptWidget:Check', self => $gnupg_widget, TicketObj => $Ticket, ); $checks_failure = 1 unless $status; } } if ( $SubmitTicket && !$checks_failure ) { my $incident_cycle = $IncidentObj->QueueObj->LifecycleObj; foreach my $Ticket ( @selected_children ) { my $id = $Ticket->id; push @results, map { loc("Ticket [_1]: [_2]", $id, $_) } ProcessUpdateMessage( TicketObj => $Ticket, ARGSRef => \%ARGS, KeepAttachments => 1 ); my %additional; if ( $Status ) { $additional{Status} = RT::IR->MapStatus( $Status, $incident_cycle => $Ticket ); } unless ( RT::IR->IsLinkedToActiveIncidents( $Ticket, $IncidentObj ) ) { push @results, ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => { %ARGS, %additional }, ); } } # manually clear this out since we told ProcessUpdateMessage to KeepAttachments delete $session{'Attachments'}{ $ARGS{'Token'} }; my $update_incident_state = 1; if ( $Status && $incident_cycle->IsInactive( $Status ) ) { # if we are going to resolve/reject incident then do it only when we've # rejected all children that are not linked to another active incident(s) $update_incident_state = 0 if RT::IR->IncidentHasActiveChildren( $IncidentObj ); } push @results, ProcessTicketLinks( TicketObj => $IncidentObj, ARGSRef => \%ARGS ); if ( $update_incident_state ) { push @results, ProcessObjectCustomFieldUpdates( Object => $IncidentObj, ARGSRef => \%ARGS ); push @results, ProcessTicketBasics( TicketObj => $IncidentObj, ARGSRef => { %ARGS, Status => $Status }, ); } else { push @results, loc("Status of the Incident left unchanged; not all children were updated"); } my $pass = {}; $m->callback( CallbackName => 'BeforeDisplay', ARGSRef => \%ARGS, PassArguments => $pass, Ticket => $IncidentObj ); MaybeRedirectForResults( Actions => \@results, Path => "/RTIR/Incident/Display.html", Arguments => { %$pass, id => $id }, ); } my $title; if ($Status eq 'resolved') { $title = loc("Resolve Incident #[_1] (including children)", $id); } elsif ($Status eq 'abandoned') { $title = loc("Abandon Incident #[_1] (including children)", $id); } else { if ($All) { $title = loc("Incident #[_1]: Reply to All", $id); } else { $title = loc("Incident #[_1]: Reply to Reporters", $id); } } my @queues = ('Incident Reports'); push @queues, 'Investigations' if $All; push @queues, 'Blocks' unless RT->Config->Get('RTIR_DisableBlocksQueue'); $Query ||= RT::IR->ActiveQuery( Queue => \@queues ); my $BaseURL = "RTIR/Incident/Reply/?". $m->comp('/Elements/QueryString', id => $id, All => $All, Status => $Status, Action => $Action, ); my $candidates = RT::IR->IncidentChildren( $IncidentObj, Queue => \@queues )->Count; <%ARGS> $id => undef $All => 0 $Status => '' $Action => 'Correspond' $Query => '' $Format => RT->Config->Get('RTIRSearchResultFormats')->{'BulkReply'} $Rows => 0 $Page => 1 $OrderBy => RT->Config->Get('DefaultSearchResultOrderBy') $Order => RT->Config->Get('DefaultSearchResultOrder') @SelectedReports => () @SelectedInvestigations => () @SelectedBlocks => () $SubmitTicket => 0