[Rt-commit] rt branch, 4.6/custom-date-ranges-config-in-web-ui, created. rt-4.4.4-726-ga5c790ae4a

Michel Rodriguez michel at bestpractical.com
Wed Feb 5 04:37:33 EST 2020


The branch, 4.6/custom-date-ranges-config-in-web-ui has been created
        at  a5c790ae4ae45de190cd4714f48192f21a14c40d (commit)

- Log -----------------------------------------------------------------
commit dc5b10a7a8e6bf06006731a00a60370a03fc3e09
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 bc6f947b02..7d85eea92e 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1757,119 +1757,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
@@ -4537,6 +4424,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 fa4056d99cdcd9a94d6fe6384565b66925718a1b
Author: michel <michel at bestpractical.com>
Date:   Tue Feb 4 17:28:33 2020 +0100

    First stab at displaying both types of CustomDateRanges in the config tab.

diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index 9a71a48b81..46a57e81c5 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -1190,6 +1190,8 @@ our %META;
     },
     CustomDateRanges => {
         Type            => 'HASH',
+        WidgetArguments => { Hints  => 'Custom Data Ranges in the data base can be edited through the query editor', }, # loc
+        Dual  => 1,
         PostLoadCheck   => sub {
             my $config = shift;
             # use scalar context intentionally to avoid not a hash error
diff --git a/share/html/Admin/Tools/Config/Elements/Option b/share/html/Admin/Tools/Config/Elements/Option
index 41617d0b44..4879cc7519 100644
--- a/share/html/Admin/Tools/Config/Elements/Option
+++ b/share/html/Admin/Tools/Config/Elements/Option
@@ -72,6 +72,18 @@ return if $name =~ /Password/i && $name !~ /MinimumPasswordLength/;
 my $has_execute_code = $session{CurrentUser}->HasRight(Right => 'ExecuteCode', Object => RT->System);
 
 my $raw_value = RT->Config->Get( $name );
+if ( $meta->{Dual} ) {
+    my $attribute = RT->System->FirstAttribute( $name );
+    if ( $attribute ) {
+        # this actually does not work for CustomDateRanges when they have all been deleted
+        # since the top level ('RT::Ticket') may still exists, even though it's empty
+        if ( my $value_in_attribute = $attribute->Content ) {
+            $raw_value = { 'In Config Files'  => $raw_value,
+                           'In Data Base'     => $value_in_attribute,
+            };
+        }
+    }
+}
 my $val = $stringify->($raw_value);
 my $doc_url = "https://docs.bestpractical.com/rt/$doc_version/RT_Config.html#$option->{Help}";
 my $widget = $meta->{'Widget'} || '/Widgets/Form/Code';

commit 5788e0657fd015cd2026192b298fd1cd923fbcc9
Author: michel <michel at bestpractical.com>
Date:   Tue Feb 4 19:56:45 2020 +0100

    Display Dual values in Config display page

diff --git a/share/html/Admin/Tools/Configuration.html b/share/html/Admin/Tools/Configuration.html
index 1cc1ed7899..5e91a01751 100644
--- a/share/html/Admin/Tools/Configuration.html
+++ b/share/html/Admin/Tools/Configuration.html
@@ -85,7 +85,7 @@ foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
     }
     else {
         $description = loc("core config");
-    }
+    } 
     $index_conf++;
 </%PERL>
   <div class="form-row <% $index_conf%2 ? 'oddline' : 'evenline'%>">
@@ -105,6 +105,31 @@ foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
 % }
     </div>
   </div>
+<%PERL>
+# dual options live both in the config files _and_ as an attribute
+# display the attribute
+my $dual_value;
+if ( $meta->{Dual} ) {
+    my $attribute = RT->System->FirstAttribute( $key );
+    if ( $attribute ) {
+        $dual_value = $attribute->Content;
+        $index_conf++;
+        $description = loc("database");
+    }
+}
+</%PERL>
+% if ( defined $dual_value ) {
+  <div class="form-row <% $index_conf%2 ? 'oddline' : 'evenline'%>">
+    <div class="col-md-4 collection-as-table value"><% $key %></div>
+    <div class="col-md-4 collection-as-table value">
+      <% stringify($dual_value) |n %>\
+    </div>
+    <div class="col-md-4 collection-as-table value">
+      <% $description %>
+    </div>
+  </div>
+% }
+
 % }
 </&>
 

commit b56a2c4595772d2083cb6b692f0a516787558a16
Author: michel <michel at bestpractical.com>
Date:   Tue Feb 4 17:34:01 2020 +0100

    Hide the [Custom Data Ranges] menu entry for users who don't have the rights.

diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index 41b6b4c2c3..56d525fe1e 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -547,7 +547,8 @@ sub BuildMainNav {
         $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';
+            title => loc('Custom Date Ranges'), path => "/Search/CustomDateRanges.html" )
+                if $class eq 'RT::Tickets' && $current_user->HasRight( Object=> RT->System, Right => 'SuperUser');
         if ($has_query) {
             $current_search_menu->child( results => title => loc('Show Results'), path => "/Search/Results.html$args" );
         }

commit f6f3ecec0ffc2fac26f6a27f925657bcd173eb32
Author: michel <michel at bestpractical.com>
Date:   Tue Feb 4 20:28:21 2020 +0100

    Hide Global Attribute display for Dual config options.

