diff options
| -rw-r--r-- | FS/FS/Cron/rt_tasks.pm | 37 | ||||
| -rw-r--r-- | FS/FS/TicketSystem.pm | 70 | ||||
| -rw-r--r-- | rt/FREESIDE_MODIFIED | 1 | ||||
| -rw-r--r-- | rt/etc/RT_SiteConfig.pm | 4 | ||||
| -rwxr-xr-x | rt/lib/RT/Action/EscalateQueue.pm | 141 | ||||
| -rw-r--r-- | rt/lib/RT/Action/SetPriority_Local.pm | 47 | ||||
| -rw-r--r-- | rt/lib/RT/CustomFieldValues/Queues.pm | 30 | 
7 files changed, 306 insertions, 24 deletions
| diff --git a/FS/FS/Cron/rt_tasks.pm b/FS/FS/Cron/rt_tasks.pm index 066aeebde..26e305d59 100644 --- a/FS/FS/Cron/rt_tasks.pm +++ b/FS/FS/Cron/rt_tasks.pm @@ -42,6 +42,7 @@ sub rt_escalate {    foreach (qw(      Search::ActiveTicketsInQueue       Action::EscalatePriority +    Action::EscalateQueue      )) {      eval "use RT::$_";      die $@ if $@; @@ -51,7 +52,7 @@ sub rt_escalate {    # Mechanics:    # We're using EscalatePriority, so search in all queues that have a     # priority range defined. Select all active tickets in those queues and -  # LinearEscalate them. +  # EscalatePriority, then EscalateQueue them.    # to make some actions work without complaining    %void = map { $_ => "RT::$_"->new($CurrentUser) } @@ -61,6 +62,8 @@ sub rt_escalate {    # we might want to do, but escalation is the only one we do now.    my $queues = RT::Queues->new($CurrentUser);    $queues->UnLimit; +  my @actions = (); +  my @active_tickets = ();    while (my $queue = $queues->Next) {      if ( $queue->InitialPriority == $queue->FinalPriority ) {        warn "Queue '".$queue->Name."' (skipped)\n" if $DEBUG; @@ -76,8 +79,24 @@ sub rt_escalate {      $search->Prepare;      while (my $ticket = $tickets->Next) {        warn 'Ticket #'.$ticket->Id()."\n" if $DEBUG; -      # We don't need transaction stuff from rt-crontool here -      action($ticket, 'EscalatePriority', "CurrentTime:$time"); +      my @a = ( +        action($ticket, 'EscalatePriority', "CurrentTime:$time"), +        action($ticket, 'EscalateQueue') +      ); +      next if !@a; +      push @actions, @a; +      push @active_tickets, $ticket; # avoid RT's overzealous garbage collector +    } +  } +  foreach (grep {$_} @actions) { +    my ($val, $msg) = $_->Commit; +    if ( $DEBUG ) { +      if ($val) { +        warn "Action committed: ".ref($_)." #".$_->TicketObj->Id."\n"; +      } +      else { +        warn "Action returned $msg: #".$_->TicketObj->Id."\n"; +      }      }    }    return; @@ -98,11 +117,13 @@ sub action {      ScripAction   => $void{'ScripAction'},      CurrentUser   => $CurrentUser,    ); -  return unless $action_obj->Prepare; -  warn "Action prepared: $action\n" if $DEBUG; -  $action_obj->Commit; -  warn "Action committed: $action\n" if $DEBUG; -  return; +  if ( $action_obj->Prepare ) { +    warn "Action prepared: $action\n" if $DEBUG; +    return $action_obj; +  } +  else { +    return; +  }  }  1; diff --git a/FS/FS/TicketSystem.pm b/FS/FS/TicketSystem.pm index d53d2f679..f5c8e7dad 100644 --- a/FS/FS/TicketSystem.pm +++ b/FS/FS/TicketSystem.pm @@ -29,23 +29,61 @@ sub AUTOLOAD {  sub _upgrade_data {    return if $system ne 'RT_Internal'; -    my ($class, %opts) = @_; -  my ($t, $exec, @fields) = map { driver_name =~ /^mysql/i ? $_ : lc($_) } -  (qw( ScripConditions ExecModule -    Name Description ExecModule ApplicableTransTypes -    Creator Created LastUpdatedBy LastUpdated)); -  my $count_sql = "SELECT COUNT(*) FROM $t WHERE $exec = 'CustomFieldChange'"; -  my $sth = dbh->prepare($count_sql) or die dbh->errstr; -  $sth->execute or die $sth->errstr; -  my $total = $sth->fetchrow_arrayref->[0]; -  return if $total > 0; - -  my $insert_sql = "INSERT INTO $t (".join(',',@fields).") VALUES (". -  "'On Custom Field Change', 'When a custom field is changed to some value', -  'CustomFieldChange', 'Any', 1, CURRENT_DATE, 1, CURRENT_DATE )"; -  $sth = dbh->prepare($insert_sql) or die dbh->errstr; -  $sth->execute or die $sth->errstr; + +  # go ahead and use the RT API for this +   +  FS::TicketSystem->init; +  my $session = FS::TicketSystem->session(); +  my $CurrentUser = $session->{'CurrentUser'} +    or die 'freeside-upgrade must run as a valid RT user'; + +  # CustomFieldChange scrip condition +  my $ScripCondition = RT::ScripCondition->new($CurrentUser); +  $ScripCondition->LoadByCols('ExecModule' => 'CustomFieldChange'); +  if (!defined($ScripCondition->Id)) { +    my ($val, $msg) = $ScripCondition->Create( +      'Name' => 'On Custom Field Change', +      'Description' => 'When a custom field is changed to some value', +      'ExecModule' => 'CustomFieldChange', +      'ApplicableTransTypes' => 'Any', +    ); +    die $msg if !$val; +  } + +  # SetPriority scrip action +  my $ScripAction = RT::ScripAction->new($CurrentUser); +  $ScripAction->LoadByCols('ExecModule' => 'SetPriority'); +  if (!defined($ScripAction->Id)) { +    my ($val, $msg) = $ScripAction->Create( +      'Name' => 'Set Priority', +      'Description' => 'Set ticket priority', +      'ExecModule' => 'SetPriority', +      'Argument' => '', +    ); +    die $msg if !$val; +  } + +  # EscalateQueue custom field and friends +  my $CF = RT::CustomField->new($CurrentUser); +  $CF->Load('EscalateQueue'); +  if (!defined($CF->Id)) { +    my ($val, $msg) = $CF->Create( +      'Name' => 'EscalateQueue', +      'Type' => 'Select', +      'MaxValues' => 1, +      'LookupType' => 'RT::Queue', +      'Description' => 'Escalate to Queue', +      'ValuesClass' => 'RT::CustomFieldValues::Queues', #magic! +    ); +    die $msg if !$val; +    my $OCF = RT::ObjectCustomField->new($CurrentUser); +    ($val, $msg) = $OCF->Create( +      'CustomField' => $CF->Id, +      'ObjectId' => 0, +    ); +    die $msg if !$val; +  }    return;  } diff --git a/rt/FREESIDE_MODIFIED b/rt/FREESIDE_MODIFIED index 70801d50b..5bef0bf49 100644 --- a/rt/FREESIDE_MODIFIED +++ b/rt/FREESIDE_MODIFIED @@ -17,6 +17,7 @@ lib/RT/Scrip_Overlay.pm #create ticket on custom field change  lib/RT/Action/CreateTickets.pm #create ticket on custom field change  lib/RT/Action/EscalatePriority.pm #ticket escalation  lib/RT/Action/EscalateQueue.pm #ticket escalation +lib/RT/Action/SetPriority_Local.pm #ticket escalation  lib/RT/CustomFieldValues/Queues.pm #ticket escalation  lib/RT/Condition/CustomFieldChange.pm #create ticket on custom field change  lib/RT/Interface/Web_Vendor.pm diff --git a/rt/etc/RT_SiteConfig.pm b/rt/etc/RT_SiteConfig.pm index f833f0d52..872b8c3a5 100644 --- a/rt/etc/RT_SiteConfig.pm +++ b/rt/etc/RT_SiteConfig.pm @@ -53,4 +53,8 @@ Set($MessageBoxRichTextHeight, 368);  #Set($QuickCreateRedirect, 1);  #Set(@Plugins,(qw(Extension::QuickDelete RT::FM))); + +#used in ticket escalation +Set(@CustomFieldValuesSources, ('RT::CustomFieldValues::Queues')); +  1; diff --git a/rt/lib/RT/Action/EscalateQueue.pm b/rt/lib/RT/Action/EscalateQueue.pm new file mode 100755 index 000000000..adafbdfb7 --- /dev/null +++ b/rt/lib/RT/Action/EscalateQueue.pm @@ -0,0 +1,141 @@ +=head1 NAME + +RT::Action::EscalateQueue - move a ticket to a different queue when it reaches its final priority + +=head1 DESCRIPTION + +EscalateQueue is a ScripAction that will move a ticket to a new  +queue when its priority equals its final priority.  It is designed  +to be used with LinearEscalate or another action that increments +ticket priority on some schedule.  Like those actions, it is intended  +to be called from an escalation tool. + +=head1 CONFIGURATION + +FinalPriority is a ticket property, defaulting to the queue property. + +EscalateQueue is a queue custom field using RT::CustomFieldValues::Queue  +as its data source (that is, it refers to another queue).  Tickets at  +FinalPriority will be moved to that queue. + +From a shell you can use the following command: + +    rt-crontool --search RT::Search::FromSQL --search-arg \ +    "(Status='new' OR Status='open' OR Status = 'stalled')" \ +    --action RT::Action::EscalateQueue + +No action argument is needed.  Each ticket will be escalated based on the +EscalateQueue property of its current queue. + +=cut + +package RT::Action::EscalateQueue; + +use strict; +use warnings; +use base qw(RT::Action); + +our $VERSION = '0.01'; + +#What does this type of Action does + +sub Describe { +    my $self = shift; +    my $class = ref($self) || $self; +    return "$class will move a ticket to its escalation queue when it reaches its final priority." +} + +#This Prepare only returns 1 if the ticket will be escalated. + +sub Prepare { +    my $self = shift; + +    my $ticket = $self->TicketObj; +    my $queue = $ticket->QueueObj; +    my $new_queue = $queue->FirstCustomFieldValue('EscalateQueue'); + +    my $ticketid = 'Ticket #'.$ticket->Id; #for debug messages +    if ( $ticket->InitialPriority == $ticket->FinalPriority ) { +        $RT::Logger->debug("$ticketid has no priority range.  Not escalating."); +        return 0; +    } + +    if ( $ticket->Priority == $ticket->FinalPriority ) { +        if (!$new_queue) { +            $RT::Logger->debug("$ticketid has no escalation queue.  Not escalating."); +            return 0; +        } +        if ($new_queue eq $queue->Name) { +            $RT::Logger->debug("$ticketid would be escalated to its current queue."); +            return 0; +        } +        $self->{'new_queue'} = $new_queue; +        return 1; +    } +    return 0; +} + +# whereas Commit returns 1 if it succeeds at whatever it's doing +sub Commit { +    my $self = shift; + +    return 1 if !exists($self->{'new_queue'}); + +    my $ticket = $self->TicketObj; +    my $ticketid = 'Ticket #'.$ticket->Id; +    my $new_queue = RT::Queue->new($ticket->CurrentUser); +    $new_queue->Load($self->{'new_queue'}); +    if ( ! $new_queue ) { +        $RT::Logger->debug("Escalation queue ".$self->{'new_queue'}." not found."); +        return 0; +    } +  +    $RT::Logger->debug("Escalating $ticket from ".$ticket->QueueObj->Name . +        ' to '.  $new_queue->Name . ', FinalPriority '.$new_queue->FinalPriority); + +    my ( $val, $msg ) = $ticket->SetQueue($self->{'new_queue'}); +    if (! $val) { +        $RT::Logger->error( "Couldn't set queue: $msg" ); +        return (0, $msg); +    } + +    # Set properties of the ticket according to its new queue, so that  +    # escalation Does What You Expect.  Don't record transactions for this; +    # the queue change should be enough. + +    ( $val, $msg ) = $ticket->_Set( +      Field => 'FinalPriority', +      Value => $new_queue->FinalPriority, +      RecordTransaction => 0, +    ); +    if (! $val) { +        $RT::Logger->error( "Couldn't set new final priority: $msg" ); +        return (0, $msg); +    } +    my $Due = new RT::Date( $ticket->CurrentUser ); +    if ( my $due_in = $new_queue->DefaultDueIn ) { +        $Due->SetToNow; +        $Due->AddDays( $due_in ); +    } +    ( $val, $msg ) = $ticket->_Set( +      Field => 'Due', +      Value => $Due->ISO, +      RecordTransaction => 0, +    ); +    if (! $val) { +        $RT::Logger->error( "Couldn't set new due date: $msg" ); +        return (0, $msg); +    } +    return 1; +} + +1; + +=head1 AUTHOR + +Mark Wells E<lt>mark@freeside.bizE<gt> + +Based on in part LinearEscalate by Kevin Riggle E<lt>kevinr@bestpractical.comE<gt> +and Ruslan Zakirov E<lt>ruz@bestpractical.comE<gt> . + +=cut diff --git a/rt/lib/RT/Action/SetPriority_Local.pm b/rt/lib/RT/Action/SetPriority_Local.pm new file mode 100644 index 000000000..efaadc961 --- /dev/null +++ b/rt/lib/RT/Action/SetPriority_Local.pm @@ -0,0 +1,47 @@ +package RT::Action::SetPriority; +use strict; +no warnings 'redefine'; + +# Extension to allow relative priority changes: +# if Argument is "R" followed by a value, it's  +# relative to current priority. +sub Commit { +    my $self = shift; +    my ($rel, $val); +    my $arg = $self->Argument; +    if ( $arg ) { +      ($rel, $val) = ( $arg =~ /^(r?)(-?\d+)$/i ); +      if (!length($val)) { +        warn "Bad argument to SetPriority: '$arg'\n"; +        return 0; +      } +    } +    else { +      my %Rules = $self->Rules; +      $rel = length($Rules{'inc'}) ? 1 : 0; +      $val = $Rules{'inc'} || $Rules{'set'}; +      if ($val !~ /^[+-]?\d+$/) { +        warn "Bad argument to SetPriority: '$val'\n"; +        return 0; +      } +    } +    $val += $self->TicketObj->Priority if $rel; +    $self->TicketObj->SetPriority($val); +} + +sub Options { +  ( +    { +      'name'    => 'set', +      'label'   => 'Set to value', +      'type'    => 'text', +    }, +    { +      'name'    => 'inc', +      'label'   => 'Increment by', +      'type'    => 'text', +    }, +  ) +} + +1; diff --git a/rt/lib/RT/CustomFieldValues/Queues.pm b/rt/lib/RT/CustomFieldValues/Queues.pm new file mode 100644 index 000000000..59529b6ac --- /dev/null +++ b/rt/lib/RT/CustomFieldValues/Queues.pm @@ -0,0 +1,30 @@ +package RT::CustomFieldValues::Queues; + +use strict; +use warnings; + +use base qw(RT::CustomFieldValues::External); + +sub SourceDescription { +    return 'RT ticket queues'; +} + +sub ExternalValues { +    my $self = shift; + +    my @res; +    my $i = 0; +    my $queues = RT::Queues->new( $self->CurrentUser ); +    $queues->UnLimit; +    $queues->OrderByCols( { FIELD => 'Name' } ); +    while( my $queue = $queues->Next ) { +        push @res, { +            name        => $queue->Name, +            description => $queue->Description, +            sortorder   => $i++, +        }; +    } +    return \@res; +} + +1; | 
