[Rt-commit] rt branch, 5.0/custom-date-ranges-config-ui, created. rt-5.0.0alpha1-245-g0355413e5a
? sunnavy
sunnavy at bestpractical.com
Wed May 6 21:44:56 EDT 2020
The branch, 5.0/custom-date-ranges-config-ui has been created
at 0355413e5af580eaa013bda475ebd41e5d441483 (commit)
- Log -----------------------------------------------------------------
commit ff4b420e17fe87183037155b895e6438d976d390
Author: sunnavy <sunnavy at bestpractical.com>
Date: Wed May 6 21:38:08 2020 +0800
Migrate CustomDateRanges page to elevator themes
diff --git a/share/html/Elements/SelectCustomDateRangeField b/share/html/Elements/SelectCustomDateRangeField
index 9a08b84885..17a47f998b 100644
--- a/share/html/Elements/SelectCustomDateRangeField
+++ b/share/html/Elements/SelectCustomDateRangeField
@@ -45,7 +45,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<select name="<% $Name %>" id="<% $Name %>" class="chosen custom-date-range-field">
+<select name="<% $Name %>" id="<% $Name %>" class="form-control selectpicker custom-date-range-field">
<option value="" >-</option>
% for my $field ( $ObjectType->new( $session{CurrentUser} )->CustomDateRangeFields ) {
<option <% $field eq ($Default//'') ? 'selected="selected"' : '' |n %> value="<% $field %>" ><% $field %></option>
diff --git a/share/html/Search/CustomDateRanges.html b/share/html/Search/CustomDateRanges.html
index c2768ad5ec..6eae2fb4d5 100644
--- a/share/html/Search/CustomDateRanges.html
+++ b/share/html/Search/CustomDateRanges.html
@@ -50,22 +50,22 @@
<& /Elements/ListActions, actions => \@results &>
-<&|/Widgets/TitleBox, title => loc('Custom Date Ranges In Config Files') &>
+<&|/Widgets/TitleBox, title => loc('Custom Date Ranges In Config Files'), class => 'mx-auto max-width-xl' &>
% if ( $config && keys %{$config->{'RT::Ticket'}} ) {
<div class="table-responsive">
- <table class="collection-as-table">
+ <table class="table collection-as-table">
<tr class="collection-as-table">
<th class="collection-as-table"><&|/l&>Name</&></th>
<th class="collection-as-table"><&|/l&>From</&></th>
<th class="collection-as-table"><&|/l&>From Value if Unset</&></th>
<th class="collection-as-table"><&|/l&>To</&></th>
<th class="collection-as-table"><&|/l&>To Value if Unset</&></th>
- <th class="collection-as-table"><&|/l&>Business<br>Hours?</&></th>
+ <th class="collection-as-table"><&|/l&>Business Hours?</&></th>
</tr>
% my $i = 0;
% for my $name ( sort keys %{$config->{'RT::Ticket'}} ) {
% $i++;
- <tr class="<% $i % 2 ? 'oddline' : 'evenline' %>">
+ <tr class="collection-as-table">
<td class="collection-as-table"><% $name %></td>
% my $spec = $config->{'RT::Ticket'}{$name};
% my %date_range_spec = RT::Ticket->_ParseCustomDateRangeSpec($name, $spec);
@@ -80,22 +80,22 @@
</div>
% }
% else {
- <p><&|/l&>No custom date ranges in config files</&></p>
+ <p class="mt-3 mt-1 ml-3"><&|/l&>No custom date ranges in config files</&></p>
% }
</&>
<form name="CustomDateRanges" method="POST" method="?">
- <&|/Widgets/TitleBox, title => loc('Custom Date Ranges') &>
+ <&|/Widgets/TitleBox, title => loc('Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
<div class="table-responsive">
- <table class="collection-as-table">
- <tr class="collection-as-table">
+ <table class="collection-as-table table">
+ <tr class="collection-as-table text-center">
<th class="collection-as-table"><&|/l&>Name</&></th>
<th class="collection-as-table"><&|/l&>From</&></th>
<th class="collection-as-table"><&|/l&>From Value if Unset</&></th>
<th class="collection-as-table"><&|/l&>To</&></th>
<th class="collection-as-table"><&|/l&>To Value if Unset</&></th>
- <th class="collection-as-table"><&|/l&>Business<br>Hours?</&></th>
- <th class="collection-as-table">
+ <th class="collection-as-table"><&|/l&>Business Hours?</&></th>
+ <th class="collection-as-table text-left">
<input type="checkbox" name="DeleteAll" value="1" onclick="setCheckbox(this, /^\d+-Delete$/)" />
<&|/l&>Delete</&>
</th>
@@ -105,15 +105,15 @@
% my $id = 0;
% for my $name ( sort keys %{$content->{'RT::Ticket'}} ) {
% $i++;
- <tr class="<% $i % 2 ? 'oddline' : 'evenline' %>">
- <td class="collection-as-table"><input type="text" name="<% $id %>-name" value="<% $name %>" /></td>
+ <tr class="collection-as-table">
+ <td class="collection-as-table"><input type="text" name="<% $id %>-name" value="<% $name %>" class="form-control" /></td>
% my %date_range_spec = RT::Ticket->_ParseCustomDateRangeSpec($name, $content->{'RT::Ticket'}{$name});
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-from", Default => $date_range_spec{from} &></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-from_fallback", Default => $date_range_spec{from_fallback} &></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-to", Default => $date_range_spec{to} &></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-to_fallback", Default => $date_range_spec{to_fallback} &></td>
<td class="collection-as-table">
- <select name="<% $id %>-business_time">
+ <select name="<% $id %>-business_time" class="form-control selectpicker">
<option value="1" <% $date_range_spec{business_time} ? 'selected="selected"' : '' |n%>><&|/l&>Yes</&></option>
<option value="0" <% $date_range_spec{business_time} ? '': 'selected="selected"' |n%>><&|/l&>No</&></option>
</select>
@@ -126,14 +126,14 @@
% for ( 1 .. 3 ) {
% $i++;
- <tr class="<% $i % 2 ? 'oddline' : 'evenline' %>">
- <td class="collection-as-table"><input type="text" name="name" value="" /></td>
+ <tr class="collection-as-table">
+ <td class="collection-as-table"><input type="text" name="name" value="" class="form-control" /></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'from' &></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'from_fallback' &></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'to' &></td>
<td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'to_fallback' &></td>
<td class="collection-as-table">
- <select name="business_time">
+ <select name="business_time" class="form-control selectpicker">
<option value="1"><&|/l&>Yes</&></option>
<option value="0" selected="selected"><&|/l&>No</&></option>
</select>
@@ -147,12 +147,6 @@
</&>
</form>
-<script type="text/javascript">
-jQuery(function() {
- jQuery('select.chosen.custom-date-range-field').chosen({ width: '12em', no_results_text: ' ', search_contains: true });
-});
-</script>
-
<%INIT>
Abort(loc("Permission Denied")) unless $session{'CurrentUser'}->HasRight( Object=> RT->System, Right => 'SuperUser');
commit b4788d39b8bba19a7b5f0f5328a8dea0a1629186
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 02:13:48 2020 +0800
Switch from system attribute to "CustomDateRangesUI" configuration
We are going to move to system config page, and it makes more sense we
use configuration correspondingly.
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 99242eac13..6e53760122 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -2656,11 +2656,9 @@ sub CustomDateRanges {
%ranges = %{ $config->{$type} } if $config->{$type};
}
- if ( my $attribute = RT->System->FirstAttribute('CustomDateRanges') ) {
- if ( my $content = $attribute->Content ) {
- for my $name ( keys %{ $content->{$type} || {} } ) {
- $ranges{$name} ||= $content->{$type}{$name};
- }
+ if ( my $db_config = RT->Config->Get('CustomDateRangesUI') ) {
+ for my $name ( keys %{ $db_config->{$type} || {} } ) {
+ $ranges{$name} ||= $db_config->{$type}{$name};
}
}
return %ranges;
diff --git a/share/html/Search/CustomDateRanges.html b/share/html/Search/CustomDateRanges.html
index 6eae2fb4d5..e375a30375 100644
--- a/share/html/Search/CustomDateRanges.html
+++ b/share/html/Search/CustomDateRanges.html
@@ -152,11 +152,12 @@
Abort(loc("Permission Denied")) unless $session{'CurrentUser'}->HasRight( Object=> RT->System, Right => 'SuperUser');
my $config = RT->Config->Get('CustomDateRanges');
-my $attribute = RT->System->FirstAttribute('CustomDateRanges') || RT::Attribute->new( $session{CurrentUser} );
+my $db_config = RT::Configuration->new( $session{CurrentUser} );
+$db_config->LoadByCols( Name => 'CustomDateRangesUI', Disabled => 0 );
my $content;
-$content = $attribute->Content if $attribute->id;
+$content = $db_config->_DeserializeContent( $db_config->Content ) if $db_config->id;
my @results;
@@ -266,12 +267,11 @@ if ($Save) {
if ($need_save) {
my ( $ret, $msg );
- if ( $attribute->id ) {
- ( $ret, $msg ) = $attribute->SetContent($content);
+ if ( $db_config->id ) {
+ ( $ret, $msg ) = $db_config->SetContent($content);
}
else {
- ( $ret, $msg )
- = $attribute->Create( Name => 'CustomDateRanges', Object => RT->System, Content => $content );
+ ( $ret, $msg ) = $db_config->Create(Name => 'CustomDateRangesUI', Content => $content);
}
unless ($ret) {
commit b7594c45a10eca4b24cddd81f87871e55091fb65
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 04:56:46 2020 +0800
Abstract (Show/Edit/Process)CustomDateRanges for future reusage
We will need them on both system edit config and user pref pages.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index e685be5b86..f611c7e9be 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -4748,6 +4748,150 @@ sub UpdateDashboard {
}
}
+=head2 ProcessCustomDateRanges ARGSRef => ARGSREF
+
+Returns an array of results messages.
+
+=cut
+
+sub ProcessCustomDateRanges {
+ my %args = (
+ ARGSRef => undef,
+ @_
+ );
+ my $args_ref = $args{ARGSRef};
+
+ my $config = RT->Config->Get('CustomDateRanges');
+ my $db_config = RT::Configuration->new( $session{CurrentUser} );
+ $db_config->LoadByCols( Name => 'CustomDateRangesUI', Disabled => 0 );
+
+ my $content;
+ $content = $db_config->_DeserializeContent( $db_config->Content ) if $db_config->id;
+
+ my @results;
+ my %label = (
+ from => 'From', # loc
+ to => 'To', # loc
+ from_fallback => 'From Value if Unset', # loc
+ to_fallback => 'To Value if Unset', # loc
+ );
+
+ my $need_save;
+ if ($content) {
+ my @current_names = sort keys %{ $content->{'RT::Ticket'} };
+ for my $id ( 0 .. $#current_names ) {
+ my $current_name = $current_names[$id];
+ my $spec = $content->{'RT::Ticket'}{$current_name};
+ my $name = $args_ref->{"$id-name"};
+
+ if ( $config && $config->{'RT::Ticket'}{$name} ) {
+ push @results, loc( "[_1] already exists", $name );
+ next;
+ }
+
+ if ( $args_ref->{"$id-Delete"} ) {
+ delete $content->{'RT::Ticket'}{$current_name};
+ push @results, loc( 'Deleted [_1]', $current_name );
+ $need_save ||= 1;
+ next;
+ }
+
+ my $updated;
+ for my $field (qw/from from_fallback to to_fallback/) {
+ next if ( $spec->{$field} // '' ) eq $args_ref->{"$id-$field"};
+ if (( $args_ref->{"$id-$field"}
+ && RT::Ticket->_ParseCustomDateRangeSpec( $name, join ' - ', 'now', $args_ref->{"$id-$field"} )
+ )
+ || ( !$args_ref->{"$id-$field"} && $field =~ /fallback/ )
+ )
+ {
+ $spec->{$field} = $args_ref->{"$id-$field"};
+ $updated ||= 1;
+ }
+ else {
+ push @results, loc( 'Invalid [_1] for [_2]', loc( $label{$field} ), $name );
+ next;
+ }
+ }
+
+ if ( $spec->{business_time} != $args_ref->{"$id-business_time"} ) {
+ $spec->{business_time} = $args_ref->{"$id-business_time"};
+ $updated ||= 1;
+ }
+
+ $content->{'RT::Ticket'}{$name} = $spec;
+ if ( $name ne $current_name ) {
+ delete $content->{'RT::Ticket'}{$current_name};
+ $updated ||= 1;
+ }
+
+ if ($updated) {
+ push @results, loc( 'Updated [_1]', $name );
+ $need_save ||= 1;
+ }
+ }
+ }
+
+ if ( $args_ref->{name} ) {
+ for my $field (qw/from from_fallback to to_fallback business_time/) {
+ $args_ref->{$field} = [ $args_ref->{$field} ] unless ref $args_ref->{$field};
+ }
+
+ my $i = 0;
+ for my $name ( @{ $args_ref->{name} } ) {
+ if ($name) {
+ if ( $config && $config->{'RT::Ticket'}{$name} || $content && $content->{'RT::Ticket'}{$name} ) {
+ push @results, loc( "[_1] already exists", $name );
+ $i++;
+ next;
+ }
+ }
+ else {
+ $i++;
+ next;
+ }
+
+ my $spec = { business_time => $args_ref->{business_time}[$i] };
+ for my $field (qw/from from_fallback to to_fallback/) {
+ if (( $args_ref->{$field}[$i]
+ && RT::Ticket->_ParseCustomDateRangeSpec( $name, join ' - ', 'now', $args_ref->{$field}[$i] )
+ )
+ || ( !$args_ref->{$field}[$i] && $field =~ /fallback/ )
+ )
+ {
+ $spec->{$field} = $args_ref->{$field}[$i];
+ }
+ else {
+ push @results, loc( 'Invalid [_1] for [_2]', loc($field), $name );
+ $i++;
+ next;
+ }
+ }
+
+ $content->{'RT::Ticket'}{$name} = $spec;
+ push @results, loc( 'Created [_1]', $name );
+ $need_save ||= 1;
+ $i++;
+ }
+ }
+
+ if ($need_save) {
+ my ( $ret, $msg );
+ if ( $db_config->id ) {
+ ( $ret, $msg ) = $db_config->SetContent($content);
+ }
+ else {
+ ( $ret, $msg ) = $db_config->Create(Name => 'CustomDateRangesUI', Content => $content);
+ }
+
+ unless ($ret) {
+ RT->Logger->error("Couldn't save content: $msg");
+ push @results, $msg;
+ }
+ }
+ return @results;
+}
+
package RT::Interface::Web;
RT::Base->_ImportOverlays();
diff --git a/share/html/Elements/EditCustomDateRanges b/share/html/Elements/EditCustomDateRanges
new file mode 100644
index 0000000000..a2dc468249
--- /dev/null
+++ b/share/html/Elements/EditCustomDateRanges
@@ -0,0 +1,110 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (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 }}}
+
+<div class="table-responsive">
+ <table class="collection-as-table table">
+ <tr class="collection-as-table text-center">
+ <th class="collection-as-table"><&|/l&>Name</&></th>
+ <th class="collection-as-table"><&|/l&>From</&></th>
+ <th class="collection-as-table"><&|/l&>From Value if Unset</&></th>
+ <th class="collection-as-table"><&|/l&>To</&></th>
+ <th class="collection-as-table"><&|/l&>To Value if Unset</&></th>
+ <th class="collection-as-table"><&|/l&>Business Hours?</&></th>
+ <th class="collection-as-table text-left">
+ <input type="checkbox" name="DeleteAll" value="1" onclick="setCheckbox(this, /^\d+-Delete$/)" />
+ <&|/l&>Delete</&>
+ </th>
+ </tr>
+% my $i = 0;
+% if ( keys %CustomDateRanges ) {
+% my $id = 0;
+% for my $name ( sort keys %CustomDateRanges ) {
+% $i++;
+ <tr class="collection-as-table">
+ <td class="collection-as-table"><input type="text" name="<% $id %>-name" value="<% $name %>" class="form-control" /></td>
+% my %date_range_spec = $ObjectType->_ParseCustomDateRangeSpec($name, $CustomDateRanges{$name});
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-from", Default => $date_range_spec{from} &></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-from_fallback", Default => $date_range_spec{from_fallback} &></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-to", Default => $date_range_spec{to} &></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-to_fallback", Default => $date_range_spec{to_fallback} &></td>
+ <td class="collection-as-table">
+ <select name="<% $id %>-business_time" class="form-control selectpicker">
+ <option value="1" <% $date_range_spec{business_time} ? 'selected="selected"' : '' |n%>><&|/l&>Yes</&></option>
+ <option value="0" <% $date_range_spec{business_time} ? '': 'selected="selected"' |n%>><&|/l&>No</&></option>
+ </select>
+ </td>
+ <td class="collection-as-table"><input type="checkbox" name="<% $id %>-Delete" value="1" /></td>
+ </tr>
+% $id++;
+% }
+% }
+
+% for ( 1 .. 3 ) {
+% $i++;
+ <tr class="collection-as-table">
+ <td class="collection-as-table"><input type="text" name="name" value="" class="form-control" /></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'from' &></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'from_fallback' &></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'to' &></td>
+ <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'to_fallback' &></td>
+ <td class="collection-as-table">
+ <select name="business_time" class="form-control selectpicker">
+ <option value="1"><&|/l&>Yes</&></option>
+ <option value="0" selected="selected"><&|/l&>No</&></option>
+ </select>
+ </td>
+ <td class="collection-as-table"></td>
+ </tr>
+% }
+ </table>
+</div>
+
+<%ARGS>
+%CustomDateRanges => ()
+$ObjectType => 'RT::Ticket'
+</%ARGS>
diff --git a/share/html/Elements/ShowCustomDateRanges b/share/html/Elements/ShowCustomDateRanges
new file mode 100644
index 0000000000..04cce6f790
--- /dev/null
+++ b/share/html/Elements/ShowCustomDateRanges
@@ -0,0 +1,78 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (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 }}}
+
+<div class="table-responsive">
+ <table class="table collection-as-table">
+ <tr class="collection-as-table">
+ <th class="collection-as-table"><&|/l&>Name</&></th>
+ <th class="collection-as-table"><&|/l&>From</&></th>
+ <th class="collection-as-table"><&|/l&>From Value if Unset</&></th>
+ <th class="collection-as-table"><&|/l&>To</&></th>
+ <th class="collection-as-table"><&|/l&>To Value if Unset</&></th>
+ <th class="collection-as-table"><&|/l&>Business Hours?</&></th>
+ </tr>
+% my $i = 0;
+% for my $name ( sort keys %CustomDateRanges ) {
+% $i++;
+ <tr class="collection-as-table">
+ <td class="collection-as-table"><% $name %></td>
+% my %date_range_spec = $ObjectType->_ParseCustomDateRangeSpec($name, $CustomDateRanges{$name});
+ <td class="collection-as-table"><% $date_range_spec{from} %></td>
+ <td class="collection-as-table"><% $date_range_spec{from_fallback} || '' %></td>
+ <td class="collection-as-table"><% $date_range_spec{to} %></td>
+ <td class="collection-as-table"><% $date_range_spec{to_fallback} || '' %></td>
+ <td class="collection-as-table"><% $date_range_spec{business_time} ? loc('Yes') : loc('No') %></td>
+ </tr>
+% }
+ </table>
+</div>
+
+<%ARGS>
+%CustomDateRanges => ()
+$ObjectType => 'RT::Ticket'
+</%ARGS>
diff --git a/share/html/Search/CustomDateRanges.html b/share/html/Search/CustomDateRanges.html
index e375a30375..cef7ec821e 100644
--- a/share/html/Search/CustomDateRanges.html
+++ b/share/html/Search/CustomDateRanges.html
@@ -52,32 +52,7 @@
<&|/Widgets/TitleBox, title => loc('Custom Date Ranges In Config Files'), class => 'mx-auto max-width-xl' &>
% if ( $config && keys %{$config->{'RT::Ticket'}} ) {
-<div class="table-responsive">
- <table class="table collection-as-table">
- <tr class="collection-as-table">
- <th class="collection-as-table"><&|/l&>Name</&></th>
- <th class="collection-as-table"><&|/l&>From</&></th>
- <th class="collection-as-table"><&|/l&>From Value if Unset</&></th>
- <th class="collection-as-table"><&|/l&>To</&></th>
- <th class="collection-as-table"><&|/l&>To Value if Unset</&></th>
- <th class="collection-as-table"><&|/l&>Business Hours?</&></th>
- </tr>
-% my $i = 0;
-% for my $name ( sort keys %{$config->{'RT::Ticket'}} ) {
-% $i++;
- <tr class="collection-as-table">
- <td class="collection-as-table"><% $name %></td>
-% my $spec = $config->{'RT::Ticket'}{$name};
-% my %date_range_spec = RT::Ticket->_ParseCustomDateRangeSpec($name, $spec);
- <td class="collection-as-table"><% $date_range_spec{from} %></td>
- <td class="collection-as-table"><% $date_range_spec{from_fallback} || '' %></td>
- <td class="collection-as-table"><% $date_range_spec{to} %></td>
- <td class="collection-as-table"><% $date_range_spec{to_fallback} || '' %></td>
- <td class="collection-as-table"><% $date_range_spec{business_time} ? loc('Yes') : loc('No') %></td>
- </tr>
-% }
- </table>
-</div>
+ <& /Elements/ShowCustomDateRanges, CustomDateRanges => $config->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
% }
% else {
<p class="mt-3 mt-1 ml-3"><&|/l&>No custom date ranges in config files</&></p>
@@ -86,63 +61,7 @@
<form name="CustomDateRanges" method="POST" method="?">
<&|/Widgets/TitleBox, title => loc('Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
- <div class="table-responsive">
- <table class="collection-as-table table">
- <tr class="collection-as-table text-center">
- <th class="collection-as-table"><&|/l&>Name</&></th>
- <th class="collection-as-table"><&|/l&>From</&></th>
- <th class="collection-as-table"><&|/l&>From Value if Unset</&></th>
- <th class="collection-as-table"><&|/l&>To</&></th>
- <th class="collection-as-table"><&|/l&>To Value if Unset</&></th>
- <th class="collection-as-table"><&|/l&>Business Hours?</&></th>
- <th class="collection-as-table text-left">
- <input type="checkbox" name="DeleteAll" value="1" onclick="setCheckbox(this, /^\d+-Delete$/)" />
- <&|/l&>Delete</&>
- </th>
- </tr>
-% my $i = 0;
-% if ( $content ) {
-% my $id = 0;
-% for my $name ( sort keys %{$content->{'RT::Ticket'}} ) {
-% $i++;
- <tr class="collection-as-table">
- <td class="collection-as-table"><input type="text" name="<% $id %>-name" value="<% $name %>" class="form-control" /></td>
-% my %date_range_spec = RT::Ticket->_ParseCustomDateRangeSpec($name, $content->{'RT::Ticket'}{$name});
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-from", Default => $date_range_spec{from} &></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-from_fallback", Default => $date_range_spec{from_fallback} &></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-to", Default => $date_range_spec{to} &></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => "$id-to_fallback", Default => $date_range_spec{to_fallback} &></td>
- <td class="collection-as-table">
- <select name="<% $id %>-business_time" class="form-control selectpicker">
- <option value="1" <% $date_range_spec{business_time} ? 'selected="selected"' : '' |n%>><&|/l&>Yes</&></option>
- <option value="0" <% $date_range_spec{business_time} ? '': 'selected="selected"' |n%>><&|/l&>No</&></option>
- </select>
- </td>
- <td class="collection-as-table"><input type="checkbox" name="<% $id %>-Delete" value="1" /></td>
- </tr>
-% $id++;
-% }
-% }
-
-% for ( 1 .. 3 ) {
-% $i++;
- <tr class="collection-as-table">
- <td class="collection-as-table"><input type="text" name="name" value="" class="form-control" /></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'from' &></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'from_fallback' &></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'to' &></td>
- <td class="collection-as-table"><& /Elements/SelectCustomDateRangeField, Name => 'to_fallback' &></td>
- <td class="collection-as-table">
- <select name="business_time" class="form-control selectpicker">
- <option value="1"><&|/l&>Yes</&></option>
- <option value="0" selected="selected"><&|/l&>No</&></option>
- </select>
- </td>
- <td class="collection-as-table"></td>
- </tr>
-% }
- </table>
- </div>
+ <& /Elements/EditCustomDateRanges, CustomDateRanges => $content->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
<& /Elements/Submit, Name => 'Save', Label => loc('Save Changes') &>
</&>
</form>
@@ -162,123 +81,7 @@ $content = $db_config->_DeserializeContent( $db_config->Content ) if $db_config-
my @results;
if ($Save) {
- my %label = (
- from => 'From', # loc
- to => 'To', # loc
- from_fallback => 'From Value if Unset', # loc
- to_fallback => 'To Value if Unset', # loc
- );
-
- my $need_save;
- if ($content) {
- my @current_names = sort keys %{ $content->{'RT::Ticket'} };
- for my $id ( 0 .. $#current_names ) {
- my $current_name = $current_names[$id];
- my $spec = $content->{'RT::Ticket'}{$current_name};
- my $name = $ARGS{"$id-name"};
-
- if ( $config && $config->{'RT::Ticket'}{$name} ) {
- push @results, loc( "[_1] already exists", $name );
- next;
- }
-
- if ( $ARGS{"$id-Delete"} ) {
- delete $content->{'RT::Ticket'}{$current_name};
- push @results, loc( 'Deleted [_1]', $current_name );
- $need_save ||= 1;
- next;
- }
-
- my $updated;
- for my $field (qw/from from_fallback to to_fallback/) {
- next if ( $spec->{$field} // '' ) eq $ARGS{"$id-$field"};
- if (( $ARGS{"$id-$field"}
- && RT::Ticket->_ParseCustomDateRangeSpec( $name, join ' - ', 'now', $ARGS{"$id-$field"} )
- )
- || ( !$ARGS{"$id-$field"} && $field =~ /fallback/ )
- )
- {
- $spec->{$field} = $ARGS{"$id-$field"};
- $updated ||= 1;
- }
- else {
- push @results, loc( 'Invalid [_1] for [_2]', loc( $label{$field} ), $name );
- next;
- }
- }
-
- if ( $spec->{business_time} != $ARGS{"$id-business_time"} ) {
- $spec->{business_time} = $ARGS{"$id-business_time"};
- $updated ||= 1;
- }
-
- $content->{'RT::Ticket'}{$name} = $spec;
- if ( $name ne $current_name ) {
- delete $content->{'RT::Ticket'}{$current_name};
- $updated ||= 1;
- }
-
- if ( $updated ) {
- push @results, loc( 'Updated [_1]', $name );
- $need_save ||= 1;
- }
- }
- }
-
- if ( $ARGS{name} ) {
- for my $field (qw/from from_fallback to to_fallback business_time/) {
- $ARGS{$field} = [ $ARGS{$field} ] unless ref $ARGS{$field};
- }
-
- my $i = 0;
- for my $name ( @{ $ARGS{name} } ) {
- if ($name) {
- if ( $config && $config->{'RT::Ticket'}{$name} || $content && $content->{'RT::Ticket'}{$name} ) {
- push @results, loc( "[_1] already exists", $name );
- $i++;
- next;
- }
- }
- else {
- $i++;
- next;
- }
-
- my $spec = { business_time => $ARGS{business_time}[$i] };
- for my $field ( qw/from from_fallback to to_fallback/ ) {
- if ( ($ARGS{$field}[$i] && RT::Ticket->_ParseCustomDateRangeSpec( $name, join ' - ', 'now', $ARGS{$field}[$i] ))
- || ( !$ARGS{$field}[$i] && $field =~ /fallback/ )
- ) {
- $spec->{$field} = $ARGS{$field}[$i];
- }
- else {
- push @results, loc( 'Invalid [_1] for [_2]', loc($field), $name );
- $i++;
- next;
- }
- }
-
- $content->{'RT::Ticket'}{$name} = $spec;
- push @results, loc( 'Created [_1]', $name );
- $need_save ||= 1;
- $i++;
- }
- }
-
- if ($need_save) {
- my ( $ret, $msg );
- if ( $db_config->id ) {
- ( $ret, $msg ) = $db_config->SetContent($content);
- }
- else {
- ( $ret, $msg ) = $db_config->Create(Name => 'CustomDateRangesUI', Content => $content);
- }
-
- unless ($ret) {
- RT->Logger->error("Couldn't save content: $msg");
- push @results, $msg;
- }
- }
+ push @results, ProcessCustomDateRanges( ARGSRef => \%ARGS );
}
MaybeRedirectForResults(
commit 65a64c328abddfcf8e148c3251f02fe3324764ca
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 05:46:07 2020 +0800
Delete before checking other rules for conflicts
Otherwise, when there is a conflicted entry, people couldn't delete it.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index f611c7e9be..ce762f7e67 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -4784,11 +4784,6 @@ sub ProcessCustomDateRanges {
my $spec = $content->{'RT::Ticket'}{$current_name};
my $name = $args_ref->{"$id-name"};
- if ( $config && $config->{'RT::Ticket'}{$name} ) {
- push @results, loc( "[_1] already exists", $name );
- next;
- }
-
if ( $args_ref->{"$id-Delete"} ) {
delete $content->{'RT::Ticket'}{$current_name};
push @results, loc( 'Deleted [_1]', $current_name );
@@ -4796,6 +4791,11 @@ sub ProcessCustomDateRanges {
next;
}
+ if ( $config && $config->{'RT::Ticket'}{$name} ) {
+ push @results, loc( "[_1] already exists", $name );
+ next;
+ }
+
my $updated;
for my $field (qw/from from_fallback to to_fallback/) {
next if ( $spec->{$field} // '' ) eq $args_ref->{"$id-$field"};
commit 1f5bef7ba9628ca2de49411e2053ecc8a2bb41dd
Author: michel <michel at bestpractical.com>
Date: Mon Feb 3 14:56:10 2020 +0100
Move CustomDateRanges to the Features section of the web config editor.
diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 6834f32939..15ea944c5d 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1736,119 +1736,6 @@ For C<RT::Asset>: C<Basics>, C<Dates>, C<People>, C<Links>
Extensions may also add their own built-in groupings, refer to the individual
extension documentation for those.
-=item C<%CustomDateRanges>
-
-This option lets you declare additional date ranges to be calculated
-and displayed in search results. Durations between any two core fields,
-as well as custom fields, are supported. Each custom date range is
-added as an additional display column in the search builder.
-
-Set C<%CustomDateRanges> to a nested structure similar to the following:
-
- Set(%CustomDateRanges,
- 'RT::Ticket' => {
- 'Resolution Time' => 'Resolved - Created',
-
- 'Downtime' => {
- value => 'CF.Recovered - CF.{First Alert}',
- business_time => 1,
- },
-
- 'Time To Beta' => {
- value => 'CF.Beta - now',
-
- format => sub {
- my ($duration, $beta, $now, $ticket) = @_;
- my $days = int($duration / (24*60*60));
- if ($days < 0) {
- $ticket->loc('[quant,_1,day,days] ago', -$days);
- }
- else {
- $ticket->loc('in [quant,_1,day,days]', $days);
- }
- },
- },
- },
- );
-
-The first level keys are record types. Each record type's value must be a
-hash reference. Each pair in the second-level hash defines a new range. The
-key is the range's name (which is displayed to users in the UI), and its
-value describes the range and must be either a string or a hashref.
-
-Values that are plain strings simply describe the calculation to be made.
-
-Values that are hashrefs that could include:
-
-=over 4
-
-=item value
-
-A string that describes the calculation to be made.
-
-The calculation is expected to be of the format C<"field - field"> where each
-field may be:
-
-=over 4
-
-=item * a core field
-
-For example, L<RT::Ticket> supports: Created, Starts, Started, LastUpdated,
-Told or LastContact, Due and Resolved.
-
-=item * a custom field
-
-You may use either C<CF.Name> or C<CF.{Longer Name}> syntax.
-
-=item * the word C<now>
-
-=back
-
-Custom date range calculations are defined using typical math operators with
-a space before and after. Subtraction (-) is currently supported.
-
-If either field and its corresponding fallback field(see blow) are both unset,
-then nothing will be displayed for that record (and the C<format> code
-reference will not be called). If you need additional control over how
-results are calculated, see L<docs/customizing/search_result_columns.pod>.
-
-=item from and to
-
-When value is not set, C<from/to> will be used to calculate instead.
-Technically, C<Resolved - Created"> is equal to:
-
- { from => 'Created', to => 'Resolved' }
-
-=item from_fallback and to_fallback
-
-Fallback fields when the main fields are unset, e.g.
-
- { from => 'CF.{First Alert}',
- to => 'CF.Recovered',
- to_fallback => 'now',
- }
-
-When C<CF.Recovered> is unset, "now" will be used in the calculation.
-
-=item business_time
-
-A boolean value to indicate if it's a business time or not.
-
-When the schedule can't be deducted from corresponding object, the
-C<Default> one defined in C<%ServiceBusinessHours> will be used instead.
-
-=item format
-
-A code reference that allows customization of how the duration is displayed
-to users. This code reference receives four parameters: the duration (a
-number of seconds), the end time (an L<RT::Date> object), the start time
-(another L<RT::Date>), and the record itself (which corresponds to the
-first-level key; in the example config above, it would be the L<RT::Ticket>
-object). The code reference should return the string to be displayed to the
-user.
-
-=back
-
=item C<$CanonicalizeRedirectURLs>
Set C<$CanonicalizeRedirectURLs> to 1 to use C<$WebURL> when
@@ -4695,6 +4582,124 @@ Set( %ServiceBusinessHours, );
=back
+=head2 Custom Date Ranges
+
+=over 4
+
+=item C<%CustomDateRanges>
+
+This option lets you declare additional date ranges to be calculated
+and displayed in search results. Durations between any two core fields,
+as well as custom fields, are supported. Each custom date range is
+added as an additional display column in the search builder.
+
+Set C<%CustomDateRanges> to a nested structure similar to the following:
+
+ Set(%CustomDateRanges,
+ 'RT::Ticket' => {
+ 'Resolution Time' => 'Resolved - Created',
+
+ 'Downtime' => {
+ value => 'CF.Recovered - CF.{First Alert}',
+ business_time => 1,
+ },
+
+ 'Time To Beta' => {
+ value => 'CF.Beta - now',
+
+ format => sub {
+ my ($duration, $beta, $now, $ticket) = @_;
+ my $days = int($duration / (24*60*60));
+ if ($days < 0) {
+ $ticket->loc('[quant,_1,day,days] ago', -$days);
+ }
+ else {
+ $ticket->loc('in [quant,_1,day,days]', $days);
+ }
+ },
+ },
+ },
+ );
+
+The first level keys are record types. Each record type's value must be a
+hash reference. Each pair in the second-level hash defines a new range. The
+key is the range's name (which is displayed to users in the UI), and its
+value describes the range and must be either a string or a hashref.
+
+Values that are plain strings simply describe the calculation to be made.
+
+Values that are hashrefs that could include:
+
+=over 4
+
+=item value
+
+A string that describes the calculation to be made.
+
+The calculation is expected to be of the format C<"field - field"> where each
+field may be:
+
+=over 4
+
+=item * a core field
+
+For example, L<RT::Ticket> supports: Created, Starts, Started, LastUpdated,
+Told or LastContact, Due and Resolved.
+
+=item * a custom field
+
+You may use either C<CF.Name> or C<CF.{Longer Name}> syntax.
+
+=item * the word C<now>
+
+=back
+
+Custom date range calculations are defined using typical math operators with
+a space before and after. Subtraction (-) is currently supported.
+
+If either field and its corresponding fallback field(see blow) are both unset,
+then nothing will be displayed for that record (and the C<format> code
+reference will not be called). If you need additional control over how
+results are calculated, see L<docs/customizing/search_result_columns.pod>.
+
+=item from and to
+
+When value is not set, C<from/to> will be used to calculate instead.
+Technically, C<Resolved - Created"> is equal to:
+
+ { from => 'Created', to => 'Resolved' }
+
+=item from_fallback and to_fallback
+
+Fallback fields when the main fields are unset, e.g.
+
+ { from => 'CF.{First Alert}',
+ to => 'CF.Recovered',
+ to_fallback => 'now',
+ }
+
+When C<CF.Recovered> is unset, "now" will be used in the calculation.
+
+=item business_time
+
+A boolean value to indicate if it's a business time or not.
+
+When the schedule can't be deducted from corresponding object, the
+C<Default> one defined in C<%ServiceBusinessHours> will be used instead.
+
+=item format
+
+A code reference that allows customization of how the duration is displayed
+to users. This code reference receives four parameters: the duration (a
+number of seconds), the end time (an L<RT::Date> object), the start time
+(another L<RT::Date>), and the record itself (which corresponds to the
+first-level key; in the example config above, it would be the L<RT::Ticket>
+object). The code reference should return the string to be displayed to the
+user.
+
+=back
+
+=back
=cut
commit 5f5e453651ad0fe1d04bad3d45f31a568a91d841
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 02:37:24 2020 +0800
Add missing EOF newline
Editors usually automatically add it for us, and it's annoying we drop
this unrelated change every time.
diff --git a/share/html/Admin/Tools/EditConfig.html b/share/html/Admin/Tools/EditConfig.html
index 788e1806f3..0d7af8c6ec 100644
--- a/share/html/Admin/Tools/EditConfig.html
+++ b/share/html/Admin/Tools/EditConfig.html
@@ -195,4 +195,4 @@ my $nav_type='tab'; # 'tab' or 'pill'
% }
</div><!-- content-all -->
</div><!-- titlebox-content -->
-</div><!-- configuration -->
\ No newline at end of file
+</div><!-- configuration -->
commit b6b91bebb970b0e4713ea205cc7a858aa51a72d5
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 03:35:01 2020 +0800
Move CustomDateRanges to system edit config page
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index 853e05b971..ccac869202 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -1190,6 +1190,7 @@ our %META;
},
CustomDateRanges => {
Type => 'HASH',
+ Widget => '/Widgets/Form/CustomDateRanges',
PostLoadCheck => sub {
my $config = shift;
# use scalar context intentionally to avoid not a hash error
@@ -1219,6 +1220,10 @@ our %META;
}
},
},
+ CustomDateRangesUI => {
+ Type => 'HASH',
+ Widget => '/Widgets/Form/CustomDateRanges',
+ },
ExternalStorage => {
Type => 'HASH',
PostLoadCheck => sub {
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index ce762f7e67..785ad02c7c 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -4750,6 +4750,10 @@ sub UpdateDashboard {
=head2 ProcessCustomDateRanges ARGSRef => ARGSREF
+For system database configuration, it adds corresponding arguments to the
+passed ARGSRef, and the following code on EditConfig.html page will do the
+real update job.
+
Returns an array of results messages.
=cut
@@ -4876,18 +4880,8 @@ sub ProcessCustomDateRanges {
}
if ($need_save) {
- my ( $ret, $msg );
- if ( $db_config->id ) {
- ( $ret, $msg ) = $db_config->SetContent($content);
- }
- else {
- ( $ret, $msg ) = $db_config->Create(Name => 'CustomDateRangesUI', Content => $content);
- }
-
- unless ($ret) {
- RT->Logger->error("Couldn't save content: $msg");
- push @results, $msg;
- }
+ $args_ref->{'CustomDateRangesUI-Current'} = ''; # EditConfig.html needs this to update CustomDateRangesUI
+ $args_ref->{CustomDateRangesUI} = $content;
}
return @results;
}
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index 0f1020bf46..0c5e44e3a7 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -644,8 +644,6 @@ sub BuildMainNav {
title => loc('Edit Search'), path => "/Search/Build.html$args" );
$current_search_menu->child( advanced =>
title => loc('Advanced'), path => "/Search/Edit.html$args" );
- $current_search_menu->child( custom_date_ranges =>
- title => loc('Custom Date Ranges'), path => "/Search/CustomDateRanges.html" ) if $class eq 'RT::Tickets';
if ($has_query) {
$current_search_menu->child( results => title => loc('Show Results'), path => "/Search/Results.html$args" );
}
diff --git a/share/html/Admin/Tools/EditConfig.html b/share/html/Admin/Tools/EditConfig.html
index 0d7af8c6ec..7cd701cadd 100644
--- a/share/html/Admin/Tools/EditConfig.html
+++ b/share/html/Admin/Tools/EditConfig.html
@@ -67,10 +67,16 @@ if (delete $ARGS{Update}) {
$RT::Handle->BeginTransaction;
my $has_error;
+ if ( delete $ARGS{CustomDateRanges} ) {
+ push @results, ProcessCustomDateRanges( ARGSRef => \%ARGS );
+ }
+
eval {
for my $key (keys %ARGS) {
next if $key =~ /-Current$/;
next if $key eq 'tab' || $key eq 'section' || $key eq 'subsection';
+ # Get rid of extra arguments like in CustomDateRanges
+ next if !exists $ARGS{$key . '-Current'};
my $meta = RT->Config->Meta( $key );
my $widget = $meta->{Widget} || '/Widgets/Form/Code';
diff --git a/share/html/Search/CustomDateRanges.html b/share/html/Widgets/Form/CustomDateRanges
similarity index 76%
rename from share/html/Search/CustomDateRanges.html
rename to share/html/Widgets/Form/CustomDateRanges
index cef7ec821e..e51dd2891d 100644
--- a/share/html/Search/CustomDateRanges.html
+++ b/share/html/Widgets/Form/CustomDateRanges
@@ -45,11 +45,18 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<& /Elements/Header, Title => loc('Custom Date Ranges') &>
-<& /Elements/Tabs &>
+<%DOC>
+see docs/extending/using_forms_widgets.pod
-<& /Elements/ListActions, actions => \@results &>
+This is a special widget only for custom date ranges, it obeys the
+InputOnly/Process basic structure but no general arguments like Name,
+Default, etc.
+</%DOC>
+<& SELF:InputOnly, %ARGS &>
+
+<%METHOD InputOnly>
+<input type="hidden" name="CustomDateRanges" value="1" />
<&|/Widgets/TitleBox, title => loc('Custom Date Ranges In Config Files'), class => 'mx-auto max-width-xl' &>
% if ( $config && keys %{$config->{'RT::Ticket'}} ) {
<& /Elements/ShowCustomDateRanges, CustomDateRanges => $config->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
@@ -59,38 +66,20 @@
% }
</&>
-<form name="CustomDateRanges" method="POST" method="?">
- <&|/Widgets/TitleBox, title => loc('Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
- <& /Elements/EditCustomDateRanges, CustomDateRanges => $content->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
- <& /Elements/Submit, Name => 'Save', Label => loc('Save Changes') &>
- </&>
-</form>
+<&|/Widgets/TitleBox, title => loc('Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
+ <& /Elements/EditCustomDateRanges, CustomDateRanges => $content->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
+</&>
<%INIT>
-Abort(loc("Permission Denied")) unless $session{'CurrentUser'}->HasRight( Object=> RT->System, Right => 'SuperUser');
-
my $config = RT->Config->Get('CustomDateRanges');
my $db_config = RT::Configuration->new( $session{CurrentUser} );
$db_config->LoadByCols( Name => 'CustomDateRangesUI', Disabled => 0 );
my $content;
-
$content = $db_config->_DeserializeContent( $db_config->Content ) if $db_config->id;
-
-my @results;
-
-if ($Save) {
- push @results, ProcessCustomDateRanges( ARGSRef => \%ARGS );
-}
-
-MaybeRedirectForResults(
- Actions => \@results,
- Path => '/Search/CustomDateRanges.html',
-);
-
</%INIT>
+</%METHOD>
-<%ARGS>
-$Save => undef
-</%ARGS>
+<%METHOD Process>
+</%METHOD>
commit 1a62ec2062b88c4bd4855f061b85b35e4b840c9d
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 04:46:55 2020 +0800
Add decent space for nested widgets on system edit config page
E.g. "Custom Date Ranges In Config Files" and "Custom Date Ranges"
widgets in "Features - Custom Date Ranges"
diff --git a/share/static/css/elevator-light/admin.css b/share/static/css/elevator-light/admin.css
index 6e847a83b0..df5a5e1924 100644
--- a/share/static/css/elevator-light/admin.css
+++ b/share/static/css/elevator-light/admin.css
@@ -211,6 +211,7 @@ div.inline-row i {
margin-top: 0;
}
+.configuration .tab-content .card.titlebox .card.titlebox,
.configuration > .titlebox-content {
margin-top: 20px;
}
commit 65c4cbd9e0e90c10f43348f0ea8574532b892e99
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 06:20:09 2020 +0800
Support users(with ModifySelf) to update custom date ranges
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 785ad02c7c..a5e8d8cbce 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -4748,29 +4748,43 @@ sub UpdateDashboard {
}
}
-=head2 ProcessCustomDateRanges ARGSRef => ARGSREF
+=head2 ProcessCustomDateRanges ARGSRef => ARGSREF, UserPreference => 0|1
For system database configuration, it adds corresponding arguments to the
passed ARGSRef, and the following code on EditConfig.html page will do the
real update job.
+For user preference, it updates attributes accordingly.
+
Returns an array of results messages.
=cut
sub ProcessCustomDateRanges {
my %args = (
- ARGSRef => undef,
+ ARGSRef => undef,
+ UserPreference => 0,
@_
);
my $args_ref = $args{ARGSRef};
- my $config = RT->Config->Get('CustomDateRanges');
- my $db_config = RT::Configuration->new( $session{CurrentUser} );
- $db_config->LoadByCols( Name => 'CustomDateRangesUI', Disabled => 0 );
+ my ( $config, $content );
+ if ( $args{UserPreference} ) {
+ $config = { 'RT::Ticket' => { RT::Ticket->CustomDateRanges( ExcludeUser => $session{CurrentUser}->Id ) } };
+ $content = $session{CurrentUser}->Preferences('CustomDateRanges');
+
+ # SetPreferences also checks rights, we short-circuit to avoid
+ # returning misleading messages.
- my $content;
- $content = $db_config->_DeserializeContent( $db_config->Content ) if $db_config->id;
+ return ( 0, loc("No permission to set preferences") )
+ unless $session{CurrentUser}->CurrentUserCanModify('Preferences');
+ }
+ else {
+ $config = RT->Config->Get('CustomDateRanges');
+ my $db_config = RT::Configuration->new( $session{CurrentUser} );
+ $db_config->LoadByCols( Name => 'CustomDateRangesUI', Disabled => 0 );
+ $content = $db_config->_DeserializeContent( $db_config->Content ) if $db_config->id;
+ }
my @results;
my %label = (
@@ -4880,8 +4894,24 @@ sub ProcessCustomDateRanges {
}
if ($need_save) {
- $args_ref->{'CustomDateRangesUI-Current'} = ''; # EditConfig.html needs this to update CustomDateRangesUI
- $args_ref->{CustomDateRangesUI} = $content;
+ if ( $args{UserPreference} ) {
+ my ( $ret, $msg );
+ if ( keys %{$content->{'RT::Ticket'}} ) {
+ ( $ret, $msg ) = $session{CurrentUser}->SetPreferences( 'CustomDateRanges', $content );
+ }
+ else {
+ ( $ret, $msg ) = $session{CurrentUser}->DeletePreferences( 'CustomDateRanges' );
+ }
+
+ unless ($ret) {
+ RT->Logger->error($msg);
+ push @results, $msg;
+ }
+ }
+ else {
+ $args_ref->{'CustomDateRangesUI-Current'} = ''; # EditConfig.html needs this to update CustomDateRangesUI
+ $args_ref->{CustomDateRangesUI} = $content;
+ }
}
return @results;
}
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index 0c5e44e3a7..e58f21b903 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -299,6 +299,21 @@ sub BuildMainNav {
);
}
+
+ if ( $request_path eq '/Prefs/SearchOptions.html' ) {
+ $page->child(
+ custom_date_ranges => title => loc('Custom Date Ranges'),
+ path => "/Prefs/CustomDateRanges.html"
+ );
+ }
+
+ if ( $request_path eq '/Prefs/CustomDateRanges.html' ) {
+ $page->child(
+ search_options => title => loc('Search Preferences'),
+ path => "/Prefs/SearchOptions.html"
+ );
+ }
+
}
my $logout_url = RT->Config->Get('LogoutURL');
if ( $current_user->Name
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 6e53760122..d66b70405c 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -2648,17 +2648,46 @@ Return all of the custom date ranges of current class.
sub CustomDateRanges {
my $self = shift;
- my $type = ref $self || $self;
+ my %args = (
+ Type => undef,
+ ExcludeSystem => undef,
+ ExcludeUsers => undef,
+ ExcludeUser => undef,
+ @_,
+ );
+ my $type = $args{Type} || ref $self || $self,;
my %ranges;
- if ( my $config = RT->Config->Get('CustomDateRanges') ) {
- %ranges = %{ $config->{$type} } if $config->{$type};
+ if ( !$args{ExcludeSystem} ) {
+ if ( my $config = RT->Config->Get('CustomDateRanges') ) {
+ for my $name ( keys %{ $config->{$type} || {} } ) {
+ $ranges{$name} ||= $config->{$type}{$name};
+ }
+ }
+
+ if ( my $db_config = RT->Config->Get('CustomDateRangesUI') ) {
+ for my $name ( keys %{ $db_config->{$type} || {} } ) {
+ $ranges{$name} ||= $db_config->{$type}{$name};
+ }
+ }
}
- if ( my $db_config = RT->Config->Get('CustomDateRangesUI') ) {
- for my $name ( keys %{ $db_config->{$type} || {} } ) {
- $ranges{$name} ||= $db_config->{$type}{$name};
+ if ( !$args{ExcludeUsers} ) {
+ my $attributes = RT::Attributes->new( RT->SystemUser );
+ $attributes->Limit( FIELD => 'Name', VALUE => 'Pref-CustomDateRanges' );
+ $attributes->Limit( FIELD => 'ObjectType', VALUE => 'RT::User' );
+ if ( $args{ExcludeUser} ) {
+ $attributes->Limit( FIELD => 'Creator', OPERATOR => '!=', VALUE => $args{ExcludeUser} );
+ }
+ $attributes->OrderBy( FIELD => 'id' );
+
+ while ( my $attribute = $attributes->Next ) {
+ if ( my $content = $attribute->Content ) {
+ for my $name ( keys %{ $content->{$type} || {} } ) {
+ $ranges{$name} ||= $content->{$type}{$name};
+ }
+ }
}
}
return %ranges;
diff --git a/share/html/Prefs/CustomDateRanges.html b/share/html/Prefs/CustomDateRanges.html
new file mode 100644
index 0000000000..0ed9fbbc3e
--- /dev/null
+++ b/share/html/Prefs/CustomDateRanges.html
@@ -0,0 +1,100 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (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 }}}
+<& /Elements/Header, Title => loc('Custom Date Ranges') &>
+<& /Elements/Tabs &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<&|/Widgets/TitleBox, title => loc('System Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
+% if ( keys %{$system_config->{'RT::Ticket'}} ) {
+ <& /Elements/ShowCustomDateRanges, CustomDateRanges => $system_config->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
+% }
+% else {
+ <p class="mt-3 mt-1 ml-3"><&|/l&>No system custom date ranges</&></p>
+% }
+</&>
+
+<&|/Widgets/TitleBox, title => loc('Other Users Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
+% if ( keys %{$user_config->{'RT::Ticket'}} ) {
+ <& /Elements/ShowCustomDateRanges, CustomDateRanges => $user_config->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
+% }
+% else {
+ <p class="mt-3 mt-1 ml-3"><&|/l&>No other users custom date ranges</&></p>
+% }
+</&>
+
+<form name="CustomDateRanges" method="POST">
+ <&|/Widgets/TitleBox, title => loc('My Custom Date Ranges'), class => 'mx-auto max-width-xl' &>
+ <& /Elements/EditCustomDateRanges, CustomDateRanges => $content->{'RT::Ticket'} || {}, ObjectType => 'RT::Ticket' &>
+ <& /Elements/Submit, Name => 'Save', Label => loc('Save Changes') &>
+ </&>
+</form>
+
+<%INIT>
+my $system_config = { 'RT::Ticket' => { RT::Ticket->CustomDateRanges( ExcludeUsers => 1 ) } };
+my $user_config = {
+ 'RT::Ticket' => { RT::Ticket->CustomDateRanges( ExcludeSystem => 1, ExcludeUser => $session{CurrentUser}->Id ) } };
+
+my $content = $session{CurrentUser}->Preferences('CustomDateRanges');
+
+my @results;
+
+if ($Save) {
+ push @results, ProcessCustomDateRanges( ARGSRef => \%ARGS, UserPreference => 1 );
+}
+
+MaybeRedirectForResults(
+ Actions => \@results,
+ Path => '/Prefs/CustomDateRanges.html',
+);
+
+</%INIT>
+
+<%ARGS>
+$Save => undef
+</%ARGS>
commit 0355413e5af580eaa013bda475ebd41e5d441483
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu May 7 09:38:03 2020 +0800
Warn custom date range name conflicts on config load
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index ccac869202..be9828119f 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -1218,6 +1218,38 @@ our %META;
RT->Logger->error("Config option \%CustomDateRanges{$class} is not a HASH");
}
}
+
+ my %system_config = %$ranges;
+ if ( my $db_config = $config->Get('CustomDateRangesUI') ) {
+ for my $type ( keys %$db_config ) {
+ for my $name ( keys %{ $db_config->{$type} || {} } ) {
+ if ( $system_config{$type}{$name} ) {
+ RT->Logger->warning("$type custom date range $name is defined by config file and db");
+ }
+ else {
+ $system_config{$name} = $db_config->{$type}{$name};
+ }
+ }
+ }
+ }
+
+ for my $type ( keys %system_config ) {
+ my $attributes = RT::Attributes->new( RT->SystemUser );
+ $attributes->Limit( FIELD => 'Name', VALUE => 'Pref-CustomDateRanges' );
+ $attributes->Limit( FIELD => 'ObjectType', VALUE => 'RT::User' );
+ $attributes->OrderBy( FIELD => 'id' );
+
+ while ( my $attribute = $attributes->Next ) {
+ if ( my $content = $attribute->Content ) {
+ for my $name ( keys %{ $content->{$type} || {} } ) {
+ if ( $system_config{$type}{$name} ) {
+ RT->Logger->warning( "$type custom date range $name is defined by system and user #"
+ . $attribute->ObjectId );
+ }
+ }
+ }
+ }
+ }
},
},
CustomDateRangesUI => {
-----------------------------------------------------------------------
More information about the rt-commit
mailing list