Option to sort custom fields by the array order in a grouping

Hi all,

We often have quite a few of our custom fields in Tickets put into the Custom Field Groupings in the RT_SiteConfig. One thing that has bugged me occasionally is that the CFs in a grouping are in an ordered array in the config, but the array ordering isn’t reflected when displaying the CFs. Instead the ordering is defined either per queue or by CF creation time. Last week I ended up spending an hour or so making a custom local overlay to fix this, which I thought I’d share here in case anyone was interested.

The “hack” comes in three parts: an overlay for lib/RT/CustomFields.pm, a minor tweak needed in a local copy of /Elements/ShowCustomFields and an extra config variable to turn this behaviour on. First off the overlay, which we’ve got in /opt/rt5/local/lib/RT/CustomFields_Local.pm:

#!/usr/bin/perl

package RT::CustomFields;

use warnings;
use strict;
use Carp;
use Data::Dumper;
no warnings qw(redefine);

sub GroupingSort {
    my $self = shift;
    my $obj = shift;
    my $grouping = shift;

    my $grouping_class = $self->NewItem->_GroupingClass($obj);

    my $config = RT->Config->Get('CustomFieldGroupings');
    $config = {} unless ref($config) eq 'HASH';
    $config = $config->{$grouping_class} || [];
    my %h = ref $config eq "ARRAY" ? @{$config} : %{$config};

    if ( $grouping ) {
        my $list = $h{$grouping};
	$self->{'__GroupingSort'} = $list;
	$self->{'__GroupingSortPos'} = -1;
    }
    return;
}

sub NoGroupingSort {
    my $self = shift;

    $self->{'__GroupingSort'} = undef;
    return;
}

sub Next {
    my $self = shift;

    if($self->{'__GroupingSort'}) {
	$self->_DoSearch() if $self->{'must_redo_search'};
	$self->{'__GroupingSortPos'}++;
	my $CFName = $self->{'__GroupingSort'}[$self->{'__GroupingSortPos'}];
	if(!$CFName) {
	    # Gone through the whole list, so back to the start
	    $self->{'__GroupingSortPos'} = -1;
	    return(undef);
	} else {
	    foreach my $thisCF (@{$self->{'items'}}) {
		if($thisCF->Name eq $CFName) {
		    return($thisCF);
		}
	    }
	    return(undef);
	}
    } else {
	return ( $self->SUPER::Next() );
    }
}

sub Last {
    my $self = shift;

    if($self->{'__GroupingSort'}) {
	my $CFName = 
	    $self->{'__GroupingSort'}[scalar(@{$self->{'__GroupingSort'}})-1];
	foreach my $thisCF (@{$self->{'items'}}) {
	    if($thisCF->Name eq $CFName) {
		return($thisCF);
	    }
	}
	return(undef);
    } else {
	return ( $self->SUPER::Last() );
    }
}

sub GotoFirstItem {
    my $self = shift;

    if($self->{'__GroupingSort'}) {
	$self->{'__GroupingSortPos'} = -1;
    } else {
	return ( $self->SUPER::GotoFirstItem() );
    }
}

1;

Then I made a local copy of /opt/rt5/share/html/Elements/ShowCustomFields into /opt/rt5/local/html/Elements/ShowCustomFields and applied this small diff:

$ diff ShowCustomFields /opt/rt5/share/html/Elements/ShowCustomFields 
107,111d106
< # If this is in a grouping and we've asked for sorting based on CustomFieldGrouping arrays, turn that on now.
< if(defined $Grouping && RT->Config->Get('CustomFieldGroupingsSort') eq 'true') {
<   $CustomFields->GroupingSort( $Object => $Grouping );
< }
< 

Lastly in the RT_SiteConfig.pm (or in our case a separate custom field groupings sub-config) we have an extra line:

Set($CustomFieldGroupingsSort, 'true');

Once these are in place, do the usual Mason cache flush/webserver kick dance and you should find that custom field groups are now shown sorted based on the order of the arrays in the %CustomFieldGroups hash in the config. The win for us is that we’re doing a spot of preparatory work on a potential new system which will possibly be generating lots of queues dynamically, so by doing this we don’t have to fiddle with CF sort orders in each queue. Every queue gets its CF sort orders in groupings from the main config file and we can update/alter the array order in that config once for all queues. Hoorah!

2 Likes

Brilliant ! I had a user resquesting exactly this feature, will try it for sure !

1 Like

A quick update - we noticed that we also needed to apply the quick fix to a local copy of /Elements/EditCustomFields as well if you want the order to appear when groups of CFs are edited:

$ diff -c /opt/rt5/share/html/Elements/EditCustomFields /opt/rt5/local/html/Elements/EditCustomFields
*** /opt/rt5/share/html/Elements/EditCustomFields	2021-02-26 14:41:14.995341318 +0000
--- /opt/rt5/local/html/Elements/EditCustomFields	2021-04-21 09:08:35.199016403 +0100
***************
*** 96,101 ****
--- 96,108 ----
  
  $CustomFields->LimitToGrouping( $Object => $Grouping ) if defined $Grouping;
  
+ # If this is in a grouping and we've asked for sorting based on 
+ # CustomFieldGrouping arrays, turn that on now.
+ if(defined $Grouping && RT->Config->Get('CustomFieldGroupingsSort') eq 'true')
+ {
+   $CustomFields->GroupingSort( $Object => $Grouping );
+ }
+ 
  $m->callback( %ARGS, CallbackName => 'MassageCustomFields', CustomFields => $CustomFields,
                    Object => $Object, ShowHintsRef => \$ShowHints, InTableRef => \$InTable );