[Rt-commit] rt branch, 5.0/priority-as-string, created. rt-5.0.0alpha1-19-g8f4722c85c

? sunnavy sunnavy at bestpractical.com
Wed Mar 18 15:17:36 EDT 2020


The branch, 5.0/priority-as-string has been created
        at  8f4722c85ce6f347c1e81dcec03b17d365ab7840 (commit)

- Log -----------------------------------------------------------------
commit 020ad76279ab64845b742f5e8594f6c52fb0825e
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Feb 26 22:56:09 2020 +0800

    Core RT-Extension-PriorityAsString

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 9bed2dbd13..23066ea535 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2709,6 +2709,42 @@ cut down on page clutter. Once this option is clicked the link will change to
 
 Set($HideOneTimeSuggestions, 0);
 
+=item C<%PriorityAsString>
+
+Specify a mapping between priority strings and the internal numeric
+representation
+
+=cut
+
+Set(%PriorityAsString, (Low => 0, Medium => 50, High => 100));
+
+=item C<@PriorityAsStringOrder>
+
+Fine-tuned control of the order of priorities as displayed in the drop-down
+box; usually this computed automatically and need not be set explicitly.  It
+can be used to limit the set of options presented during update, but allow a
+richer set of levels when they are adjusted automatically.
+
+=cut
+
+Set(@PriorityAsStringOrder, qw(Low Medium High));
+
+=item C<%PriorityAsStringQueues>
+
+Each key is the name of a different queue; queues which do not appear in
+this configuration will use RT's default numeric scale.  This option means
+that C<%PriorityAsString> and C<@PriorityAsStringOrder> are ignored (no
+global override, you must specify a set of priorities per queue).
+
+    Set(%PriorityAsStringQueues,
+       General => { Low => 0, Medium => 50, High => 100 },
+       Binary  => { Low => 0, High => 10 },
+    );
+
+=cut
+
+Set(%PriorityAsStringQueues, ());
+
 =back
 
 =head2 Group Summary Configuration
diff --git a/lib/RT.pm b/lib/RT.pm
index 6116333a40..b7a294b6a8 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -767,6 +767,7 @@ our %CORED_PLUGINS = (
     'RT::Extension::AdminConditionsAndActions' => '4.4.2',
     'RT::Extension::RightsInspector' => '5.0',
     'RT::Extension::ConfigInDatabase' => '5.0',
+    'RT::Extension::PriorityAsString' => '5.0',
 );
 
 sub InitPlugins {
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index d0946326ba..2814989ba2 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -3731,6 +3731,46 @@ sub Serialize {
     return %store;
 }
 
+sub PriorityAsString {
+    my $self = shift;
+    return $self->_PriorityAsString( $self->Priority );
+}
+
+sub InitialPriorityAsString {
+    my $self = shift;
+    return $self->_PriorityAsString( $self->InitialPriority );
+}
+
+sub FinalPriorityAsString {
+    my $self = shift;
+    return $self->_PriorityAsString( $self->FinalPriority );
+}
+
+sub _PriorityAsString {
+    my $self     = shift;
+    my $priority = shift;
+    return undef unless defined $priority && length $priority;
+
+    my %map;
+    my $queues = RT->Config->Get('PriorityAsStringQueues');
+    if (@_) {
+        %map = %{ shift(@_) };
+    }
+    elsif ( $queues and $queues->{ $self->QueueObj->Name } ) {
+        %map = %{ $queues->{ $self->QueueObj->Name } };
+    }
+    else {
+        %map = RT->Config->Get('PriorityAsString');
+    }
+
+    # Count from high down to low until we find one that our number is
+    # greater than or equal to.
+    foreach my $label ( sort { $map{$b} <=> $map{$a} } keys %map ) {
+        return $label if $priority >= $map{$label};
+    }
+    return "unknown";
+}
+
 RT::Base->_ImportOverlays();
 
 1;
diff --git a/share/html/Elements/RT__Ticket/ColumnMap b/share/html/Elements/RT__Ticket/ColumnMap
index 1d9c1be72d..0162875d55 100644
--- a/share/html/Elements/RT__Ticket/ColumnMap
+++ b/share/html/Elements/RT__Ticket/ColumnMap
@@ -384,6 +384,45 @@ if(RT->Config->Get('DisplayTotalTimeWorked')) {
     }
 }
 
+{
+    my $printer = sub {
+        my ( $class, $string ) = @_;
+        return '' unless defined $string && length $string;
+
+        my $request_path = $HTML::Mason::Commands::r->path_info // '';
+        if ( $request_path =~ /Results\.tsv/ ) {
+            return loc($string);
+        }
+
+        my $escaped     = $m->interp->apply_escapes( $string,      'h' );
+        my $loc_escaped = $m->interp->apply_escapes( loc($string), 'h' );
+        return \( qq{<span class="ticket-info-$class-} . lc($escaped) . qq{">$loc_escaped</span>} );
+
+    };
+    foreach my $field (qw(Priority InitialPriority FinalPriority)) {
+        $COLUMN_MAP->{ $field . 'Number' } ||= $COLUMN_MAP->{$field};
+
+        my $class = lc($field);
+        $class =~ s/(?=<.)(?=priority)/-/;
+
+        my $method = $field . 'AsString';
+
+        my %queues = RT->Config->Get('PriorityAsStringQueues');
+        if ( not keys %queues ) {
+            $COLUMN_MAP->{$field}{'value'} = sub {
+                return $printer->( $class, $_[0]->$method() );
+            };
+        }
+        else {
+            $COLUMN_MAP->{$field}{'value'} = sub {
+                return $queues{ $_[0]->QueueObj->Name }
+                    ? $printer->( $class, $_[0]->$method() )
+                    : $_[0]->$field;
+            };
+        }
+    }
+}
+
 $m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
 return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
 </%init>
diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriority
index 2889b4629e..0dfec265b6 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriority
@@ -51,5 +51,25 @@ $Name => 'Priority'
 $Default => ''
 </%ARGS>
 <%INIT>
+my %queues = RT->Config->Get('PriorityAsStringQueues');
+
+# If enabled for all queues, always show the drop-down
+return $m->comp("/Elements/SelectPriorityAsString",%ARGS)
+    unless keys %queues;
+
+# Some callsites we can easily override with callbacks with logic to
+# know when to call SelectPriorityAsString; for ticket create and queue
+# modify, we need to inspect the callstack.
+my $caller      = $m->callers(1)->path;
+my $caller_args = $m->caller_args(1);
+my $QueueObj = RT::Queue->new( $session{'CurrentUser'} );
+if ( $caller eq "/Admin/Queues/Modify.html") {
+    $QueueObj->Load( $caller_args->{id} ) || $QueueObj->Load( $caller_args->{Name} );
+} elsif ( $caller eq "/Ticket/Create.html" or $caller eq "/m/ticket/create" ) {
+    $QueueObj->Load( $caller_args->{Queue} );
+}
+return $m->comp("/Elements/SelectPriorityAsString",%ARGS, Mapping => $queues{$QueueObj->Name})
+    if $QueueObj->Id and $queues{$QueueObj->Name};
+
 $Default = '' unless defined $Default;
 </%INIT>
diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriorityAsString
similarity index 67%
copy from share/html/Elements/SelectPriority
copy to share/html/Elements/SelectPriorityAsString
index 2889b4629e..bc1d4c46b9 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriorityAsString
@@ -45,11 +45,39 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<input name="<% $Name %>" type="text" value="<% $Default %>" size="5" class="form-control" />
+<select class="select-priority selectpicker form-control" name="<% $Name %>">
+% unless ( defined $Default ) {
+  <option value="">-</option>
+% }
+<%PERL>
+foreach my $label ( @order ) {
+    my ($value, $selected);
+    if ( $label eq $default_label ) {
+        ($value, $selected) = ($Default, 'selected="selected"');
+    } else {
+        ($value, $selected) = ($map{ $label }, '');
+    }
+</%PERL>
+  <option class="<% lc $label %>" value="<% $value %>" <% $selected |n %>><% loc($label) %></option>
+% }
+</select>
 <%ARGS>
 $Name => 'Priority'
-$Default => ''
+$Default => undef
+$Mapping => undef
 </%ARGS>
 <%INIT>
-$Default = '' unless defined $Default;
+
+my %map   = $Mapping ? %{ $Mapping } : RT->Config->Get('PriorityAsString');
+my @order;
+if (not $Mapping and RT->Config->Get('PriorityAsStringOrder')) {
+    @order = grep {exists $map{$_}} RT->Config->Get('PriorityAsStringOrder');
+} else {
+    @order = sort { $map{$a} <=> $map{$b} } keys %map;
+}
+
+my $default_label = '';
+if ( defined $Default && length $Default ) {
+    $default_label = RT::Ticket->_PriorityAsString( $Default, \%map ) || '';
+}
 </%INIT>
diff --git a/share/html/Search/Elements/PickBasics b/share/html/Search/Elements/PickBasics
index 124fc7d9b3..3d4612cbca 100644
--- a/share/html/Search/Elements/PickBasics
+++ b/share/html/Search/Elements/PickBasics
@@ -398,6 +398,27 @@ else {
             Value => { Type => 'text', Size => 5 }
         },
     );
