From: Mark Wells Date: Thu, 10 Dec 2015 00:00:14 +0000 (-0800) Subject: automatic creation of subtask tickets, #34061 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=d0c5ecc6d52a28f583ec45afc3460dc613b38eba automatic creation of subtask tickets, #34061 --- diff --git a/rt/lib/RT/Condition/CustomFieldEquals.pm b/rt/lib/RT/Condition/CustomFieldEquals.pm new file mode 100644 index 000000000..69dedcbca --- /dev/null +++ b/rt/lib/RT/Condition/CustomFieldEquals.pm @@ -0,0 +1,39 @@ +package RT::Condition::CustomFieldEquals; +use base 'RT::Condition'; +use strict; + +=head2 IsApplicable + +If a custom field has a value equal to some specified value. + +=cut + +# Based on Chuck Boeheim's code posted on the RT Wiki 3/13/06 +# Simplified to avoid carrying old schema around. The new mechanics are that +# the ScripCondition's "Argument" is the custom field name = value. If the +# transaction initially sets the CF value to a the specified value, or +# changes it from not equaling to equaling the specified value, the condition +# returns true. +# Don't use this on custom fields that allow multiple values. + +sub IsApplicable { + my $self = shift; + my $trans = $self->TransactionObj; + my $scrip = $self->ScripObj; + my ($field, $value) = split('=', $self->Argument, 2); + + if ($trans->Type eq 'Create') { + return ($trans->TicketObj->FirstCustomFieldValue($field) eq $value); + } + if ($trans->Type eq 'CustomField') { + my $cf = RT::CustomField->new($self->CurrentUser); + $cf->Load($field); + return $trans->Field == $cf->Id + and ($trans->NewValue eq $value) + and ($trans->OldValue ne $value) + } + return undef; +} + +1; + diff --git a/rt/share/html/Admin/Queues/Tasks.html b/rt/share/html/Admin/Queues/Tasks.html new file mode 100755 index 000000000..30ec12b1c --- /dev/null +++ b/rt/share/html/Admin/Queues/Tasks.html @@ -0,0 +1,269 @@ +<& /Admin/Elements/Header, Title => $title &> +<& /Elements/Tabs &> +<& /Elements/ListActions, actions => \@results &> + +
+ +

+ +% if ( $PossibleCustomFields->Count > 0 ) { + + + +% } else { + +% } +

+ +% my (@links, @postponed); # not really used here +% my $idx = 1; +% foreach my $task_id (@task_ids, 'new') { +% # simulate creating the tickets, but don't evaluate any perl inclusions +% # in the content (_ActiveContent => 0 earlier) +% my ($ticket, $ticketargs); +% if ( $task_id eq 'new' ) { +% $ticket = RT::Ticket->new($session{'CurrentUser'}); +% $ticketargs = { +% Queue => $Queue, +% # any other defaults make sense here? +% }; +% } else { +% ($ticket, $ticketargs) = +% $Action->ParseLines($task_id, \@links, \@postponed); +% } +% my $subject = $ticketargs->{Subject}; +% my $subjectprefix = 0; +% if ( $subject =~ s/^\Q$SUBJECT_PREFIX\E// ) { +% $subjectprefix = 1; +% } + + + + + + + + + + + + + + + + + +% $idx++; +% } +
+

+