diff --git a/share/html/Admin/Tools/Configuration.html b/share/html/Admin/Tools/Configuration.html
index 5e91a01751..13842c280c 100644
--- a/share/html/Admin/Tools/Configuration.html
+++ b/share/html/Admin/Tools/Configuration.html
@@ -254,6 +254,8 @@ for my $type (qw/Tickets Queues Transactions Groups PrivilegedUsers Unprivileged
 % my $index_size = 0;
 % while ( my $attr = $attrs->Next ) {
 % next if $attr->Name eq 'UpgradeHistory';
+% my $meta = RT->Config->Meta( $attr->Name );
+% next if $meta && $meta->{Dual};
 <div class="<% $index_size%2 ? 'oddline' : 'evenline'%> form-row">
 % if ($attr->Name eq 'UserLogo') {
 %   my $content = $attr->Content;

commit a5c790ae4ae45de190cd4714f48192f21a14c40d
Author: michel <michel at bestpractical.com>
Date:   Wed Feb 5 09:47:58 2020 +0100

    Use bootstrap for custom date ranges edit form.

diff --git a/share/html/Search/CustomDateRanges.html b/share/html/Search/CustomDateRanges.html
index 0eef09ba16..f8e9e51e6e 100644
--- a/share/html/Search/CustomDateRanges.html
+++ b/share/html/Search/CustomDateRanges.html
@@ -84,61 +84,61 @@
 
 <form name="CustomDateRanges" method="POST" method="?">
   <&|/Widgets/TitleBox, title => loc('Custom Date Ranges') &>
-    <table class="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">
-          <input type="checkbox" name="DeleteAll" value="1" onclick="setCheckbox(this, /^\d+-Delete$/)" />
-          <&|/l&>Delete</&>
-        </th>
-      </tr>
+  <div id="custom-date-ranges">
+    <div class="form-row input-row-header">
+      <div class="col-md-2"><&|/l&>Name</&></div>
+      <div class="col-md-2"><&|/l&>From</&></div>
+      <div class="col-md-2"><&|/l&>From Value if Unset</&></div>
+      <div class="col-md-2"><&|/l&>To</&></div>
+      <div class="col-md-2"><&|/l&>To Value if Unset</&></div>
+      <div class="col-md-1"><&|/l&>Business<br>Hours?</&></div>
+      <div class="col-md-1">
+        <input type="checkbox" name="DeleteAll" value="1" onclick="setCheckbox(this, /^\d+-Delete$/)" />
+        <&|/l&>Delete</&>
+      </div>
+    </div>
 % my $i = 0;
 % if ( $content ) {
 % 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>
+      <div class="form-row input-row">
+        <div class="col-md-2"><input type="text" size="15" name="<% $id %>-name" value="<% $name %>" /></div>
 %       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">
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => "$id-from", Default => $date_range_spec{from} &></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => "$id-from_fallback", Default => $date_range_spec{from_fallback} &></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => "$id-to", Default => $date_range_spec{to} &></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => "$id-to_fallback", Default => $date_range_spec{to_fallback} &></div>
+        <div class="col-md-1">
           <select name="<% $id %>-business_time">
             <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>
+        </div>
+        <div class="col-md-1"><input type="checkbox" name="<% $id %>-Delete" value="1" /></div>
+      </div>
 %     $id++;
 %   }
 % }
 
 % for ( 1 .. 3 ) {
 % $i++;
-      <tr class="<% $i % 2 ? 'oddline' : 'evenline' %>">
-        <td class="collection-as-table"><input type="text" name="name" value="" /></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">
+      <div class="form-row input-row">
+        <div class="col-md-2"><input type="text" size="15" name="name" value="" /></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => 'from' &></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => 'from_fallback' &></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => 'to' &></div>
+        <div class="col-md-2"><& /Elements/SelectCustomDateRangeField, Name => 'to_fallback' &></div>
+        <div class="col-md-1">
           <select name="business_time">
             <option value="1"><&|/l&>Yes</&></option>
             <option value="0" selected="selected"><&|/l&>No</&></option>
           </select>
-        </td>
-        <td class="collection-as-table"></td>
-      </tr>
+        </div>
+        <div class="col-md-1"></div>
+      </div>
 % }
-    </table>
+    </div>
     <& /Elements/Submit, Name => 'Save', Label => loc('Save Changes') &>
   </&>
 </form>
diff --git a/share/static/css/elevator-light/forms.css b/share/static/css/elevator-light/forms.css
index e422e40bab..d9720afb31 100644
--- a/share/static/css/elevator-light/forms.css
+++ b/share/static/css/elevator-light/forms.css
@@ -54,6 +54,7 @@ div.results .titlebox-title .left {
   border-width: 1px 1px 0 1px;
 }
 
+div.input-row-header div,
 div.results .titlebox,
 div.error-titlebox {
   border: none;
@@ -61,6 +62,10 @@ div.error-titlebox {
   font-weight: bold;
 }
 
+div.input-row-header div input {
+    vertical-align: middle;
+}
+
 div.results .titlebox-content {
   border: 1px solid #aa9;
   border-bottom: 2px solid #990;

-----------------------------------------------------------------------


More information about the rt-commit mailing list