+
+    my %as_string = RT->Config->Get('PriorityAsStringQueues');
+    if ( %as_string && %queues && scalar(keys %queues) == grep {$as_string{$_}} keys %queues ) {
+        # Additionally, all queues in PriorityAsStringQueues must use the _same_
+        # values for each name; if "High" is mapped to 10 in one queue and 100
+        # in another, we can't use names
+        my %values;
+        my $match = 1;
+        for my $q (keys %queues) {
+            for my $priority (keys %{$as_string{$q}}) {
+                $match = 0 if exists $values{$priority} and $as_string{$q}{$priority} != $values{$priority};
+                $values{$priority} = $as_string{$q}{$priority};
+            }
+        }
+        if ( $match ) {
+            # Swap out the /Elements/SelectPriority for /Elements/SelectPriorityAsString
+            my ($priority) = grep {$_->{Name} eq "Priority"} @lines;
+            $priority->{Value}{Path} = "/Elements/SelectPriorityAsString";
+            $priority->{Value}{Arguments}{Mapping} = \%values;
+        }
+    }
 }
 
 $m->callback( Conditions => \@lines );
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 7f15512880..9309da30c6 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -162,6 +162,17 @@ if ($ExcludeOwner) {
 # inflate the marker for custom roles into the field specs for each one
 @fields = map { ($_->{special}||'') eq 'roles' ? @role_fields : $_ } @fields;
 
+my %as_string = RT->Config->Get('PriorityAsStringQueues');
+
+if ( keys %as_string && $TicketObj && $as_string{$TicketObj->QueueObj->Name} ) {
+    # Swap out the /Elements/SelectPriority for /Elements/SelectPriorityAsString
+    for my $field (@fields) {
+        next unless ($field->{comp}||'') eq "/Elements/SelectPriority";
+        $field->{comp} = "/Elements/SelectPriorityAsString";
+        $field->{args}{Mapping} = $as_string{$TicketObj->QueueObj->Name};
+    }
+}
+
 $m->callback( CallbackName => 'MassageFields', %ARGS, TicketObj => $TicketObj, Fields => \@fields );
 
 # Process the field list, skipping if html is provided and running the
diff --git a/share/html/Ticket/Elements/ShowPriority b/share/html/Ticket/Elements/ShowPriority
index 5fb0ad82c6..8043e34f52 100644
--- a/share/html/Ticket/Elements/ShowPriority
+++ b/share/html/Ticket/Elements/ShowPriority
@@ -45,7 +45,24 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
+% if (keys %queues and not $queues{$Ticket->QueueObj->Name}) {
 <% $Ticket->Priority %>/<% $Ticket->FinalPriority || ''%>
+% } else {
+% my $current = $Ticket->PriorityAsString || '';
+% my $final = $Ticket->FinalPriorityAsString || '';
+<span class="ticket-info-priority-<% $CSSClass->(lc($current)) %>"><% loc($current) %></span>/\
+<span class="ticket-info-final-priority-<% $CSSClass->(lc($final)) %>"><% loc($final) %></span>
+% }
 <%ARGS>
 $Ticket => undef
 </%ARGS>
+<%INIT>
+my %queues = RT->Config->Get('PriorityAsStringQueues');
+
+my $CSSClass = sub {
+    my $value = shift;
+    return '' unless defined $value;
+    $value =~ s/[^A-Za-z0-9_-]/_/g;
+    return $value;
+};
+</%INIT>

commit 00e43354ee821b1b9bf9703acf061ecf54fd3af0
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Feb 29 02:28:47 2020 +0800

    Add "priority-" prefix to css classes of priority strings
    
    CSS classes like low/medium/high could cause name conflicts easily.

diff --git a/share/html/Elements/SelectPriorityAsString b/share/html/Elements/SelectPriorityAsString
index bc1d4c46b9..2eca8da620 100644
--- a/share/html/Elements/SelectPriorityAsString
+++ b/share/html/Elements/SelectPriorityAsString
@@ -58,7 +58,7 @@ foreach my $label ( @order ) {
         ($value, $selected) = ($map{ $label }, '');
     }
 </%PERL>
-  <option class="<% lc $label %>" value="<% $value %>" <% $selected |n %>><% loc($label) %></option>
+  <option class="priority-<% lc $label %>" value="<% $value %>" <% $selected |n %>><% loc($label) %></option>
 % }
 </select>
 <%ARGS>

commit d4833d239771cd220293eb93cda23d18c79ebcc3
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Feb 27 04:38:20 2020 +0800

    Drop @PriorityAsStringOrder and %PriorityAsStringQueues configs
    
    We will merge their functionality into %PriorityAsString.
    
    As an extension, PriorityAsString grew additional configuration options
    as features were added. As a core feature, we want to add as few new
    configuration options as possible while still providing the needed
    features.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 23066ea535..1933c56f3c 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2718,33 +2718,6 @@ representation
 
 Set(%PriorityAsString, (Low => 0, Medium => 50, High => 100));
 
-=item C<@PriorityAsStringOrder>
-
-Fine-tuned control of the order of priorities as displayed in the drop-down
-box; usually this computed automatically and need not be set explicitly.  It
-can be used to limit the set of options presented during update, but allow a
-richer set of levels when they are adjusted automatically.
-
-=cut
-
-Set(@PriorityAsStringOrder, qw(Low Medium High));
-
-=item C<%PriorityAsStringQueues>
-
-Each key is the name of a different queue; queues which do not appear in
-this configuration will use RT's default numeric scale.  This option means
-that C<%PriorityAsString> and C<@PriorityAsStringOrder> are ignored (no
-global override, you must specify a set of priorities per queue).
-
-    Set(%PriorityAsStringQueues,
-       General => { Low => 0, Medium => 50, High => 100 },
-       Binary  => { Low => 0, High => 10 },
-    );
-
-=cut
-
-Set(%PriorityAsStringQueues, ());
-
 =back
 
 =head2 Group Summary Configuration
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 2814989ba2..7dbdbc4ae9 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -3751,17 +3751,7 @@ sub _PriorityAsString {
     my $priority = shift;
     return undef unless defined $priority && length $priority;
 
-    my %map;
-    my $queues = RT->Config->Get('PriorityAsStringQueues');
-    if (@_) {
-        %map = %{ shift(@_) };
-    }
-    elsif ( $queues and $queues->{ $self->QueueObj->Name } ) {
-        %map = %{ $queues->{ $self->QueueObj->Name } };
-    }
-    else {
-        %map = RT->Config->Get('PriorityAsString');
-    }
+    my %map = RT->Config->Get('PriorityAsString');
 
     # Count from high down to low until we find one that our number is
     # greater than or equal to.
diff --git a/share/html/Elements/RT__Ticket/ColumnMap b/share/html/Elements/RT__Ticket/ColumnMap
index 0162875d55..d1dfb04619 100644
--- a/share/html/Elements/RT__Ticket/ColumnMap
+++ b/share/html/Elements/RT__Ticket/ColumnMap
@@ -407,19 +407,9 @@ if(RT->Config->Get('DisplayTotalTimeWorked')) {
 
         my $method = $field . 'AsString';
 
-        my %queues = RT->Config->Get('PriorityAsStringQueues');
-        if ( not keys %queues ) {
-            $COLUMN_MAP->{$field}{'value'} = sub {
-                return $printer->( $class, $_[0]->$method() );
-            };
-        }
-        else {
-            $COLUMN_MAP->{$field}{'value'} = sub {
-                return $queues{ $_[0]->QueueObj->Name }
-                    ? $printer->( $class, $_[0]->$method() )
-                    : $_[0]->$field;
-            };
-        }
+        $COLUMN_MAP->{$field}{'value'} = sub {
+            return $printer->( $class, $_[0]->$method() );
+        };
     }
 }
 
diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriority
index 0dfec265b6..87b665c0f2 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriority
@@ -51,25 +51,8 @@ $Name => 'Priority'
 $Default => ''
 </%ARGS>
 <%INIT>
-my %queues = RT->Config->Get('PriorityAsStringQueues');
-
-# If enabled for all queues, always show the drop-down
-return $m->comp("/Elements/SelectPriorityAsString",%ARGS)
-    unless keys %queues;
-
-# Some callsites we can easily override with callbacks with logic to
-# know when to call SelectPriorityAsString; for ticket create and queue
-# modify, we need to inspect the callstack.
-my $caller      = $m->callers(1)->path;
-my $caller_args = $m->caller_args(1);
-my $QueueObj = RT::Queue->new( $session{'CurrentUser'} );
-if ( $caller eq "/Admin/Queues/Modify.html") {
-    $QueueObj->Load( $caller_args->{id} ) || $QueueObj->Load( $caller_args->{Name} );
-} elsif ( $caller eq "/Ticket/Create.html" or $caller eq "/m/ticket/create" ) {
-    $QueueObj->Load( $caller_args->{Queue} );
-}
-return $m->comp("/Elements/SelectPriorityAsString",%ARGS, Mapping => $queues{$QueueObj->Name})
-    if $QueueObj->Id and $queues{$QueueObj->Name};
+my %config = RT->Config->Get('PriorityAsString');
+return $m->comp("/Elements/SelectPriorityAsString",%ARGS) if keys %config;
 
 $Default = '' unless defined $Default;
 </%INIT>
