[Rt-commit] rt branch, 4.6/lifecycle-ui-dev, updated. rt-4.4.4-636-g22fa7153f8
Craig Kaiser
craig at bestpractical.com
Mon Feb 3 17:32:18 EST 2020
The branch, 4.6/lifecycle-ui-dev has been updated
via 22fa7153f8db11e1c836012f9ceeef302928ffda (commit)
via 7a8e1c4783d443ffebfea3611fa43d091c92652b (commit)
via 7c07622c6fe81c7dc846c50e26b3bd6797a8bfff (commit)
via d84b4532033869fe438fcd628220c9bedd472723 (commit)
via 5b812b1724a405a9f34dd9e3530b4092cfac8a48 (commit)
via 475957a273b99325da2b16b9169b9f4a71973bc7 (commit)
from 527bd331a431716a004e9628bdce40c7ffc74e42 (commit)
Summary of changes:
lib/RT/Interface/Web.pm | 6 +
lib/RT/Interface/Web/MenuBuilder.pm | 4 +
lib/RT/Lifecycle.pm | 175 +++++++++++++++++++--
share/html/Admin/Lifecycles/Actions.html | 175 +++++++++++++++++++++
.../Lifecycles/{Modify.html => Advanced.html} | 79 ++++++----
share/html/Admin/Lifecycles/Modify.html | 2 +-
share/html/Admin/Lifecycles/Rights.html | 161 +++++++++++++++++++
share/html/Elements/Lifecycle/Graph | 16 +-
share/static/js/lifecycleui-editor.js | 34 ++++
9 files changed, 598 insertions(+), 54 deletions(-)
create mode 100644 share/html/Admin/Lifecycles/Actions.html
copy share/html/Admin/Lifecycles/{Modify.html => Advanced.html} (56%)
create mode 100644 share/html/Admin/Lifecycles/Rights.html
- Log -----------------------------------------------------------------
commit 475957a273b99325da2b16b9169b9f4a71973bc7
Author: Craig Kaiser <craig at bestpractical.com>
Date: Fri Jan 31 15:27:10 2020 -0500
Ensure lifecycle cache is updated across threads when flagged
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 688e459ea1..b230628593 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -275,6 +275,12 @@ sub HandleRequest {
Module::Refresh->refresh;
}
+ my $lifecycle = RT::Lifecycle->new( RT->SystemUser );
+ if ( RT->System->LifecycleCacheNeedsUpdate > $lifecycle->{'lifecycle_cache_time'} ) {
+ $lifecycle->FillCache();
+ }
+
+
RT->Config->RefreshConfigFromDatabase();
$HTML::Mason::Commands::r->content_type("text/html; charset=utf-8");
diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 4a33fe8909..88c8c3a54e 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -56,8 +56,6 @@ our %LIFECYCLES;
our %LIFECYCLES_CACHE;
our %LIFECYCLES_TYPES;
-my $lifecycle_cache_time = 0;
-
# cache structure:
# {
# lifecycle_x => {
@@ -109,6 +107,7 @@ Simple constructor, takes no arguments.
sub new {
my $proto = shift;
my $self = bless {}, ref($proto) || $proto;
+ $self->_Init(@_);
return $self;
}
@@ -144,12 +143,6 @@ sub Load {
);
$args{'Type'} = $args{'Type'} // 'ticket';
- my $needs_update = RT->System->LifecycleCacheNeedsUpdate;
- if ( $needs_update > $lifecycle_cache_time) {
- $self->FillCache();
- $lifecycle_cache_time = $needs_update;
- }
-
my $load_class = sub {
if (defined $args{Name} and exists $LIFECYCLES_CACHE{ $args{Name} }) {
$self->{'name'} = $args{Name};
@@ -212,12 +205,6 @@ sub ListAll {
my $self = shift;
my $for = shift || 'ticket';
- my $needs_update = RT->System->LifecycleCacheNeedsUpdate;
- if ( $needs_update > $lifecycle_cache_time) {
- $self->FillCache();
- $lifecycle_cache_time = $needs_update;
- }
-
return sort grep {$LIFECYCLES_CACHE{$_}{type} eq $for}
grep $_ ne '__maps__', keys %LIFECYCLES_CACHE;
}
@@ -802,8 +789,9 @@ sub FillCache {
$class->RegisterRights if $class->require
and $class->can("RegisterRights");
}
-
- $lifecycle_cache_time = time;
+ if ( ref $self ) {
+ $self->{'lifecycle_cache_time'} = time;
+ }
return;
}
@@ -1017,4 +1005,9 @@ sub UpdateMaps {
return (1, $CurrentUser->loc("Lifecycle mappings updated"));
}
+sub _Init {
+ my $self = shift;
+ $self->{'lifecycle_cache_time'} = 0;
+}
+
1;
commit 5b812b1724a405a9f34dd9e3530b4092cfac8a48
Author: Craig Kaiser <craig at bestpractical.com>
Date: Mon Feb 3 14:07:52 2020 -0500
Add advanced page for lifecycle UI configurations
diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 88c8c3a54e..0bbdb3e566 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -1005,6 +1005,137 @@ sub UpdateMaps {
return (1, $CurrentUser->loc("Lifecycle mappings updated"));
}
+sub ValidateLifecycle {
+ my $self = shift;
+ my $lifecycle = shift;
+
+ my @warnings;
+
+ my $type = $lifecycle->{type} ||= 'ticket';
+ $LIFECYCLES_TYPES{$type} ||= {
+ '' => [],
+ initial => [],
+ active => [],
+ inactive => [],
+ actions => [],
+ };
+
+ my @statuses;
+ $lifecycle->{canonical_case} = {};
+ foreach my $category ( qw(initial active inactive) ) {
+ for my $status (@{ $lifecycle->{ $category } || [] }) {
+ if (exists $lifecycle->{canonical_case}{lc $status}) {
+ push @warnings, "Duplicate status @{[lc $status]} in lifecycle ".$self->Name;
+ } else {
+ $lifecycle->{canonical_case}{lc $status} = $status;
+ }
+ push @{ $LIFECYCLES_TYPES{$type}{$category} }, $status;
+ push @statuses, $status;
+ }
+ }
+
+ # Lower-case for consistency
+ # ->{actions} are handled below
+ for my $state (keys %{ $lifecycle->{defaults} || {} }) {
+ my $status = $lifecycle->{defaults}{$state};
+ push @warnings, "Nonexistant status @{[lc $status]} in default states in ".$self->Name." lifecycle"
+ unless $lifecycle->{canonical_case}{lc $status};
+ # $lifecycle->{defaults}{$state} =
+ # $lifecycle->{canonical_case}{lc $status} || lc $status;
+ }
+ for my $from (keys %{ $lifecycle->{transitions} || {} }) {
+ push @warnings, "Nonexistant status @{[lc $from]} in transitions in ".$self->Name." lifecycle"
+ unless $from eq '' or $lifecycle->{canonical_case}{lc $from};
+ my @statuses = @{$lifecycle->{transitions}{$from}};
+
+ for my $status ( @statuses ) {
+ push @warnings, "Nonexistant status @{[lc $status]} in transitions in ".$self->Name." lifecycle"
+ unless $lifecycle->{canonical_case}{lc $status};
+ push @{ $lifecycle->{transitions}{lc $from} },
+ }
+ }
+ my $rights = $lifecycle->{rights} || {};
+
+ for my $schema (keys %{$rights}) {
+ my ($from, $to) = split /\s*->\s*/, $schema, 2;
+ unless ($from and $to) {
+ push @warnings, "Invalid right transition $schema in ".$self->Name." lifecycle";
+ next;
+ }
+ push @warnings, "Nonexistant status @{[lc $from]} in right transition in ".$self->Name." lifecycle"
+ unless $from eq '*' or $lifecycle->{canonical_case}{lc $from};
+ push @warnings, "Nonexistant status @{[lc $to]} in right transition in ".$self->Name." lifecycle"
+ unless $to eq '*' or $lifecycle->{canonical_case}{lc $to};
+
+ push @warnings, "Invalid right name ($lifecycle->{rights}{$schema}) in ".$self->Name." lifecycle; right names must be ASCII"
+ if $lifecycle->{rights}{$schema} =~ /\P{ASCII}/;
+
+ push @warnings, "Invalid right name ($lifecycle->{rights}{$schema}) in ".$self->Name." lifecycle; right names must be <= 25 characters"
+ if length($lifecycle->{rights}{$schema}) > 25;
+
+ $lifecycle->{rights}{lc($from) . " -> " .lc($to)}
+ = $lifecycle->{rights}{$schema};
+ }
+
+ my %seen;
+ @statuses = grep !$seen{ lc $_ }++, @statuses;
+ $lifecycle->{''} = \@statuses;
+
+ unless ( $lifecycle->{'transitions'}{''} ) {
+ $lifecycle->{'transitions'}{''} = [ grep lc $_ ne 'deleted', @statuses ];
+ }
+
+ my @actions;
+ if ( ref $lifecycle->{'actions'} eq 'HASH' ) {
+ foreach my $k ( sort keys %{ $lifecycle->{'actions'} } ) {
+ push @actions, $k, $lifecycle->{'actions'}{ $k };
+ }
+ } elsif ( ref $lifecycle->{'actions'} eq 'ARRAY' ) {
+ @actions = @{ $lifecycle->{'actions'} };
+ }
+
+ $lifecycle->{'actions'} = [];
+ while ( my ($transition, $info) = splice @actions, 0, 2 ) {
+ my ($from, $to) = split /\s*->\s*/, $transition, 2;
+ unless ($from and $to) {
+ push @warnings, "Invalid action status change $transition in ".$self->Name." lifecycle";
+ next;
+ }
+ push @warnings, "Nonexistant status @{[lc $from]} in action in ".$self->Name." lifecycle"
+ unless $from eq '*' or $lifecycle->{canonical_case}{lc $from};
+ push @warnings, "Nonexistant status @{[lc $to]} in action in ".$self->Name." lifecycle"
+ unless $to eq '*' or $lifecycle->{canonical_case}{lc $to};
+ push @{ $lifecycle->{'actions'} },
+ { %$info,
+ from => ($lifecycle->{canonical_case}{lc $from} || lc $from),
+ to => ($lifecycle->{canonical_case}{lc $to} || lc $to), };
+ }
+
+ # Lower-case the transition maps
+ for my $mapname (keys %{ $LIFECYCLES_CACHE{'__maps__'} || {} }) {
+ my ($from, $to) = split /\s*->\s*/, $mapname, 2;
+ unless ($from and $to) {
+ push @warnings, "Invalid lifecycle mapping $mapname";
+ next;
+ }
+ push @warnings, "Nonexistant lifecycle $from in $mapname lifecycle map"
+ unless $LIFECYCLES_CACHE{$from};
+ push @warnings, "Nonexistant lifecycle $to in $mapname lifecycle map"
+ unless $LIFECYCLES_CACHE{$to};
+ my $map = $LIFECYCLES_CACHE{'__maps__'}{$mapname};
+ for my $status (keys %{ $map }) {
+ push @warnings, "Nonexistant status @{[lc $status]} in $from in $mapname lifecycle map"
+ if $LIFECYCLES_CACHE{$from}
+ and not $LIFECYCLES_CACHE{$from}{canonical_case}{lc $status};
+ push @warnings, "Nonexistant status @{[lc $map->{$status}]} in $to in $mapname lifecycle map"
+ if $LIFECYCLES_CACHE{$to}
+ and not $LIFECYCLES_CACHE{$to}{canonical_case}{lc $map->{$status}};
+ }
+ }
+
+ return @warnings;
+}
+
sub _Init {
my $self = shift;
$self->{'lifecycle_cache_time'} = 0;
diff --git a/share/html/Admin/Lifecycles/Modify.html b/share/html/Admin/Lifecycles/Actions.html
similarity index 78%
copy from share/html/Admin/Lifecycles/Modify.html
copy to share/html/Admin/Lifecycles/Actions.html
index 5483bcc511..f587b115ec 100644
--- a/share/html/Admin/Lifecycles/Modify.html
+++ b/share/html/Admin/Lifecycles/Actions.html
@@ -49,25 +49,15 @@
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
-<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/static/js/farbtastic.js"></script>
-
-<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Modify.html" name="ModifyLifecycle" method="post" enctype="multipart/form-data">
- <input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
- <input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
- <& /Elements/Lifecycle/Graph, Lifecycle => $LifecycleObj->Name &>
- <div class="col-md-12">
- <& /Elements/Submit, Label => loc('Save Changes') &>
- </div>
-</form>
<%INIT>
my ($title, @results);
my $LifecycleObj = RT::Lifecycle->new();
$LifecycleObj->Load(Name => $Name, Type => $Type);
Abort("Invalid lifecycle") unless $LifecycleObj->Name
- && $LifecycleObj->{data}{type} eq $Type;
+ && $LifecycleObj->{data}{type} eq $Type;
-$title = loc("Modify lifecycle [_1]", $LifecycleObj->Name);
+$title = loc("Rights for lifecycle [_1]", $LifecycleObj->Name);
if ($Config) {
my $LifecycleConfiguration = JSON::from_json($LifecycleConfiguration);
@@ -78,10 +68,10 @@ if ($Config) {
Configuration => $LifecycleConfiguration,
);
if ( $ok ) {
- push @results, "Lifecycle updated";
+ push @results, "Lifecycle updated";
}
else {
- push @results, "An error occured when attempting to update lifecycle, see RT log for more info.";
+ push @results, "An error occured when attempting to update lifecycle, see RT log for more info.";
}
}
@@ -97,3 +87,4 @@ $Type => undef
$Config => undef
$LifecycleConfiguration => undef
</%ARGS>
+
\ No newline at end of file
diff --git a/share/html/Admin/Lifecycles/Modify.html b/share/html/Admin/Lifecycles/Advanced.html
similarity index 56%
copy from share/html/Admin/Lifecycles/Modify.html
copy to share/html/Admin/Lifecycles/Advanced.html
index 5483bcc511..08e8d37fa0 100644
--- a/share/html/Admin/Lifecycles/Modify.html
+++ b/share/html/Admin/Lifecycles/Advanced.html
@@ -49,51 +49,76 @@
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
-<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/static/js/farbtastic.js"></script>
-
-<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Modify.html" name="ModifyLifecycle" method="post" enctype="multipart/form-data">
+<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Advanced.html" name="ModifyLifecycleAdvanced" method="post" enctype="multipart/form-data">
<input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
<input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
- <& /Elements/Lifecycle/Graph, Lifecycle => $LifecycleObj->Name &>
- <div class="col-md-12">
- <& /Elements/Submit, Label => loc('Save Changes') &>
+
+ <div class="form-row">
+ <span class="col-md-12">
+ <textarea class="form-control" cols="170" rows="20" name="Config" id="Config"><% JSON::to_json($config, { canonical => 1, pretty => 1 }) |n %></textarea>
+ </span>
+ </div>
+
+ <div class="form-row">
+ <div class="col-md-6 d-flex justify-content-between w-100">
+ <& /Elements/Submit, Label => loc('Validate lifecycle'), Name => 'ValidateLifecycle', id => 'ValidateLifecycle' &>
+ </div>
+ <div class="col-md-6">
+ <& /Elements/Submit, Label => loc('Update lifecycle'), Name => 'UpdateLifecycle', id => 'UpdateLifecycle' &>
+ </div>
+ </div>
+ <div class="form-row">
+ <div class="col-md-auto">
+ <div class="alert alert-danger" style="display:none">JSON structure invalid</div>
+ </div>
+ </div>
+
</div>
</form>
+
<%INIT>
my ($title, @results);
my $LifecycleObj = RT::Lifecycle->new();
$LifecycleObj->Load(Name => $Name, Type => $Type);
Abort("Invalid lifecycle") unless $LifecycleObj->Name
- && $LifecycleObj->{data}{type} eq $Type;
+ && $LifecycleObj->{data}{type} eq $Type;
$title = loc("Modify lifecycle [_1]", $LifecycleObj->Name);
-if ($Config) {
- my $LifecycleConfiguration = JSON::from_json($LifecycleConfiguration);
- my ($ok, $msg) = RT::Lifecycle->UpdateLifecycle(
- CurrentUser => $session{CurrentUser},
- LifecycleObj => $LifecycleObj,
- NewConfig => JSON::from_json($Config),
- Configuration => $LifecycleConfiguration,
- );
- if ( $ok ) {
- push @results, "Lifecycle updated";
- }
- else {
- push @results, "An error occured when attempting to update lifecycle, see RT log for more info.";
+my $config = RT->Config->Get('Lifecycles')->{$LifecycleObj->Name};
+
+if ( $UpdateLifecycle or $ValidateLifecycle ) {
+ my @warnings = $LifecycleObj->ValidateLifecycle( JSON::from_json($Config) );
+ push @results, @warnings;
+ push @results, loc("Lifecycle is valid") if scalar @warnings eq 0;
+
+ $config = JSON::from_json($Config);
+
+ if ( $UpdateLifecycle && $Config ) {
+ my ($ok, $msg) = RT::Lifecycle->UpdateLifecycle(
+ CurrentUser => $session{CurrentUser},
+ LifecycleObj => $LifecycleObj,
+ NewConfig => JSON::from_json($Config),
+ );
+ if ( $ok ) {
+ push @results, "Lifecycle updated";
+ }
+ else {
+ push @results, "An error occured when attempting to update lifecycle, see RT log for more info.";
+ }
+ # This code does automatic redirection if any updates happen.
+ MaybeRedirectForResults(
+ Actions => \@results,
+ Arguments => { Name => $LifecycleObj->Name, Type => $LifecycleObj->Type },
+ );
}
}
-
-# This code does automatic redirection if any updates happen.
-MaybeRedirectForResults(
- Actions => \@results,
- Arguments => { Name => $LifecycleObj->Name, Type => $LifecycleObj->Type },
-);
</%INIT>
<%ARGS>
$Name => undef
$Type => undef
$Config => undef
-$LifecycleConfiguration => undef
+$ValidateLifecycle => undef
+$UpdateLifecycle => undef
</%ARGS>
diff --git a/share/html/Admin/Lifecycles/Modify.html b/share/html/Admin/Lifecycles/Modify.html
index 5483bcc511..32b68fe66d 100644
--- a/share/html/Admin/Lifecycles/Modify.html
+++ b/share/html/Admin/Lifecycles/Modify.html
@@ -54,7 +54,7 @@
<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Modify.html" name="ModifyLifecycle" method="post" enctype="multipart/form-data">
<input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
<input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
- <& /Elements/Lifecycle/Graph, Lifecycle => $LifecycleObj->Name &>
+ <& /Elements/Lifecycle/Graph, LifecycleName => $LifecycleObj->Name &>
<div class="col-md-12">
<& /Elements/Submit, Label => loc('Save Changes') &>
</div>
diff --git a/share/html/Admin/Lifecycles/Modify.html b/share/html/Admin/Lifecycles/Rights.html
similarity index 78%
copy from share/html/Admin/Lifecycles/Modify.html
copy to share/html/Admin/Lifecycles/Rights.html
index 5483bcc511..f587b115ec 100644
--- a/share/html/Admin/Lifecycles/Modify.html
+++ b/share/html/Admin/Lifecycles/Rights.html
@@ -49,25 +49,15 @@
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
-<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/static/js/farbtastic.js"></script>
-
-<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Modify.html" name="ModifyLifecycle" method="post" enctype="multipart/form-data">
- <input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
- <input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
- <& /Elements/Lifecycle/Graph, Lifecycle => $LifecycleObj->Name &>
- <div class="col-md-12">
- <& /Elements/Submit, Label => loc('Save Changes') &>
- </div>
-</form>
<%INIT>
my ($title, @results);
my $LifecycleObj = RT::Lifecycle->new();
$LifecycleObj->Load(Name => $Name, Type => $Type);
Abort("Invalid lifecycle") unless $LifecycleObj->Name
- && $LifecycleObj->{data}{type} eq $Type;
+ && $LifecycleObj->{data}{type} eq $Type;
-$title = loc("Modify lifecycle [_1]", $LifecycleObj->Name);
+$title = loc("Rights for lifecycle [_1]", $LifecycleObj->Name);
if ($Config) {
my $LifecycleConfiguration = JSON::from_json($LifecycleConfiguration);
@@ -78,10 +68,10 @@ if ($Config) {
Configuration => $LifecycleConfiguration,
);
if ( $ok ) {
- push @results, "Lifecycle updated";
+ push @results, "Lifecycle updated";
}
else {
- push @results, "An error occured when attempting to update lifecycle, see RT log for more info.";
+ push @results, "An error occured when attempting to update lifecycle, see RT log for more info.";
}
}
@@ -97,3 +87,4 @@ $Type => undef
$Config => undef
$LifecycleConfiguration => undef
</%ARGS>
+
\ No newline at end of file
diff --git a/share/html/Elements/Lifecycle/Graph b/share/html/Elements/Lifecycle/Graph
index 25f5e8d863..7bbce0e622 100644
--- a/share/html/Elements/Lifecycle/Graph
+++ b/share/html/Elements/Lifecycle/Graph
@@ -84,7 +84,7 @@
jQuery(function () {
var container = document.getElementById('lifecycle-<% $id %>'),
config = <% JSON($config) |n %>,
- name = <% $Lifecycle | j%>,
+ name = <% $LifecycleName | j%>,
configuration = <% $configuration |n %>;
var editor = new RT.NewEditor( container, config, configuration );
@@ -122,21 +122,17 @@
</div>
<%INIT>
-$Lifecycle ||= $Ticket->Lifecycle
- if $Ticket;
-
-my $config = RT->Config->Get('Lifecycles')->{$Lifecycle};
-Abort("Invalid Lifecycle") if !$Lifecycle || !$config;
+my $config = RT->Config->Get('Lifecycles')->{$LifecycleName};
+Abort("Invalid Lifecycle") if !$LifecycleName || !$config;
my $configurations = RT::Configurations->new( RT->SystemUser );
-$configurations->Limit( FIELD => 'Name', VALUE => "LifecycleConfiguration-$Lifecycle" );
+$configurations->Limit( FIELD => 'Name', VALUE => "LifecycleConfiguration-$LifecycleName" );
my $configuration = $configurations->First;
$configuration = $configuration ? JSON($configuration->_DeserializeContent($configuration->Content)) : "{}";
-my $id = $Lifecycle . '-' . int(rand(2**31));
+my $id = $LifecycleName . '-' . int(rand(2**31));
</%INIT>
<%ARGS>
-$Lifecycle => undef
-$Ticket => undef
+$LifecycleName => undef
</%ARGS>
diff --git a/share/static/js/lifecycleui-editor.js b/share/static/js/lifecycleui-editor.js
index 7504cb1964..ecd72f8c09 100644
--- a/share/static/js/lifecycleui-editor.js
+++ b/share/static/js/lifecycleui-editor.js
@@ -596,3 +596,37 @@ jQuery( document ).ready(function () {
}
}
});
+
+if ( window.location.href.indexOf('/Admin/Lifecycles/Advanced') ) {
+ jQuery( document ).ready(function () {
+ function IsValidJSONString(str) {
+ try {
+ JSON.parse(str);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+
+ jQuery('#Config').bind('input propertychange', function() {
+ if ( IsValidJSONString(jQuery('#Config').val()) ) {
+ jQuery('#UpdateLifecycle').find(".button").each(function() {
+ this.disabled = '';
+ });
+ jQuery('#ValidateLifecycle').find(".button").each(function() {
+ this.disabled = '';
+ });
+ jQuery('.alert').hide();
+ }
+ else {
+ jQuery('#UpdateLifecycle').find(".button").each(function() {
+ this.disabled = 'disabled';
+ });
+ jQuery('#ValidateLifecycle').find(".button").each(function() {
+ this.disabled = 'disabled';
+ });
+ jQuery('.alert').show();
+ }
+ });
+ });
+}
\ No newline at end of file
commit d84b4532033869fe438fcd628220c9bedd472723
Author: Craig Kaiser <craig at bestpractical.com>
Date: Mon Feb 3 17:30:17 2020 -0500
Add Rights page for lifecycle UI
diff --git a/share/html/Admin/Lifecycles/Rights.html b/share/html/Admin/Lifecycles/Rights.html
index f587b115ec..b45671e4b4 100644
--- a/share/html/Admin/Lifecycles/Rights.html
+++ b/share/html/Admin/Lifecycles/Rights.html
@@ -49,6 +49,48 @@
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
+<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Rights.html" name="ModifyLifecycleRights" method="post" enctype="multipart/form-data">
+ <input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
+ <input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
+ <h1><&|/l&>Lifecycles</&></h1>
+
+ <table class="table table-sm collection collection-as-table" cellspacing="0">
+ <tbody>
+ <tr class="collection-as-table">
+ <th class="collection-as-table">From Status</th>
+ <th class="collection-as-table">To Status</th>
+ <th class="collection-as-table">Right</th>
+ </tr>
+ </tbody>
+ <tbody class="list-item">
+% my $i = 1;
+% foreach my $right ( sort @rights ) {
+ <tr>
+ <td class="collection-as-table">
+ <& /Elements/SelectStatus, Statuses => \@statuses, Default => $right->{'To'}, Name => "Right-To-$i" &>
+ </td>
+ <td class="collection-as-table">
+ <& /Elements/SelectStatus, Statuses => \@statuses, Default => $right->{'From'}, Name => "Right-From-$i" &>
+ </td>
+ <td class="collection-as-table">
+ <input type="text" value="<% $right->{'Name'} %>" class="form-control" Name="Right-Name-<%$i%>" />
+ </td>
+ </tr>
+% ++$i;
+% }
+ <tr>
+ <td class="collection-as-table"><& /Elements/SelectStatus, Statuses => \@statuses, Name => 'Right-To-0' &></td>
+ <td class="collection-as-table"><& /Elements/SelectStatus, Statuses => \@statuses, Name => 'Right-From-0' &></td>
+ <td class="collection-as-table"><input type="text" class="form-control" name='Right-Name-0' /></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="col-md-12">
+ <& /Elements/Submit, Label => loc('Update lifecycle rights'), Name => 'UpdateLifecycleRights', id => 'UpdateLifecycleRights' &>
+ </div>
+</form>
+
<%INIT>
my ($title, @results);
my $LifecycleObj = RT::Lifecycle->new();
@@ -59,13 +101,43 @@ Abort("Invalid lifecycle") unless $LifecycleObj->Name
$title = loc("Rights for lifecycle [_1]", $LifecycleObj->Name);
-if ($Config) {
- my $LifecycleConfiguration = JSON::from_json($LifecycleConfiguration);
+my @statuses = $LifecycleObj->Statuses;
+push @statuses, '*';
+
+my $rights = $LifecycleObj->Rights || ();
+
+my @rights;
+foreach my $key ( keys %{$rights} ) {
+ my ($to, $from) = $key =~ /(.+) \-\> (.+)/;
+ push @rights, { To => $to, From => $from, Name => $rights->{$key} };
+}
+
+if ($UpdateLifecycleRights) {
+ my %new_rights;
+ foreach my $arg ( keys %ARGS ) {
+ my ($field, $count) = $arg =~ /Right-(\w+)-(\d+)/;
+ next unless $field and defined $count && $ARGS{$arg};
+
+ if ( $new_rights{$count} ) {
+ $new_rights{$count}->{$field} = $ARGS{$arg};
+ }
+ else {
+ $new_rights{$count} = {};
+ $new_rights{$count}->{$field} = $ARGS{$arg};
+ }
+ }
+ my $new_rights;
+ # Convert to RT internal format
+ foreach my $right ( keys %new_rights ) {
+ $new_rights->{"$new_rights{$right}->{To} -> $new_rights{$right}->{From}"} = $new_rights{$right}->{Name};
+ }
+ my $config = RT->Config->Get('Lifecycles')->{$LifecycleObj->Name};
+ $config->{'rights'} = $new_rights;
+
my ($ok, $msg) = RT::Lifecycle->UpdateLifecycle(
CurrentUser => $session{CurrentUser},
LifecycleObj => $LifecycleObj,
- NewConfig => JSON::from_json($Config),
- Configuration => $LifecycleConfiguration,
+ NewConfig => $config,
);
if ( $ok ) {
push @results, "Lifecycle updated";
@@ -84,7 +156,6 @@ MaybeRedirectForResults(
<%ARGS>
$Name => undef
$Type => undef
-$Config => undef
-$LifecycleConfiguration => undef
+$UpdateLifecycleRights => undef
</%ARGS>
\ No newline at end of file
commit 7c07622c6fe81c7dc846c50e26b3bd6797a8bfff
Author: Craig Kaiser <craig at bestpractical.com>
Date: Mon Feb 3 17:30:31 2020 -0500
Add Actions page for lifecycle UI
diff --git a/share/html/Admin/Lifecycles/Actions.html b/share/html/Admin/Lifecycles/Actions.html
index f587b115ec..d1bec15702 100644
--- a/share/html/Admin/Lifecycles/Actions.html
+++ b/share/html/Admin/Lifecycles/Actions.html
@@ -49,6 +49,63 @@
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
+<form action="<%RT->Config->Get('WebPath')%>/Admin/Lifecycles/Actions.html" name="ModifyLifecycleRights" method="post" enctype="multipart/form-data">
+ <input type="hidden" class="hidden" name="Name" value="<% $LifecycleObj->Name %>" />
+ <input type="hidden" class="hidden" name="Type" value="<% $LifecycleObj->Type %>" />
+ <h1><&|/l&>Lifecycles</&></h1>
+
+ <table class="table table-sm collection collection-as-table" cellspacing="0">
+ <tbody>
+ <tr class="collection-as-table">
+ <th class="collection-as-table">From Status</th>
+ <th class="collection-as-table">To Status</th>
+ <th class="collection-as-table">Label</th>
+ <th class="collection-as-table">Update</th>
+ </tr>
+ </tbody>
+ <tbody class="list-item">
+% my $i = 1;
+% foreach my $action ( @{$actions} ) {
+ <tr>
+ <td class="collection-as-table">
+ <& /Elements/SelectStatus, Statuses => \@statuses, Default => $action->{'to'}, Name => "Action-To-$i" &>
+ </td>
+ <td class="collection-as-table">
+ <& /Elements/SelectStatus, Statuses => \@statuses, Default => $action->{'from'}, Name => "Action-From-$i" &>
+ </td>
+ <td class="collection-as-table">
+ <input type="text" value="<% $action->{'label'} %>" class="form-control" Name="Action-Label-<%$i%>" />
+ </td>
+ <td>
+ <select name="Action-Update-<% $i %>" class="form-control selectpicker">
+ <option <% $action->{'update'} && $action->{'update'} eq 'Respond' ? qq[selected="selected"] : '' %> value="Respond">Reply</option>
+ <option <% $action->{'update'} && $action->{'update'} eq 'Comment' ? qq[selected="selected"] : ''%> value="Comment">Comment</option>
+ <option <% !$action->{'update'} ? qq[selected='selected'] : '' %> value="">None</option>
+ </select>
+ </td>
+ </tr>
+% ++$i;
+% }
+ <tr>
+ <td class="collection-as-table"><& /Elements/SelectStatus, Statuses => \@statuses, Name => 'Action-To-0' &></td>
+ <td class="collection-as-table"><& /Elements/SelectStatus, Statuses => \@statuses, Name => 'Action-From-0' &></td>
+ <td class="collection-as-table"><input type="text" class="form-control" name='Actoin-Label-0' /></td>
+ <td>
+ <select name="Action-Update-0" class="selectpicker form-control">
+ <option value="Respond">Reply</option>
+ <option value="Comment">Comment</option>
+ <option value="None">None</option>
+ </select>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="col-md-12">
+ <& /Elements/Submit, Label => loc('Update lifecycle actions'), Name => 'UpdateLifecycleActions', id => 'UpdateLifecycleActions' &>
+ </div>
+</form>
+
<%INIT>
my ($title, @results);
my $LifecycleObj = RT::Lifecycle->new();
@@ -57,15 +114,44 @@ $LifecycleObj->Load(Name => $Name, Type => $Type);
Abort("Invalid lifecycle") unless $LifecycleObj->Name
&& $LifecycleObj->{data}{type} eq $Type;
-$title = loc("Rights for lifecycle [_1]", $LifecycleObj->Name);
+$title = loc("Actions for lifecycle [_1]", $LifecycleObj->Name);
+
+my @statuses = $LifecycleObj->Statuses;
+push @statuses, '*';
+my $actions = $LifecycleObj->{'data'}->{'actions'} || ();
+
+if ($UpdateLifecycleActions) {
+ my %new_actions;
+ foreach my $arg ( keys %ARGS ) {
+ my ($field, $count) = $arg =~ /Action-(\w+)-(\d+)/;
+ next unless $field and defined $count && $ARGS{$arg};
+
+ if ( $new_actions{$count} ) {
+ $new_actions{$count}->{lc $field} = $ARGS{$arg};
+ }
+ else {
+ $new_actions{$count} = {};
+ $new_actions{$count}->{lc $field} = $ARGS{$arg};
+ }
+ }
+ my @new_actions;
+ foreach my $key ( keys %new_actions ) {
+ next unless $new_actions{$_}->{from} && $new_actions{$_}->{to};
+
+ push @new_actions, "$new_actions{$key}->{from} -> $new_actions{$key}->{to}";
+ push @new_actions, {
+ label => $new_actions{$key}->{label},
+ update => $new_actions{$key}->{update}
+ };
+ }
+
+ my $config = RT->Config->Get('Lifecycles')->{$LifecycleObj->Name};
+ $config->{'actions'} = \@new_actions;
-if ($Config) {
- my $LifecycleConfiguration = JSON::from_json($LifecycleConfiguration);
my ($ok, $msg) = RT::Lifecycle->UpdateLifecycle(
CurrentUser => $session{CurrentUser},
LifecycleObj => $LifecycleObj,
- NewConfig => JSON::from_json($Config),
- Configuration => $LifecycleConfiguration,
+ NewConfig => $config,
);
if ( $ok ) {
push @results, "Lifecycle updated";
@@ -84,7 +170,6 @@ MaybeRedirectForResults(
<%ARGS>
$Name => undef
$Type => undef
-$Config => undef
-$LifecycleConfiguration => undef
+$UpdateLifecycleActions => undef
</%ARGS>
\ No newline at end of file
commit 7a8e1c4783d443ffebfea3611fa43d091c92652b
Author: Craig Kaiser <craig at bestpractical.com>
Date: Mon Feb 3 17:31:46 2020 -0500
Add lifecycle Rights/Actions/Advanced pages to menu
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index 98659d9b50..70e3017a0a 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -883,6 +883,10 @@ sub _BuildAdminMenu {
$page->child( basics => title => loc('Modify'), path => "/Admin/Lifecycles/Modify.html?Type=" . $Type_uri . "&Name=" . $Name_uri );
$page->child( mappings => title => loc('Mappings'), path => "/Admin/Lifecycles/Mappings.html?Type=" . $Type_uri . "&Name=" . $Name_uri );
+
+ $page->child( rights => title => loc('Rights'), path => "/Admin/Lifecycles/Rights.html?Type=" . $Type_uri . "&Name=" . $Name_uri );
+ $page->child( actions => title => loc('Actions'), path => "/Admin/Lifecycles/Actions.html?Type=" . $Type_uri . "&Name=" . $Name_uri );
+ $page->child( advanced => title => loc('Advanced'), path => "/Admin/Lifecycles/Advanced.html?Type=" . $Type_uri . "&Name=" . $Name_uri );
}
}
else {
commit 22fa7153f8db11e1c836012f9ceeef302928ffda
Author: Craig Kaiser <craig at bestpractical.com>
Date: Mon Feb 3 17:32:14 2020 -0500
Add helper methods to RT::Lifecycle
diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 0bbdb3e566..e0bdc620dd 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -276,6 +276,20 @@ sub IsValid {
return 0;
}
+=head3
+
+Lists all statuses for lifecycle object self.
+
+=cut
+
+sub Statuses {
+ my $self = shift;
+
+ return @{$self->{'data'}->{''}};
+}
+
+
+
=head3 StatusType
Takes a status and returns its type, one of 'initial', 'active' or
@@ -462,6 +476,12 @@ sub CheckRight {
return $to eq 'deleted' ? 'DeleteTicket' : 'ModifyTicket';
}
+sub Rights {
+ my $self = shift;
+
+ return $self->{'data'}->{'rights'};
+}
+
=head3 RightsDescription [TYPE]
Returns hash with description of rights that are defined for
@@ -1051,7 +1071,6 @@ sub ValidateLifecycle {
for my $status ( @statuses ) {
push @warnings, "Nonexistant status @{[lc $status]} in transitions in ".$self->Name." lifecycle"
unless $lifecycle->{canonical_case}{lc $status};
- push @{ $lifecycle->{transitions}{lc $from} },
}
}
my $rights = $lifecycle->{rights} || {};
-----------------------------------------------------------------------
More information about the rt-commit
mailing list