From 367982377a92682f3da5b06022294ca69a2195f4 Mon Sep 17 00:00:00 2001 From: sunnavy Date: Fri, 2 Feb 2024 16:42:48 -0500 Subject: [PATCH] Support to update maps of a lifecycle via JSON on Advanced page --- lib/RT/Lifecycle.pm | 11 ++- share/html/Admin/Lifecycles/Advanced.html | 82 ++++++++++++++++++++++- t/web/lifecycle_mappings.t | 56 ++++++++++++++++ 3 files changed, 145 insertions(+), 4 deletions(-) diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm index 0f7d08b2575..b978a4c04ed 100644 --- a/lib/RT/Lifecycle.pm +++ b/lib/RT/Lifecycle.pm @@ -1005,12 +1005,15 @@ sub UpdateLifecycle { return (1, $CurrentUser->loc("Lifecycle [_1] updated", $name)); } -=head2 UpdateMaps( CurrentUser => undef, Maps => undef ) +=head2 UpdateMaps( CurrentUser => undef, Maps => undef, Name => undef ) Update lifecycle maps. Returns (STATUS, MESSAGE). STATUS is true if succeeded, otherwise false. +Note that the Maps in argument will be merged into existing maps. To delete +all existing items for a lifecycle before merging, pass its Name. + =cut sub UpdateMaps { @@ -1018,14 +1021,18 @@ sub UpdateMaps { my %args = ( CurrentUser => undef, Maps => undef, + Name => undef, @_, ); my $CurrentUser = $args{CurrentUser}; my $lifecycles = RT->Config->Get('Lifecycles'); + my $all_maps = $lifecycles->{__maps__} || {}; + my @keep_keys = grep { $args{Name} && !/^\Q$args{Name}\E -> | -> \Q$args{Name}\E$/ } keys %$all_maps; + %{ $lifecycles->{__maps__} } = ( - %{ $lifecycles->{__maps__} || {} }, + map( { $_ => $all_maps->{$_} } @keep_keys ), %{ $args{Maps} }, ); diff --git a/share/html/Admin/Lifecycles/Advanced.html b/share/html/Admin/Lifecycles/Advanced.html index 09e0c1355e7..ddc7452a64b 100644 --- a/share/html/Admin/Lifecycles/Advanced.html +++ b/share/html/Admin/Lifecycles/Advanced.html @@ -54,10 +54,11 @@ -
+ + - + <&| /Widgets/TitleBox, title => loc('Basics'), content_class => 'mx-auto width-sm' &>
@@ -83,8 +84,40 @@ <& /Elements/Submit, Label => loc('Delete'), Name => 'Delete' &>
+
+ + +
+ + + <&| /Widgets/TitleBox, title => loc('Mappings'), content_class => 'mx-auto width-sm' &> + +
+ + + +
+ + + +
+
+ <& /Elements/Submit, Label => loc('Validate Mappings'), Name => 'ValidateMaps' &> +
+
+ <& /Elements/Submit, Label => loc('Save Mappings'), Name => 'UpdateMaps' &> +
+
+ +
+ + <%INIT> my ($title, @results); my $LifecycleObj = RT::Lifecycle->new(); @@ -100,6 +133,13 @@ push @results, loc("The graphical lifecycle builder is not currently supported on IE 11. You can update the lifecycle configuration using the Advanced tab or access the lifecycle editor in a supported browser."); } +if ( !defined $Maps && ( my $all_maps = RT->Config->Get('Lifecycles')->{__maps__} ) ) { + for my $item ( grep {/^\Q$Name\E -> | -> \Q$Name\E$/} keys %$all_maps ) { + $Maps->{$item} = $all_maps->{$item}; + } + $Maps = JSON::to_json( $Maps || {}, { canonical => 1, pretty => 1 } ); +} + my $redirect_to ='/Admin/Lifecycles/Advanced.html'; my %redirect_args; @@ -157,6 +197,41 @@ ); } } +elsif ( $ValidateMaps || $UpdateMaps ) { + my $maps = JSON::from_json($Maps || '{}'); + + my ( $valid, @warnings ) + = $LifecycleObj->ValidateLifecycleMaps( Maps => $maps, CurrentUser => $session{CurrentUser} ); + + my $updated; + if ($valid) { + if ($ValidateMaps) { + push @results, loc('Mappings is valid'); + } + else { + # Maps will be merged into existing value, here we remove existing values so admins can delete items + + ( $updated, my $msg ) = RT::Lifecycle->UpdateMaps( + CurrentUser => $session{CurrentUser}, + Maps => $maps, + Name => $Name, + ); + push @results, $msg; + } + + } + else { + push @results, @warnings; + } + + %redirect_args = ( + Name => $Name, + Type => $Type, + + # Get the latest canonicalized data if updated successfully + $updated ? () : ( Maps => $Maps ), + ); +} MaybeRedirectForResults( Actions => \@results, @@ -172,4 +247,7 @@ $Validate => undef $Update => undef $Delete => undef +$ValidateMaps => undef +$UpdateMaps => undef +$Maps => undef diff --git a/t/web/lifecycle_mappings.t b/t/web/lifecycle_mappings.t index f9efafa1b79..dcaec2b6511 100644 --- a/t/web/lifecycle_mappings.t +++ b/t/web/lifecycle_mappings.t @@ -172,6 +172,62 @@ diag "Test updating sales-engineering mappings"; ); } +diag "Test advanced mappings"; +{ + $m->get_ok( $url . '/Admin/Lifecycles/Advanced.html?Type=ticket&Name=sales-engineering' ); + my $form = $m->form_name('ModifyLifecycleAdvancedMappings'); + + require JSON; + my $maps = JSON::from_json( $form->value('Maps') ); + is_deeply( + $maps, + { + 'sales-engineering -> default' => { + 'rejected' => 'rejected', + 'resolved' => 'resolved', + 'deleted' => 'deleted', + 'engineering' => 'open', + 'stalled' => 'stalled', + 'sales' => 'new' + } + }, + 'Correct current value' + ); + + $maps->{'default -> sales-engineering'} = { 'new' => 'sales', }; + + $m->submit_form_ok( + { + fields => { + Maps => JSON::to_json($maps), + Name => "sales-engineering", + Type => "ticket", + }, + button => 'UpdateMaps', + }, + 'Update maps' + ); + $m->content_contains('Lifecycle mappings updated'); + $form = $m->form_name('ModifyLifecycleAdvancedMappings'); + is_deeply( $maps, JSON::from_json( $form->value('Maps') ), 'Correct updated value' ); + + $m->submit_form_ok( + { + fields => { + Maps => '', + Name => "sales-engineering", + Type => "ticket", + }, + button => 'UpdateMaps', + }, + 'Clear maps' + ); + $m->content_contains('Lifecycle mappings updated'); + $form = $m->form_name('ModifyLifecycleAdvancedMappings'); + $form->value( "Maps", "{}", 'Maps got cleared' ); +} + + sub reload_lifecycle { # to get rid of the warning of: # you're changing config option in a test file when server is active