diff --git a/share/html/Elements/SelectPriorityAsString b/share/html/Elements/SelectPriorityAsString
index 2eca8da620..0ecfd44873 100644
--- a/share/html/Elements/SelectPriorityAsString
+++ b/share/html/Elements/SelectPriorityAsString
@@ -64,17 +64,11 @@ foreach my $label ( @order ) {
 <%ARGS>
 $Name => 'Priority'
 $Default => undef
-$Mapping => undef
 </%ARGS>
 <%INIT>
 
-my %map   = $Mapping ? %{ $Mapping } : RT->Config->Get('PriorityAsString');
-my @order;
-if (not $Mapping and RT->Config->Get('PriorityAsStringOrder')) {
-    @order = grep {exists $map{$_}} RT->Config->Get('PriorityAsStringOrder');
-} else {
-    @order = sort { $map{$a} <=> $map{$b} } keys %map;
-}
+my %map   = RT->Config->Get('PriorityAsString');
+my @order = sort { $map{$a} <=> $map{$b} } keys %map;
 
 my $default_label = '';
 if ( defined $Default && length $Default ) {
diff --git a/share/html/Search/Elements/PickBasics b/share/html/Search/Elements/PickBasics
index 3d4612cbca..124fc7d9b3 100644
--- a/share/html/Search/Elements/PickBasics
+++ b/share/html/Search/Elements/PickBasics
@@ -398,27 +398,6 @@ else {
             Value => { Type => 'text', Size => 5 }
         },
     );
-
-    my %as_string = RT->Config->Get('PriorityAsStringQueues');
-    if ( %as_string && %queues && scalar(keys %queues) == grep {$as_string{$_}} keys %queues ) {
-        # Additionally, all queues in PriorityAsStringQueues must use the _same_
-        # values for each name; if "High" is mapped to 10 in one queue and 100
-        # in another, we can't use names
-        my %values;
-        my $match = 1;
-        for my $q (keys %queues) {
-            for my $priority (keys %{$as_string{$q}}) {
-                $match = 0 if exists $values{$priority} and $as_string{$q}{$priority} != $values{$priority};
-                $values{$priority} = $as_string{$q}{$priority};
-            }
-        }
-        if ( $match ) {
-            # Swap out the /Elements/SelectPriority for /Elements/SelectPriorityAsString
-            my ($priority) = grep {$_->{Name} eq "Priority"} @lines;
-            $priority->{Value}{Path} = "/Elements/SelectPriorityAsString";
-            $priority->{Value}{Arguments}{Mapping} = \%values;
-        }
-    }
 }
 
 $m->callback( Conditions => \@lines );
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 9309da30c6..7f15512880 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -162,17 +162,6 @@ if ($ExcludeOwner) {
 # inflate the marker for custom roles into the field specs for each one
 @fields = map { ($_->{special}||'') eq 'roles' ? @role_fields : $_ } @fields;
 
-my %as_string = RT->Config->Get('PriorityAsStringQueues');
-
-if ( keys %as_string && $TicketObj && $as_string{$TicketObj->QueueObj->Name} ) {
-    # Swap out the /Elements/SelectPriority for /Elements/SelectPriorityAsString
-    for my $field (@fields) {
-        next unless ($field->{comp}||'') eq "/Elements/SelectPriority";
-        $field->{comp} = "/Elements/SelectPriorityAsString";
-        $field->{args}{Mapping} = $as_string{$TicketObj->QueueObj->Name};
-    }
-}
-
 $m->callback( CallbackName => 'MassageFields', %ARGS, TicketObj => $TicketObj, Fields => \@fields );
 
 # Process the field list, skipping if html is provided and running the
diff --git a/share/html/Ticket/Elements/ShowPriority b/share/html/Ticket/Elements/ShowPriority
index 8043e34f52..6e4ae1b964 100644
--- a/share/html/Ticket/Elements/ShowPriority
+++ b/share/html/Ticket/Elements/ShowPriority
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-% if (keys %queues and not $queues{$Ticket->QueueObj->Name}) {
+% if (!keys %config) {
 <% $Ticket->Priority %>/<% $Ticket->FinalPriority || ''%>
 % } else {
 % my $current = $Ticket->PriorityAsString || '';
@@ -57,7 +57,7 @@
 $Ticket => undef
 </%ARGS>
 <%INIT>
-my %queues = RT->Config->Get('PriorityAsStringQueues');
+my %config = RT->Config->Get('PriorityAsString');
 
 my $CSSClass = sub {
     my $value = shift;

commit 0cd0ff47879efeef642059b4090db841aa74b131
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Feb 27 05:16:51 2020 +0800

    Add $EnablePriorityAsString config to globally disable PriorityAsString
    
    Technically this could also be done by setting %PriorityString to empty,
    but this separate boolean config is more friendly to RT admins.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 1933c56f3c..063d4b98a9 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2709,6 +2709,20 @@ cut down on page clutter. Once this option is clicked the link will change to
 
 Set($HideOneTimeSuggestions, 0);
 
+=item C<$EnablePriorityAsString>
+
+Priority is stored as a number internally. This determines whether
+Priority is displayed to users as a number or using configured
+labels like Low, Medium, High. See L<%PriorityAsString> for details
+on this configuration.
+
+The default is enabled, so strings are shown. Set to C<0> to display
+numbers, which was the previous default for RT.
+
+=cut
+
+Set($EnablePriorityAsString, 1);
+
 =item C<%PriorityAsString>
 
 Specify a mapping between priority strings and the internal numeric
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index 9362e2a667..7164ecc928 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -1450,6 +1450,9 @@ our %META;
     EnableReminders => {
         Widget => '/Widgets/Form/Boolean',
     },
+    EnablePriorityAsString => {
+        Widget => '/Widgets/Form/Boolean',
+    },
     ExternalStorageDirectLink => {
         Widget => '/Widgets/Form/Boolean',
     },
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 7dbdbc4ae9..7e118eb385 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -3749,7 +3749,7 @@ sub FinalPriorityAsString {
 sub _PriorityAsString {
     my $self     = shift;
     my $priority = shift;
-    return undef unless defined $priority && length $priority;
+    return undef unless defined $priority && length $priority && RT->Config->Get('EnablePriorityAsString');
 
     my %map = RT->Config->Get('PriorityAsString');
 
diff --git a/share/html/Elements/RT__Ticket/ColumnMap b/share/html/Elements/RT__Ticket/ColumnMap
index d1dfb04619..2a4e3061f1 100644
--- a/share/html/Elements/RT__Ticket/ColumnMap
+++ b/share/html/Elements/RT__Ticket/ColumnMap
@@ -384,7 +384,7 @@ if(RT->Config->Get('DisplayTotalTimeWorked')) {
     }
 }
 
-{
+if ( RT->Config->Get('EnablePriorityAsString') ) {
     my $printer = sub {
         my ( $class, $string ) = @_;
         return '' unless defined $string && length $string;
diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriority
index 87b665c0f2..600b1eb38d 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriority
@@ -51,8 +51,10 @@ $Name => 'Priority'
 $Default => ''
 </%ARGS>
 <%INIT>
-my %config = RT->Config->Get('PriorityAsString');
-return $m->comp("/Elements/SelectPriorityAsString",%ARGS) if keys %config;
+if ( RT->Config->Get('EnablePriorityAsString') ) {
+    my %config = RT->Config->Get('PriorityAsString');
+    return $m->comp( "/Elements/SelectPriorityAsString", %ARGS ) unless keys %config;
+}
 
 $Default = '' unless defined $Default;
 </%INIT>
diff --git a/share/html/Ticket/Elements/ShowPriority b/share/html/Ticket/Elements/ShowPriority
index 6e4ae1b964..7abf7be7d1 100644
--- a/share/html/Ticket/Elements/ShowPriority
+++ b/share/html/Ticket/Elements/ShowPriority
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-% if (!keys %config) {
+% if (!RT->Config->Get('EnablePriorityAsString') || !keys %config) {
 <% $Ticket->Priority %>/<% $Ticket->FinalPriority || ''%>
 % } else {
 % my $current = $Ticket->PriorityAsString || '';

commit 85a9cc398f465541f0cfaee28636018d94e604be
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Feb 27 09:05:05 2020 +0800

    Refactor %PriorityAsString format to be more flexible and powerful
    
    The new format now covers full functionality of deleted
    @PriorityAsStringOrder and %PriorityAsStringQueues

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 063d4b98a9..35f2707e51 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2725,12 +2725,45 @@ Set($EnablePriorityAsString, 1);
 
 =item C<%PriorityAsString>
 
-Specify a mapping between priority strings and the internal numeric
-representation
+This setting allows you to define labels for priority values
+available on tickets. RT stores these values internally as a number,
+but this number will be hidden if C<$EnablePriorityAsString> is true.
+For the configuration, link the labels to numbers as shown below. If
+you have more or less priority settings, you can adjust the numbers,
+giving a unique number to each.
+
+    Set(%PriorityAsString,
+        Default => { Low => 0, Medium => 50, High => 100 },
+        General => [ Medium => 50, Low => 0, High => 80, 'On Fire' => 100],
+        Support => 0,
+    );
+
+The key is queue name or "Default", which is the fallback for unspecified
+queues. Values can be an ArrayRef, HashRef, or C<0>.
+
+=over
+
+=item ArrayRef
+
+This is the ordered String => Number map list. Pririty options will be
+rendered in the order they are listed in the list.
+
+=item HashRef
+
+This is the unordered String => Number map list. Priority options will be
+rendered in numerical ascending order.
+
+=item C<0>
+
+Priority is rendered as a number.
+
+=back
 
 =cut
 
-Set(%PriorityAsString, (Low => 0, Medium => 50, High => 100));
+Set(%PriorityAsString,
+    Default => { Low => 0, Medium => 50, High => 100 },
+);
 
 =back
 
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index 7164ecc928..0d648842d2 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -1381,6 +1381,47 @@ our %META;
             $self->Set( 'ExternalInfoPriority', \@values );
         },
     },
+    PriorityAsString => {
+        Type          => 'HASH',
+        PostLoadCheck => sub {
+            my $self = shift;
+            return unless $self->Get('EnablePriorityAsString');
+            my $config = $self->Get('PriorityAsString');
+
+            my %map;
+
+            for my $name ( keys %$config ) {
+                if ( my $value = $config->{$name} ) {
+                    my @list;
+                    if ( ref $value eq 'ARRAY' ) {
+                        @list = @$value;
+                    }
+                    elsif ( ref $value eq 'HASH' ) {
+                        @list = %$value;
+                    }
+                    else {
+                        RT->Logger->error("Invalid value for $name in PriorityAsString");
+                        undef $config->{$name};
+                    }
+
+                    while ( my $label = shift @list ) {
+                        my $value = shift @list;
+                        $map{$label} //= $value;
+
+                        if ( $map{$label} != $value ) {
+                            RT->Logger->debug("Priority $label is inconsistent: $map{$label} VS $value");
+                        }
+                    }
+
+                }
+            }
+
+            unless ( keys %map ) {
+                RT->Logger->debug("No valid PriorityAsString options");
+                $self->Set( 'EnablePriorityAsString', 0 );
+            }
+        },
+    },
     ServiceBusinessHours => {
         Type => 'HASH',
         PostLoadCheck   => sub {
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 7e118eb385..e76c62a561 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -3747,11 +3747,24 @@ sub FinalPriorityAsString {
 }
 
 sub _PriorityAsString {
-    my $self     = shift;
-    my $priority = shift;
+    my $self       = shift;
+    my $priority   = shift;
+    my $queue_name = shift || $self->QueueObj->__Value('Name');    # Skip ACL check
+
     return undef unless defined $priority && length $priority && RT->Config->Get('EnablePriorityAsString');
 
-    my %map = RT->Config->Get('PriorityAsString');
+    my %config = RT->Config->Get('PriorityAsString');
+    my $value = ( exists $config{$queue_name} ? $config{$queue_name} : $config{Default} ) or return undef;
+    my %map;
+    if ( ref $value eq 'ARRAY' ) {
+        %map = @$value;
+    }
+    elsif ( ref $value eq 'HASH' ) {
+        %map = %$value;
+    }
+    else {
+        RT->Logger->warning("Invalid PriorityAsString value: $value");
+    }
 
     # Count from high down to low until we find one that our number is
     # greater than or equal to.
diff --git a/share/html/Elements/RT__Ticket/ColumnMap b/share/html/Elements/RT__Ticket/ColumnMap
index 2a4e3061f1..13a5c3de00 100644
--- a/share/html/Elements/RT__Ticket/ColumnMap
+++ b/share/html/Elements/RT__Ticket/ColumnMap
@@ -160,21 +160,21 @@ $COLUMN_MAP = {
         title     => 'Priority', # loc
         attribute => 'Priority',
         value     => sub { return $_[0]->Priority },
-        edit      => sub { return \($m->scomp('/Elements/SelectPriority', Name => 'Priority', Default => $_[0]->Priority)) },
+        edit      => sub { return \($m->scomp('/Elements/SelectPriority', Name => 'Priority', Default => $_[0]->Priority, QueueObj => $_[0]->QueueObj )) },
     },
     InitialPriority => {
         title     => 'InitialPriority', # loc
         attribute => 'InitialPriority',
         name      => 'Initial Priority',
         value     => sub { return $_[0]->InitialPriority },
-        edit      => sub { return \($m->scomp('/Elements/SelectPriority', Name => 'InitialPriority', Default => $_[0]->InitialPriority)) },
+        edit      => sub { return \($m->scomp('/Elements/SelectPriority', Name => 'InitialPriority', Default => $_[0]->InitialPriority, QueueObj => $_[0]->QueueObj)) },
     },
     FinalPriority => {
         title     => 'FinalPriority', # loc
         attribute => 'FinalPriority',
         name      => 'Final Priority',
         value     => sub { return $_[0]->FinalPriority },
-        edit      => sub { return \($m->scomp('/Elements/SelectPriority', Name => 'FinalPriority', Default => $_[0]->FinalPriority)) },
+        edit      => sub { return \($m->scomp('/Elements/SelectPriority', Name => 'FinalPriority', Default => $_[0]->FinalPriority, QueueObj => $_[0]->QueueObj)) },
     },
     EffectiveId => {
         title     => 'EffectiveId', # loc
@@ -408,7 +408,8 @@ if ( RT->Config->Get('EnablePriorityAsString') ) {
         my $method = $field . 'AsString';
 
         $COLUMN_MAP->{$field}{'value'} = sub {
-            return $printer->( $class, $_[0]->$method() );
+            # Fallback to numbers when the queue disables PriorityAsString
+            return $printer->( $class, $_[0]->$method() ) || $_[0]->$field;
         };
     }
 }
diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriority
index 600b1eb38d..d637959bac 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriority
@@ -49,11 +49,75 @@
 <%ARGS>
 $Name => 'Priority'
 $Default => ''
+$QueueObj => undef
+%Queues => ()
 </%ARGS>
 <%INIT>
+use List::MoreUtils 'uniq';
 if ( RT->Config->Get('EnablePriorityAsString') ) {
     my %config = RT->Config->Get('PriorityAsString');
-    return $m->comp( "/Elements/SelectPriorityAsString", %ARGS ) unless keys %config;
+
+    my @names;
+    if ($QueueObj) {
+        push @names, $QueueObj->__Value('Name');    # Skip ACL check
+    }
+    elsif (%Queues) {
+        for my $id ( keys %Queues ) {
+            my $queue = RT::Queue->new( $session{'CurrentUser'} );
+            $queue->Load($id);
+            if ( $queue->Id ) {
+                push @names, $queue->__Value('Name');    # Skip ACL check
+            }
+        }
+    }
+    else {
+        @names = keys %config;
+    }
+
+    my $use_numeric;
+    my @values;
+    for my $name ( sort { lc $a cmp lc $b } uniq @names ) {
+        my $value = exists $config{$name} ? $config{$name} : $config{Default};
+        if ($value) {
+            push @values, $value;
+        }
+        else {
+            RT->Logger->debug("PriorityAsString for Queue $name is disabled, skipping");
+            $use_numeric = 1;
+            last;
+        }
+    }
+
+    my @options;
+    my %map;
+
+    # Items in @values are hashrefs/arrayrefs, we still can de-duplicate
+    # using uniq because duplicated ones are totally identical.
+    for my $value ( uniq @values ) {
+        my @list;
+        if ( ref $value eq 'ARRAY' ) {
+            @list = @$value;
+        }
+        elsif ( ref $value eq 'HASH' ) {
+            @list = map { $_ => $value->{$_} } sort { $value->{$a} <=> $value->{$b} } keys %$value;
+        }
+
+        while ( my $label = shift @list ) {
+            my $option = { Label => $label, Value => shift @list };
+            if ( defined $map{$label} ) {
+                if ( $map{$label} != $option->{Value} ) {
+                    $use_numeric = 1;
+                    last;
+                }
+            }
+            else {
+                $map{$label} = $option->{Value};
+                push @options, $option;
+            }
+        }
+    }
+
+    return $m->comp( "/Elements/SelectPriorityAsString", %ARGS, Options => \@options ) unless $use_numeric;
 }
 
 $Default = '' unless defined $Default;
diff --git a/share/html/Elements/SelectPriorityAsString b/share/html/Elements/SelectPriorityAsString
index 0ecfd44873..02cbdb318a 100644
--- a/share/html/Elements/SelectPriorityAsString
+++ b/share/html/Elements/SelectPriorityAsString
@@ -50,12 +50,12 @@
   <option value="">-</option>
 % }
 <%PERL>
-foreach my $label ( @order ) {
-    my ($value, $selected);
+for my $option ( @Options ) {
+    my $label = $option->{Label};
+    my $value = $option->{Value};
+    my $selected = '';
     if ( $label eq $default_label ) {
         ($value, $selected) = ($Default, 'selected="selected"');
-    } else {
-        ($value, $selected) = ($map{ $label }, '');
     }
 </%PERL>
   <option class="priority-<% lc $label %>" value="<% $value %>" <% $selected |n %>><% loc($label) %></option>
@@ -64,14 +64,12 @@ foreach my $label ( @order ) {
 <%ARGS>
 $Name => 'Priority'
 $Default => undef
+$QueueObj => undef
+ at Options
 </%ARGS>
 <%INIT>
-
-my %map   = RT->Config->Get('PriorityAsString');
-my @order = sort { $map{$a} <=> $map{$b} } keys %map;
-
 my $default_label = '';
-if ( defined $Default && length $Default ) {
-    $default_label = RT::Ticket->_PriorityAsString( $Default, \%map ) || '';
+if ( defined $Default && length $Default && $QueueObj ) {
+    $default_label = RT::Ticket->_PriorityAsString( $Default, $QueueObj->__Value('Name') ) || '';
 }
 </%INIT>
diff --git a/share/html/Search/Elements/PickBasics b/share/html/Search/Elements/PickBasics
index 124fc7d9b3..9392ef1053 100644
--- a/share/html/Search/Elements/PickBasics
+++ b/share/html/Search/Elements/PickBasics
@@ -385,6 +385,7 @@ else {
             Value => {
                 Type => 'component',
                 Path => '/Elements/SelectPriority',
+                Arguments => { Queues => \%queues },
             },
         },
         {
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index e11f9fb6b2..c422696398 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -249,7 +249,8 @@
   <div class="label col-md-3"><&|/l&>Priority</&>:</div>
   <div class="value col-md-9"><& /Elements/SelectPriority,
       Name => "InitialPriority",
-    Default => $ARGS{InitialPriority} ? $ARGS{InitialPriority} : $QueueObj->DefaultValue('InitialPriority'),
+      Default => $ARGS{InitialPriority} ? $ARGS{InitialPriority} : $QueueObj->DefaultValue('InitialPriority'),
+      QueueObj => $QueueObj,
   &></div>
 </div>
 
@@ -258,6 +259,7 @@
   <div class="value col-md-9"><& /Elements/SelectPriority,
     Name => "FinalPriority",
     Default => $ARGS{FinalPriority} ? $ARGS{FinalPriority} : $QueueObj->DefaultValue('FinalPriority'),
+    QueueObj => $QueueObj,
   &></div>
 </div>
 
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 7f15512880..89f3d4ac2c 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -129,6 +129,7 @@ unless ( @fields ) {
                     args => {
                         Name => $field,
                         Default => $defaults{$field} || $TicketObj->$field,
+                        QueueObj => $TicketObj->QueueObj,
                     }
                 }
             } ('Priority', 'Final Priority')
diff --git a/share/html/Ticket/Elements/ShowPriority b/share/html/Ticket/Elements/ShowPriority
index 7abf7be7d1..0967326bd0 100644
--- a/share/html/Ticket/Elements/ShowPriority
+++ b/share/html/Ticket/Elements/ShowPriority
@@ -45,7 +45,7 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-% if (!RT->Config->Get('EnablePriorityAsString') || !keys %config) {
+% if ($use_numeric) {
 <% $Ticket->Priority %>/<% $Ticket->FinalPriority || ''%>
 % } else {
 % my $current = $Ticket->PriorityAsString || '';
@@ -57,7 +57,14 @@
 $Ticket => undef
 </%ARGS>
 <%INIT>
-my %config = RT->Config->Get('PriorityAsString');
+my $use_numeric = 1;
+
+if ( RT->Config->Get('EnablePriorityAsString') ) {
+    my %config     = RT->Config->Get('PriorityAsString');
+    my $queue_name = $Ticket->QueueObj->__Value('Name');    # Skip ACL check
+
+    $use_numeric = 0 if exists $config{$queue_name} ? $config{$queue_name} : $config{Default};
+}
 
 my $CSSClass = sub {
     my $value = shift;

commit aea948b0801f5edb1b3bf8eaba10128bf7021918
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Mar 18 22:22:56 2020 +0800

    Display priority options per queue like status options in search builder
    
    For configs that contain inconsistent settings, e.g.
    
        General => { Low => 0, Medium => 50, High => 100 },
        Support => { Low => 20, Medium => 70, High => 200 },
    
    previously we had to render the priority input as a text. Now we can
    render it as a select without worrying about the confusion, as the
    options now are like:
    
        General
          Low
          Medium
          High
        Support
          Low
          Medium
          High

diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriority
index d637959bac..b76a8a996e 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriority
@@ -70,30 +70,30 @@ if ( RT->Config->Get('EnablePriorityAsString') ) {
             }
         }
     }
+
+    if ( @names ) {
+        @names = uniq map { exists $config{$_} ? $_ : 'Default' } @names;
+    }
     else {
         @names = keys %config;
     }
 
+    @names = sort { lc $a cmp lc $b } @names;
+
     my $use_numeric;
-    my @values;
-    for my $name ( sort { lc $a cmp lc $b } uniq @names ) {
-        my $value = exists $config{$name} ? $config{$name} : $config{Default};
-        if ($value) {
-            push @values, $value;
-        }
-        else {
+    for my $name (@names) {
+        if ( !$config{$name} ) {
             RT->Logger->debug("PriorityAsString for Queue $name is disabled, skipping");
             $use_numeric = 1;
             last;
         }
     }
 
-    my @options;
-    my %map;
+    my %options;
+
+    for my $name ( @names ) {
+        my $value = $config{$name};
 
-    # Items in @values are hashrefs/arrayrefs, we still can de-duplicate
-    # using uniq because duplicated ones are totally identical.
-    for my $value ( uniq @values ) {
         my @list;
         if ( ref $value eq 'ARRAY' ) {
             @list = @$value;
@@ -104,20 +104,11 @@ if ( RT->Config->Get('EnablePriorityAsString') ) {
 
         while ( my $label = shift @list ) {
             my $option = { Label => $label, Value => shift @list };
-            if ( defined $map{$label} ) {
-                if ( $map{$label} != $option->{Value} ) {
-                    $use_numeric = 1;
-                    last;
-                }
-            }
-            else {
-                $map{$label} = $option->{Value};
-                push @options, $option;
-            }
+            push @{ $options{$name} }, $option;
         }
     }
 
-    return $m->comp( "/Elements/SelectPriorityAsString", %ARGS, Options => \@options ) unless $use_numeric;
+    return $m->comp( "/Elements/SelectPriorityAsString", %ARGS, Options => \%options ) unless $use_numeric;
 }
 
 $Default = '' unless defined $Default;
diff --git a/share/html/Elements/SelectPriorityAsString b/share/html/Elements/SelectPriorityAsString
index 02cbdb318a..cd9b7a8164 100644
--- a/share/html/Elements/SelectPriorityAsString
+++ b/share/html/Elements/SelectPriorityAsString
@@ -49,27 +49,38 @@
 % unless ( defined $Default ) {
   <option value="">-</option>
 % }
+% for my $name ( sort { lc $a cmp lc $b } keys %Options ) {
+%   if ( $group_by_name ) {
+  <optgroup label="<% $name %>">
+%   }
 <%PERL>
-for my $option ( @Options ) {
-    my $label = $option->{Label};
-    my $value = $option->{Value};
-    my $selected = '';
-    if ( $label eq $default_label ) {
-        ($value, $selected) = ($Default, 'selected="selected"');
-    }
+    for my $option ( @{$Options{$name}} ) {
+        my $label = $option->{Label};
+        my $value = $option->{Value};
+        my $selected = '';
+        if ( $label eq $default_label ) {
+            ($value, $selected) = ($Default, 'selected="selected"');
+        }
 </%PERL>
-  <option class="priority-<% lc $label %>" value="<% $value %>" <% $selected |n %>><% loc($label) %></option>
+    <option class="priority-<% lc $label %>" value="<% $value %>" <% $selected |n %>><% loc($label) %></option>
+%   }
+%   if ( $group_by_name ) {
+  </optgroup>
+%   }
 % }
 </select>
 <%ARGS>
 $Name => 'Priority'
 $Default => undef
 $QueueObj => undef
- at Options
+%Options
 </%ARGS>
 <%INIT>
 my $default_label = '';
 if ( defined $Default && length $Default && $QueueObj ) {
     $default_label = RT::Ticket->_PriorityAsString( $Default, $QueueObj->__Value('Name') ) || '';
 }
+
+my %config = RT->Config->Get('PriorityAsString');
+my $group_by_name = keys %Options > 1;
 </%INIT>

commit 90add74160f12e03bdc57d1ec3c2691f97a3ff7c
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Fri Feb 28 05:05:35 2020 +0800

    Show string version of priorities to end users when possible
    
    This is to obey the rule of least surprise. If PriorityAsString is
    enabled, we should show priorities as string consistently.
    
    E.g. when user updated Priority from "Low" to "Medium", the successful
    message and corresponding transaction should show:
    
        Priority changed from 'Low' to 'Medium'
    
    instead of:
    
        Priority changed from '0' to '50'
    
    Another example: in search builder, when user selected "Priority" "less
    than" "High", we should add
    
        Priority < 'High'
    
    instead of:
    
        Priority < 100
    
    But when configs conflict somehow like:
    
        General => { Low => 0, Medium => 50, High => 100 },
        Support => { Low => 20, Medium => 70, High => 200 },
    
    Since there is no one-to-one map of String => Number, to avoid
    confusion, we have to show priorities as numbers(e.g. in search builder
    when invovled queues conflict). I believe it's a rare case.

diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
index 14d66885f7..2349383e83 100644
--- a/lib/RT/Queue.pm
+++ b/lib/RT/Queue.pm
@@ -1231,6 +1231,17 @@ sub SetDefaultValue {
         },
     );
 
+    if ( $args{Name} =~ /Priority/ && RT->Config->Get('EnablePriorityAsString') ) {
+        if ( $old_value ne $self->loc('(no value)') ) {
+            my $str = RT::Ticket->_PriorityAsString( $old_value, $self->Name );
+            $old_value = $self->loc($str) if $str;
+        }
+        if ( $new_value ne $self->loc('(no value)') ) {
+            my $str = RT::Ticket->_PriorityAsString( $new_value, $self->Name );
+            $new_value = $self->loc($str) if $str;
+        }
+    }
+
     if ( $ret ) {
         return ( $ret, $self->loc( 'Default value of [_1] changed from [_2] to [_3]', $args{Name}, $old_value, $new_value ) );
     }
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index e094c63ef6..383564e42a 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -3133,6 +3133,45 @@ sub _parser {
         }
     );
 
+    if ( RT->Config->Get('EnablePriorityAsString') ) {
+        my $queues = $tree->GetReferencedQueues;
+        my %config = RT->Config->Get('PriorityAsString');
+        my @names;
+        if (%$queues) {
+            for my $id ( keys %$queues ) {
+                my $queue = RT::Queue->new( $self->CurrentUser );
+                $queue->Load($id);
+                if ( $queue->Id ) {
+                    push @names, $queue->__Value('Name');    # Skip ACL check
+                }
+            }
+        }
+        else {
+            @names = keys %config;
+        }
+
+        my %map;
+        for my $name (@names) {
+            if ( my $value = exists $config{$name} ? $config{$name} : $config{Default} ) {
+                my %hash = ref $value eq 'ARRAY' ? @$value : %$value;
+                for my $label ( keys %hash ) {
+                    $map{lc $label} //= $hash{$label};
+                }
+            }
+        }
+
+        $tree->traverse(
+            sub {
+                my $node = shift;
+                return unless $node->isLeaf;
+                my $value = $node->getNodeValue;
+                if ( $value->{Key} =~ /^(?:Initial|Final)?Priority$/i ) {
+                    $value->{Value} = $map{ lc $value->{Value} } if defined $map{ lc $value->{Value} };
+                }
+            }
+        );
+    }
+
     # Perform an optimization pass looking for watcher bundling
     $tree->traverse(
         sub {
diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
index 914fe51caf..a4b007de47 100644
--- a/lib/RT/Transaction.pm
+++ b/lib/RT/Transaction.pm
@@ -1261,6 +1261,20 @@ sub _CanonicalizeRoleName {
                 }
             }
         }
+        elsif ( $self->Field =~ /Priority/ && RT->Config->Get('EnablePriorityAsString') ) {
+            my $object = $self->Object;
+            my ( $old_value, $new_value );
+            if ( $object->isa('RT::Ticket') ) {
+                $old_value = $object->_PriorityAsString( $self->OldValue );
+                $new_value = $object->_PriorityAsString( $self->NewValue );
+                $old_value = $self->loc($old_value) if $old_value;
+                $new_value = $self->loc($new_value) if $new_value;
+            }
+            $old_value //= $self->OldValue;
+            $new_value //= $self->NewValue;
+
+            return ( "[_1] changed from [_2] to [_3]", $self->loc( $self->Field ), "'$old_value'", "'$new_value'" );    #loc()
+        }
         else {
             return ( "[_1] changed from [_2] to [_3]",
                     $self->loc($self->Field),
diff --git a/lib/RT/Transactions.pm b/lib/RT/Transactions.pm
index 948037d123..2b71ade9fb 100644
--- a/lib/RT/Transactions.pm
+++ b/lib/RT/Transactions.pm
@@ -1001,6 +1001,45 @@ sub _parser {
         }
     );
 
+    if ( RT->Config->Get('EnablePriorityAsString') ) {
+        my $queues = $tree->GetReferencedQueues;
+        my %config = RT->Config->Get('PriorityAsString');
+        my @names;
+        if (%$queues) {
+            for my $id ( keys %$queues ) {
+                my $queue = RT::Queue->new( $self->CurrentUser );
+                $queue->Load($id);
+                if ( $queue->Id ) {
+                    push @names, $queue->__Value('Name');    # Skip ACL check
+                }
+            }
+        }
+        else {
+            @names = keys %config;
+        }
+
+        my %map;
+        for my $name (@names) {
+            if ( my $value = exists $config{$name} ? $config{$name} : $config{Default} ) {
+                my %hash = ref $value eq 'ARRAY' ? @$value : %$value;
+                for my $label ( keys %hash ) {
+                    $map{lc $label} //= $hash{$label};
+                }
+            }
+        }
+
+        $tree->traverse(
+            sub {
+                my $node = shift;
+                return unless $node->isLeaf;
+                my $value = $node->getNodeValue;
+                if ( $value->{Key} =~ /^Ticket(?:Initial|Final)?Priority$/i ) {
+                    $value->{Value} = $map{ lc $value->{Value} } if defined $map{ lc $value->{Value} };
+                }
+            }
+        );
+    }
+
     my $ea = '';
     $tree->traverse(
         sub {
diff --git a/share/html/Admin/Queues/DefaultValues.html b/share/html/Admin/Queues/DefaultValues.html
index a060a1bb3f..aa540ab0ae 100644
--- a/share/html/Admin/Queues/DefaultValues.html
+++ b/share/html/Admin/Queues/DefaultValues.html
@@ -59,7 +59,7 @@
       <&|/l&>Priority</&>:
     </div>
     <div class="col-md-9 value">
-      <& /Elements/SelectPriority, Name => "InitialPriority", Default => $queue->DefaultValue('InitialPriority') &>
+      <& /Elements/SelectPriority, Name => "InitialPriority", Default => $queue->DefaultValue('InitialPriority'), QueueObj => $queue &>
     </div>
   </div>
 
@@ -68,7 +68,7 @@
       <&|/l&>Final Priority</&>:
     </div>
     <div class="col-md-9 value">
-      <& /Elements/SelectPriority, Name => "FinalPriority", Default => $queue->DefaultValue('FinalPriority') &>
+      <& /Elements/SelectPriority, Name => "FinalPriority", Default => $queue->DefaultValue('FinalPriority'), QueueObj => $queue &>
       <span><em><&|/l&>requires running rt-crontool</&></em></span>
     </div>
   </div>
diff --git a/share/html/Elements/SelectPriority b/share/html/Elements/SelectPriority
index b76a8a996e..4ff386142d 100644
--- a/share/html/Elements/SelectPriority
+++ b/share/html/Elements/SelectPriority
@@ -51,6 +51,7 @@ $Name => 'Priority'
 $Default => ''
 $QueueObj => undef
 %Queues => ()
+$ValueAsString => undef
 </%ARGS>
 <%INIT>
 use List::MoreUtils 'uniq';
@@ -89,6 +90,7 @@ if ( RT->Config->Get('EnablePriorityAsString') ) {
         }
     }
 
+    my %map;
     my %options;
 
     for my $name ( @names ) {
@@ -105,6 +107,19 @@ if ( RT->Config->Get('EnablePriorityAsString') ) {
         while ( my $label = shift @list ) {
             my $option = { Label => $label, Value => shift @list };
             push @{ $options{$name} }, $option;
+
+            $map{$label} //= $option->{Value};
+            if ( $ValueAsString && $map{$label} != $option->{Value} ) {
+                $ValueAsString = 0;
+            }
+        }
+    }
+
+    if ($ValueAsString) {
+        for my $name ( keys %options ) {
+            for my $option ( @{ $options{$name} } ) {
+                $option->{Value} = $option->{Label};
+            }
         }
     }
 
diff --git a/share/html/Search/Bulk.html b/share/html/Search/Bulk.html
index 3893341819..884d8a328e 100644
--- a/share/html/Search/Bulk.html
+++ b/share/html/Search/Bulk.html
@@ -209,7 +209,7 @@
           <&|/l&>Make priority</&>:
         </div>
         <div class="col-md-9 value">
-          <& /Elements/SelectPriority, Name => "Priority", Default => $ARGS{Priority} &>
+          <& /Elements/SelectPriority, Name => "Priority", Default => $ARGS{Priority}, Queues => $seen_queues &>
         </div>
       </div>
 
diff --git a/share/html/Search/Elements/PickBasics b/share/html/Search/Elements/PickBasics
index 9392ef1053..e9d43aca52 100644
--- a/share/html/Search/Elements/PickBasics
+++ b/share/html/Search/Elements/PickBasics
@@ -385,7 +385,7 @@ else {
             Value => {
                 Type => 'component',
                 Path => '/Elements/SelectPriority',
-                Arguments => { Queues => \%queues },
+                Arguments => { Queues => \%queues, ValueAsString => 1 },
             },
         },
         {
diff --git a/share/html/Search/Elements/PickTickets b/share/html/Search/Elements/PickTickets
index 2ecdaddf04..b0fe2f6de4 100644
--- a/share/html/Search/Elements/PickTickets
+++ b/share/html/Search/Elements/PickTickets
@@ -181,6 +181,7 @@ my @lines = (
         Value => {
             Type => 'component',
             Path => '/Elements/SelectPriority',
+            Arguments => { Queues => \%queues, ValueAsString => 1 },
         },
     },
 );

commit c70858329c1104674bce04d6e1a7fc8c685c15f1
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Fri Feb 28 05:33:34 2020 +0800

    Update tests as PriorityAsString is enabled by default

diff --git a/t/web/download_user_info.t b/t/web/download_user_info.t
index 44155d49a7..06ade8e624 100644
--- a/t/web/download_user_info.t
+++ b/t/web/download_user_info.t
@@ -76,7 +76,7 @@ EOF
 
     my $ticket_info_tsv = <<EOF;
 id\tSubject\tStatus\tQueueName\tOwner\tPriority\tRequestors
-1\tTest\topen\tGeneral\tNobody in particular\t0\troot (Enoch Root)
+1\tTest\topen\tGeneral\tNobody in particular\tLow\troot (Enoch Root)
 EOF
 
     is $agent->content, $ticket_info_tsv, "User tickets downloaded correctly";
diff --git a/t/web/mobile.t b/t/web/mobile.t
index 3f32e49e66..831bf6652a 100644
--- a/t/web/mobile.t
+++ b/t/web/mobile.t
@@ -82,7 +82,7 @@ $m->content_contains( 'ticket1', 'subject' );
 $m->content_contains( 'open', 'status' );
 $m->content_contains( 'cc at example.com', 'cc' );
 $m->content_contains( 'admincc at example.com', 'admincc' );
-$m->content_contains( '13/93', 'priority' );
+$m->text_contains( 'Low/Medium', 'priority' );
 $m->content_contains( '2 hour', 'time estimates' );
 $m->content_contains( '30 min', 'time worked' );
 $m->content_contains( '60 min', 'time left' );

commit 0bd0ec533662560950c0ee97b171ebf59b6d99e5
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Feb 29 04:02:31 2020 +0800

    Test the PriorityAsString feature

diff --git a/t/ticket/priority.t b/t/ticket/priority.t
new file mode 100644
index 0000000000..ee76e46cb9
--- /dev/null
+++ b/t/ticket/priority.t
@@ -0,0 +1,105 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+
+my $ticket = RT::Test->create_ticket( Queue => $queue->Id, );
+
+diag "Default PriorityAsString";
+
+for my $field (qw/Priority InitialPriority FinalPriority/) {
+    is( $ticket->$field, 0, "$field is 0" );
+    my $string_method = $field . 'AsString';
+    is( $ticket->$string_method, 'Low', "$string_method is Low" );
+}
+
+diag "Disable PriorityAsString";
+
+RT->Config->Set( 'EnablePriorityAsString', 0 );
+for my $field (qw/Priority InitialPriority FinalPriority/) {
+    my $string_method = $field . 'AsString';
+    is( $ticket->$string_method, undef, "$string_method is undef" );
+}
+
+diag "Disable PriorityAsString at queue level";
+
+RT->Config->Set( 'EnablePriorityAsString', 1 );
+RT->Config->Set( 'PriorityAsString', General => 0 );
+for my $field (qw/Priority InitialPriority FinalPriority/) {
+    my $string_method = $field . 'AsString';
+    is( $ticket->$string_method, undef, "$string_method is undef" );
+}
+
+diag "Specific PriorityAsString config at queue level";
+
+RT->Config->Set(
+    'PriorityAsString',
+    Default => { Low     => 0, Medium => 50, High   => 100 },
+    General => { VeryLow => 0, Low    => 20, Medium => 50, High => 100, VeryHigh => 200 },
+);
+for my $field (qw/Priority InitialPriority FinalPriority/) {
+    my $string_method = $field . 'AsString';
+    is( $ticket->$string_method, 'VeryLow', "$string_method is updated" );
+}
+
+diag "Update Priorities";
+
+my ( $ret, $msg ) = $ticket->SetPriority(50);
+ok( $ret, "Priority is updated" );
+is( $msg, "Priority changed from 'VeryLow' to 'Medium'", 'Priority updated message' );
+
+( $ret, $msg ) = $ticket->SetFinalPriority(100);
+ok( $ret, "FinalPriority is updated" );
+is( $msg, "FinalPriority changed from 'VeryLow' to 'High'", 'FinalPriority updated message' );
+
+diag "Queue default priorities";
+
+( $ret, $msg ) = $queue->SetDefaultValue( Name => 'InitialPriority', Value => 20 );
+ok( $ret, "InitialPriority defaulted to Low" );
+is( $msg, 'Default value of InitialPriority changed from (no value) to Low', "InitialPriority updated message" );
+
+( $ret, $msg ) = $queue->SetDefaultValue( Name => 'FinalPriority', Value => 100 );
+ok( $ret, "FinalPriority defaulted to High" );
+is( $msg, 'Default value of FinalPriority changed from (no value) to High', "FinalPriority updated message" );
+
+$ticket = RT::Test->create_ticket( Queue => $queue->Id, );
+is( $ticket->PriorityAsString,        'Low',  'PriorityAsString is correct' );
+is( $ticket->InitialPriorityAsString, 'Low',  'InitialPriorityAsString is correct' );
+is( $ticket->FinalPriorityAsString,   'High', 'FinalPriorityAsString is correct' );
+
+diag "Explicitly set priorities on create";
+
+$ticket = RT::Test->create_ticket( Queue => $queue->Id, InitialPriority => '50', FinalPriority => 200 );
+is( $ticket->PriorityAsString,        'Medium',   'PriorityAsString is correct' );
+is( $ticket->InitialPriorityAsString, 'Medium',   'InitialPriorityAsString is correct' );
+is( $ticket->FinalPriorityAsString,   'VeryHigh', 'FinalPriorityAsString is correct' );
+
+diag "Ticket/Transaction search";
+
+for my $field (qw/Priority InitialPriority FinalPriority/) {
+    my $tickets = RT::Tickets->new( RT->SystemUser );
+    $tickets->FromSQL("Queue = 'General' AND $field = 'Low'");
+    like( $tickets->BuildSelectQuery, qr/$field = '20'/, "$field is translated properly" );
+
+    my $txns = RT::Transactions->new( RT->SystemUser );
+    $txns->FromSQL("TicketQueue = 'General' AND Ticket$field = 'Low'");
+    like( $txns->BuildSelectQuery, qr/$field = '20'/, "Ticket$field is translated properly" );
+}
+
+my $tickets = RT::Tickets->new( RT->SystemUser );
+$tickets->FromSQL("Queue = 'General' AND Priority = 'Medium'");
+is( $tickets->Count, 2, 'Found 2 tickets' );
+while ( my $ticket = $tickets->Next ) {
+    is( $ticket->PriorityAsString, 'Medium', 'Priority is correct' );
+}
+
+my $txns = RT::Transactions->new( RT->SystemUser );
+$txns->FromSQL("TicketQueue = 'General' AND TicketPriority = 'Medium' AND Field = 'Priority'");
+is( $txns->Count, 1, 'Found 1 txn' );
+my $txn = $txns->First;
+is( $txn->OldValue, 0,  'OldValue is correct' );
+is( $txn->NewValue, 50, 'NewValue is correct' );
+
+done_testing;
diff --git a/t/web/priority.t b/t/web/priority.t
new file mode 100644
index 0000000000..5989e09efe
--- /dev/null
+++ b/t/web/priority.t
@@ -0,0 +1,195 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok( $m->login, 'Log in' );
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+
+diag "Default PriorityAsString";
+
+$m->goto_create_ticket( $queue->Id );
+my $form = $m->form_name('TicketCreate');
+
+for my $field (qw/InitialPriority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'option', "$field input is a select" );
+    is_deeply( [ $priority_input->possible_values ], [ '', 0, 50, 100 ], "$field options" );
+    is( $form->value($field), '', "$field default value" );
+}
+
+$m->submit_form_ok( { fields => { Subject => 'Test PriorityAsString', InitialPriority => 50 } }, 'Create ticket' );
+$m->text_like( qr{Priority:\s*Medium/Low}, 'Priority/FinalPriority on display' );
+
+$m->follow_link_ok( { text => 'Basics' } );
+$form = $m->form_name('TicketModify');
+
+for my $field (qw/Priority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'option', "$field input is a select" );
+    is_deeply( [ $priority_input->possible_values ], [ 0, 50, 100 ], "$field options" );
+    is( $form->value($field), $field eq 'Priority' ? 50 : 0, "$field default value" );
+}
+
+$m->submit_form_ok( { fields => { Priority => 100, FinalPriority => 100 } }, 'Update Priority' );
+$m->text_contains( qq{Priority changed from 'Medium' to 'High'},   'Priority is updated' );
+$m->text_contains( qq{FinalPriority changed from 'Low' to 'High'}, 'FinalPriority is updated' );
+
+diag "Disable PriorityAsString";
+
+my $config = RT::Configuration->new( RT->SystemUser );
+my ( $ret, $msg ) = $config->Create( Name => 'EnablePriorityAsString', Content => 0 );
+ok( $ret, 'Updated config' );
+
+$m->goto_create_ticket( $queue->Id );
+$form = $m->form_name('TicketCreate');
+for my $field (qw/InitialPriority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'text', "$field input is a text" );
+    is( $form->value($field),  '',     "$field default value" );
+}
+
+$config->Load('EnablePriorityAsString');
+( $ret, $msg ) = $config->SetContent(1);
+ok( $ret, 'Updated config' );
+
+diag "Set PriorityAsString config of General to a hashref";
+
+# config cache refreshes at most once in one second, so wait a bit.
+sleep 1;
+
+$config = RT::Configuration->new( RT->SystemUser );
+( $ret, $msg ) = $config->Create(
+    Name    => 'PriorityAsString',
+    Content => {
+        Default => { Low     => 0, Medium => 50, High   => 100 },
+        General => { VeryLow => 0, Low    => 20, Medium => 50, High => 100, VeryHigh => 200 },
+    },
+);
+ok( $ret, 'Updated config' );
+
+$m->goto_create_ticket( $queue->Id );
+$form = $m->form_name('TicketCreate');
+for my $field (qw/InitialPriority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'option', "$field input is a select" );
+    is_deeply( [ $priority_input->possible_values ], [ '', 0, 20, 50, 100, 200 ], "$field options" );
+    is( $form->value($field), '', "$field default value" );
+}
+
+diag "Disable PriorityAsString for General";
+
+sleep 1;
+( $ret, $msg ) = $config->SetContent(
+    {   Default => { Low => 0, Medium => 50, High => 100 },
+        General => 0,
+    }
+);
+ok( $ret, 'Updated config' );
+$m->goto_create_ticket( $queue->Id );
+$form = $m->form_name('TicketCreate');
+for my $field (qw/InitialPriority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'text', "$field input is a text" );
+    is( $form->value($field),  '',     "$field default value" );
+}
+
+diag "Set PriorityAsString config of General to an arrayref";
+
+sleep 1;
+( $ret, $msg ) = $config->SetContent(
+    {   Default => { Low    => 0,  Medium  => 50, High => 100 },
+        General => [ Medium => 50, VeryLow => 0,  Low  => 20, High => 100, VeryHigh => 200 ],
+    }
+);
+ok( $ret, 'Updated config' );
+
+$m->goto_create_ticket( $queue->Id );
+$form = $m->form_name('TicketCreate');
+for my $field (qw/InitialPriority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'option', "$field input is a select" );
+    is_deeply( [ $priority_input->possible_values ], [ '', 50, 0, 20, 100, 200 ], "$field options" );
+    is( $form->value($field), '', "$field default value" );
+}
+
+diag "Queue default values";
+
+$m->get_ok('/Admin/Queues/DefaultValues.html?id=1');
+$form = $m->form_name('ModifyDefaultValues');
+for my $field (qw/InitialPriority FinalPriority/) {
+    my $priority_input = $form->find_input($field);
+    is( $priority_input->type, 'option', 'Priority input is a select' );
+    is_deeply( [ $priority_input->possible_values ], [ '', 50, 0, 20, 100, 200 ], 'Priority options' );
+}
+$m->submit_form_ok( { fields => { InitialPriority => 50, FinalPriority => 100 }, button => 'Update' },
+    'Update default values' );
+$m->text_contains( 'Default value of InitialPriority changed from (no value) to Medium', 'InitialPriority is updated' );
+$m->text_contains( 'Default value of FinalPriority changed from (no value) to High',     'FinalPriority is updated' );
+
+$m->goto_create_ticket( $queue->Id );
+$form = $m->form_name('TicketCreate');
+is( $form->value('InitialPriority'), 50,  'InitialPriority default value' );
+is( $form->value('FinalPriority'),   100, 'FinalPriority default value' );
+
+diag "Search builder";
+
+$m->follow_link_ok( { text => 'Tickets' }, 'Ticket search builder' );
+$form = $m->form_name('BuildQuery');
+my $priority_input = $form->find_input('ValueOfPriority');
+is( $priority_input->type, 'option', 'ValueOfPriority input is a select' );
+is_deeply(
+    [ $priority_input->possible_values ],
+    [ '', 0, 50, 100, 50, 0, 20, 100, 200 ],
+    'ValueOfPriority option values are numbers'
+);
+
+$m->submit_form_ok( { fields => { ValueOfQueue => 'General' }, button => 'AddClause' }, 'Limit queue' );
+$form           = $m->form_name('BuildQuery');
+$priority_input = $form->find_input('ValueOfPriority');
+is( $priority_input->type, 'option', 'ValueOfPriority input is a select' );
+is_deeply(
+    [ $priority_input->possible_values ],
+    [ '', 'Medium', 'VeryLow', 'Low', 'High', 'VeryHigh' ],
+    'ValueOfTicketPriority option values are strings'
+);
+
+$m->submit_form_ok( { fields => { PriorityOp => '=', ValueOfPriority => 'High' }, button => 'DoSearch' },
+    'Limit priority' );
+$m->title_is('Found 1 ticket');
+$m->text_contains('Test PriorityAsString');
+
+$m->follow_link_ok( { text => 'Advanced' } );
+$m->submit_form_ok(
+    { form_name => 'BuildQueryAdvanced', fields => { Query => qq{Queue = 'General' AND Priority = 'Low'} } },
+    'Search tickets with LowPriority' );
+$m->submit_form_ok( { form_name => 'BuildQuery', button => 'DoSearch' } );
+$m->title_is('Found 0 tickets');
+
+$m->follow_link_ok( { text => 'Transactions' }, 'Transaction search builder' );
+$form           = $m->form_name('BuildQuery');
+$priority_input = $form->find_input('ValueOfTicketPriority');
+is_deeply(
+    [ $priority_input->possible_values ],
+    [ '', 0, 50, 100, 50, 0, 20, 100, 200 ],
+    'ValueOfPriority option values are numbers'
+);
+
+$m->submit_form_ok( { fields => { ValueOfTicketQueue => 'General' }, button => 'AddClause' }, 'Limit queue' );
+$form           = $m->form_name('BuildQuery');
+$priority_input = $form->find_input('ValueOfTicketPriority');
+is( $priority_input->type, 'option', 'ValueOfTicketPriority input is a select' );
+is_deeply(
+    [ $priority_input->possible_values ],
+    [ '', 'Medium', 'VeryLow', 'Low', 'High', 'VeryHigh' ],
+    'ValueOfTicketPriority option values are strings'
+);
+
+$m->submit_form_ok( { fields => { TicketPriorityOp => '=', ValueOfTicketPriority => 'High' }, button => 'DoSearch' },
+    'Limit priority' );
+$m->title_is('Found 4 transactions');
+$m->text_contains('Test PriorityAsString');
+
+done_testing;

commit 8f4722c85ce6f347c1e81dcec03b17d365ab7840
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Mar 18 14:45:24 2020 +0800

    Hide the confusing FinalPriority from end users
    
    FinalPriority is for priority escalation, which is not useful to end
    users. Besides, FinalPriority was shown along with Priority like
    "Low/Medium", which is quite confusing.

diff --git a/share/html/Search/Elements/ConditionRow b/share/html/Search/Elements/ConditionRow
index 7d5e785bab..101f6293c8 100644
--- a/share/html/Search/Elements/ConditionRow
+++ b/share/html/Search/Elements/ConditionRow
@@ -93,7 +93,8 @@ $handle_block = sub {
         my @options = @{ $box->{'Options'} };
         while( defined( my $k = shift @options ) ) {
             my $v = shift @options;
-            $res .= qq{<option value="$k">$v</option>};
+            my $class = CSSClass($k);
+            $res .= qq{<option class="$class" value="$k">$v</option>};
         }
         $res .= qq{</select>};
         return $res;
diff --git a/share/html/Search/Elements/EditFormat b/share/html/Search/Elements/EditFormat
index b145e8def3..e3d8722057 100644
--- a/share/html/Search/Elements/EditFormat
+++ b/share/html/Search/Elements/EditFormat
@@ -89,7 +89,7 @@ jQuery( function() {
           <select name="SelectDisplayColumns" multiple="multiple" class="form-control selectpicker">
 % my %seen;
 % foreach my $field ( grep !$seen{lc $_}++, @$AvailableColumns) {
-          <option value="<% $field %>" <% $selected{$field} ? 'selected="selected"' : '' |n%>>\
+          <option class="<% CSSClass($field) %>" value="<% $field %>" <% $selected{$field} ? 'selected="selected"' : '' |n%>>\
             <% $field =~ /^(?:CustomField|CF)\./ ? $field : loc($field) %></option>
 % }
           </select>
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index c422696398..30213e8d45 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -254,7 +254,7 @@
   &></div>
 </div>
 
-<div class="form-row">
+<div class="form-row FinalPriority">
   <div class="label col-md-3"><&|/l&>Final Priority</&>:</div>
   <div class="value col-md-9"><& /Elements/SelectPriority,
     Name => "FinalPriority",
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 89f3d4ac2c..9204e999d3 100644
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -179,7 +179,8 @@ for my $field (@fields) {
 <div>
 % }
 % for my $field (@fields) {
-  <div class="form-row">
+%# Prefer input name as css class, e.g. "FinalPriority" instead of "Final_Priority"
+  <div class="form-row <% CSSClass( $field->{args}{Name} || $field->{'name'} ) %>">
     <div class="label col-md-3"><% loc($field->{'name'}) %>:</div>
     <div class="value col-md-9"><% $field->{'html'} |n %></div>
   </div>
diff --git a/share/html/Ticket/Elements/ShowPriority b/share/html/Ticket/Elements/ShowPriority
index 0967326bd0..67f5de1622 100644
--- a/share/html/Ticket/Elements/ShowPriority
+++ b/share/html/Ticket/Elements/ShowPriority
@@ -46,12 +46,12 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 % if ($use_numeric) {
-<% $Ticket->Priority %>/<% $Ticket->FinalPriority || ''%>
+<span class="Priority"><% $Ticket->Priority %></span>/<span class="FinalPriority"><% $Ticket->FinalPriority || ''%></span>
 % } else {
 % my $current = $Ticket->PriorityAsString || '';
 % my $final = $Ticket->FinalPriorityAsString || '';
-<span class="ticket-info-priority-<% $CSSClass->(lc($current)) %>"><% loc($current) %></span>/\
-<span class="ticket-info-final-priority-<% $CSSClass->(lc($final)) %>"><% loc($final) %></span>
+<span class="Priority ticket-info-priority-<% $CSSClass->(lc($current)) %>"><% loc($current) %></span>/\
+<span class="FinalPriority ticket-info-final-priority-<% $CSSClass->(lc($final)) %>"><% loc($final) %></span>
 % }
 <%ARGS>
 $Ticket => undef
diff --git a/share/static/css/elevator-light/ticket.css b/share/static/css/elevator-light/ticket.css
index fbc1456d55..1aeacc09ea 100644
--- a/share/static/css/elevator-light/ticket.css
+++ b/share/static/css/elevator-light/ticket.css
@@ -152,3 +152,17 @@ ul.select-queue li a:visited {
 #create-linked-ticket {
     padding-top: 15px;
 }
+
+.priority div.value .current-value {
+    visibility: hidden;
+}
+
+.priority div.value .current-value span.Priority {
+    visibility: visible;
+}
+
+/* .TicketFinalPriority is used in ticket transaction search */
+.FinalPriority,
+.TicketFinalPriority {
+    display: none;
+}
diff --git a/share/static/css/mobile.css b/share/static/css/mobile.css
index 92104249e5..831e67186f 100644
--- a/share/static/css/mobile.css
+++ b/share/static/css/mobile.css
@@ -457,3 +457,11 @@ div.error .titlebox-title {
 div.error div.error {
     background-color: #fcc;
 }
+
+.priority.value  {
+    visibility: hidden;
+}
+
+.priority.value span.Priority {
+    visibility: visible;
+}

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


More information about the rt-commit mailing list