+
<&|/l&>Subject: + + /> <&|/l&>Prefix with main subject +
<&|/l&>In queue:<& /Elements/SelectQueue, + Name => "$idx-Queue", + ShowNullOption => 0, + Default => ($ticketargs->{Queue} || $Queue), + &>
<&|/l&>Content: +
+<& /Elements/Submit, Label => 'Save Changes' &> +
+<%init> +my @results; + +my $QueueObj = RT::Queue->new($session{'CurrentUser'}); +$QueueObj->Load($Queue); +Abort(loc("Queue [_1] not found",$Queue)) unless $QueueObj->Id; + +my $title = loc("Set up subtasks for queue [_1]", $QueueObj->Name); + +my $TEMPLATE_NAME = '[Subtask]'; +my $SCRIPCONDITION_NAME = '[Subtask] Queue='.$Queue; +my $SUBJECT_PREFIX = q({ $Tickets{'TOP'}->Subject }-); + +my ($Scrip, $ScripCondition, $Template, $CustomField); + +# SystemUser for the scrip so that the user doesn't need ACLs to edit scrips +# as such. all the scrip parameters are hardcoded anyway... + +$ScripCondition = RT::ScripCondition->new($RT::SystemUser); +$ScripCondition->LoadByCol('Name', $SCRIPCONDITION_NAME); + +$Template = RT::Template->new($session{'CurrentUser'}); +$Template->LoadByName( + Name => $TEMPLATE_NAME, + Queue => $Queue, +); + +$Scrip = RT::Scrip->new($RT::SystemUser); +{ + my $Scrips = RT::Scrips->new($RT::SystemUser); + $Scrips->LimitToQueue($Queue); + $Scrips->Limit( FIELD => 'Template', VALUE => $TEMPLATE_NAME ); + if ( $Scrips->Count > 0 ) { + $Scrip = $Scrips->First; + } +} + +# The CF name to test, and the value it must have to trigger the scrip. +my $cfid = $ARGS{ConditionCF}; +my $cfvalue = $ARGS{ConditionValue}; +$CustomField = RT::CustomField->new($session{'CurrentUser'}); +if ( $cfid ) { + $CustomField->Load($cfid); +} +my $cfname = $CustomField->Name; + +# if there's input from the form, process it into a new template content +my $new_content = ''; + +if ( $ARGS{task_id} ) { # actually contains numeric indices + my @task_ids = $ARGS{task_id}; + @task_ids = @{ $task_ids[0] } if ref($task_ids[0]); + foreach my $task_id (@task_ids) { + # find the inputs for this task_id + my %task_opts = map { $_ => $ARGS{$_} } + grep /^$task_id-/, keys(%ARGS); + my $task_content = "===Create-Ticket: $task_id +Depended-On-By: TOP +CF-$cfname: +"; + # any other static content can go here, but we always want the child + # ticket relationship, and we want to force the ConditionCF to be empty + # to avoid recursion. + + my $has_content = 0; + + # special case: automate prefixing the main ticket subject + if ( $task_opts{"$task_id-SubjectPrefix"} ) { + $task_opts{"$task_id-Subject"} = + $SUBJECT_PREFIX . $task_opts{"$task_id-Subject"}; + } + + foreach my $key (sort keys %task_opts) { + $key =~ /^$task_id-(.*)/; + my $tag = $1; + my $value = $task_opts{$key}; + $value =~ s/^\s*//; + $value =~ s/\s*$//; + $value =~ s/\r//g; + $task_content .= "$tag: $value\n"; + # only create a task if the ticket has non-whitespace content + if ( lc($tag) eq 'content' and length($value) > 0 ) { + $task_content .= "ENDOFCONTENT\n"; + $has_content = 1; + } + } + if ( $has_content ) { + $new_content .= $task_content; + } + } + warn "NEW CONTENT:\n$new_content\n\n"; # XXX + + if ( ! $Template->Id ) { + my ( $val, $msg ) = $Template->Create( + Queue => $Queue, + Name => $TEMPLATE_NAME, + Description => 'Subtask tickets', + Type => 'Perl', + Content => $new_content, + ); + if (!$val) { + push @results, loc("Could not create template: [_1]", $msg); + } else { + push @results, loc("Template created"); + } + } elsif ( $Template->Content ne $new_content ) { # template needs updating + my ( $val, $msg ) = $Template->SetContent($new_content); + if (!$val) { + push @results, loc("Could not update template: [_1]", $msg); + } else { + push @results, loc("Template updated"); + } + } + + # Set up ScripCondition + if ( !$cfname ) { + push @results, loc("No custom field selected"); + } elsif ( length($cfvalue) == 0 ) { + push @results, loc("Custom field value is required"); + } elsif ( ! $ScripCondition->Id ) { + my ( $val, $msg ) = $ScripCondition->Create( + Name => $SCRIPCONDITION_NAME, + Description => "When CF.[$cfname] equals '$cfvalue'", + ExecModule => 'CustomFieldEquals', + Argument => "$cfname=$cfvalue", + ApplicableTransTypes => 'Any', + ); + if (!$val) { + push @results, loc("Could not create custom field condition: [_1]", $msg); + } else { + push @results, loc("Custom field condition created"); + } + } elsif ( $ScripCondition->Argument ne "$cfname=$cfvalue" ) { + my ( $val, $msg ) = $ScripCondition->SetArgument("$cfname=$cfvalue"); + if (!$val) { + push @results, loc("Could not set custom field condition: [_1]", $msg); + } else { + push @results, loc("Custom field condition set"); + } + } + + # Set up Scrip + if ( $Template->Id and ! $Scrip->Id ) { + my ($val, $msg) = $Scrip->Create( + Queue => $Queue, + Template => $Template->Id, + Description => 'Create subtasks for ' . $QueueObj->Name, + ScripCondition => $ScripCondition->Id, + ScripAction => 'Create Tickets', + ); + if (!$val) { + push @results, loc("Could not create scrip: [_1]", $msg); + } else { + push @results, loc("Scrip created"); + } + } # else don't need to create the scrip + + # even if $new_content is empty, there's no harm in letting the scrip and + # template exist with empty content. they just won't do anything. +} + +# CHANGES HAVE BEEN SAVED. +# Now prepare to (re-)display the form. + +# ask RT::Action::CreateTickets how it will parse the template +my $action_class = 'RT::Action::CreateTickets'; +$action_class->require; +my $Action = $action_class->new( + CurrentUser => $session{'CurrentUser'}, +); +# this will populate $Action with the 'create_tickets' hash +warn $Template->Content; +$Action->Parse( + Content => $Template->Content, + _ActiveContent => 0, +); +warn Dumper \$Action; +my @task_ids; +@task_ids = @{ $Action->{create_tickets} } if exists $Action->{create_tickets}; + +my $PossibleCustomFields = $QueueObj->TicketCustomFields; + + +<%ARGS> +$Queue => undef #queue id + diff --git a/rt/share/html/Elements/Tabs b/rt/share/html/Elements/Tabs index 3e28e2578..bc2badf4a 100755 --- a/rt/share/html/Elements/Tabs +++ b/rt/share/html/Elements/Tabs @@ -289,6 +289,8 @@ my $build_admin_menu = sub { my $txn_cfs = $queue->child( 'transaction-custom-fields' => title => loc('Transaction Custom Fields'), path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket-RT::Transaction&id='.$id ); + $queue->child( 'tasks' => title => loc('Subtasks'), path => "Admin/Queues/Tasks.html?Queue=".$id ); + $queue->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Queues/GroupRights.html?id=".$id ); $queue->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Queues/UserRights.html?id=" . $id ); $queue->child( 'history' => title => loc('History'), path => "/Admin/Queues/History.html?id=" . $id );