[Rt-commit] rt branch, 4.4-trunk, updated. rt-4.4.4-513-g14af0211a5

Jim Brandt jbrandt at bestpractical.com
Fri Jun 4 16:29:05 EDT 2021


The branch, 4.4-trunk has been updated
       via  14af0211a5d60a66c6b6962a4c921d3e99ce26b2 (commit)
       via  19aa86b44020e048ad7c24b28e681dbbdf2a900e (commit)
       via  5d7039ff0ddfa6af780c362e6389d2d708b2060a (commit)
       via  fd29f7e1cf500c9b2b757b012c06f6aa34287b13 (commit)
       via  8b5c8d1b28dc06b5259fb7d07b6da0df8efd6731 (commit)
       via  7abb7900e4f547c6bea1d2399c6ad07fad9b94d7 (commit)
       via  d9c9d676486698056ef0e862d08fe1f1b938b4d6 (commit)
       via  c37090a5b3233324213ca7c29a7250e4bf2513d9 (commit)
      from  43acfb645b556e1c3d2d57bc5d7c610517664be4 (commit)

Summary of changes:
 docs/UPGRADING-4.4                           |  12 +++
 etc/upgrade/4.4.5/content                    |  34 ++++++
 lib/RT/Interface/Web.pm                      |   2 +-
 lib/RT/Tickets.pm                            |  53 ++++++++-
 share/html/Elements/ColumnMap                | 155 +++++++++++++++++++++------
 share/html/Search/Elements/BuildFormatString |  25 ++++-
 share/html/Search/Elements/EditSort          |  24 +++--
 t/ticket/sort-by-user.t                      |  51 ++++++++-
 t/web/cf_access.t                            |   2 +-
 t/web/query_builder.t                        | 143 +++++++++++++++++++++++-
 10 files changed, 452 insertions(+), 49 deletions(-)

- Log -----------------------------------------------------------------
commit c37090a5b3233324213ca7c29a7250e4bf2513d9
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Nov 27 00:55:00 2019 +0800

    Support order by watcher's custom fields for ticket search

diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index bd71c2c68a..82e32f1b55 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1104,7 +1104,9 @@ Try and turn a custom role descriptor (e.g. C<CustomRole.{Engineer}>) into
 sub _CustomRoleDecipher {
     my ($self, $string) = @_;
 
-    my ($field, $column) = ($string =~ /^\{(.+)\}(?:\.(\w+))?$/);
+    # $column could be core fields like "EmailAddress" or CFs like
+    # "CustomField.{Department}", the CF format is used in OrderByCols.
+    my ($field, $column) = ($string =~ /^\{(.+?)\}(?:\.(.+))?$/);
 
     my $role;
 
@@ -1528,7 +1530,54 @@ sub OrderByCols {
                 $self->{_sql_u_watchers_alias_for_sort}{ $cache_key }
                     = $users = ( $self->_WatcherJoin( Name => $type, Class => "RT::" . $class ) )[2];
             }
-            push @res, { %$row, ALIAS => $users, FIELD => $column };
+
+            if ( $column =~ /^CustomField\.\{(.+)\}$/ ) {
+                my $cf_name = $1;
+                my $cf      = RT::CustomField->new( $self->CurrentUser );
+                $cf->LoadByCols( LookupType => RT::User->CustomFieldLookupType, Name => $cf_name );
+                if ( $cf->id ) {
+                    my $ocfvs = $self->NewAlias('ObjectCustomFieldValues');
+                    $self->Join(
+                        TYPE   => 'LEFT',
+                        ALIAS1 => $users,
+                        FIELD1 => 'id',
+                        ALIAS2 => $ocfvs,
+                        FIELD2 => 'ObjectId',
+                    );
+
+                    $self->Limit(
+                        LEFTJOIN        => $ocfvs,
+                        FIELD           => 'CustomField',
+                        VALUE           => $cf->id,
+                        ENTRYAGGREGATOR => 'AND',
+                    );
+
+                    # The following is copied from RT::SearchBuilder::_OrderByCF
+                    my $CFvs = $self->Join(
+                        TYPE   => 'LEFT',
+                        ALIAS1 => $ocfvs,
+                        FIELD1 => 'CustomField',
+                        TABLE2 => 'CustomFieldValues',
+                        FIELD2 => 'CustomField',
+                    );
+                    $self->Limit(
+                        LEFTJOIN        => $CFvs,
+                        FIELD           => 'Name',
+                        QUOTEVALUE      => 0,
+                        VALUE           => "$ocfvs.Content",
+                        ENTRYAGGREGATOR => 'AND'
+                    );
+                    push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' },
+                        { %$row, ALIAS => $ocfvs, FIELD => 'Content' };
+                }
+                else {
+                    RT->Logger->warning("Couldn't load user custom field $cf_name");
+                    next;
+                }
+            }
+            else {
+                push @res, { %$row, ALIAS => $users, FIELD => $column };
+            }
        } elsif ( defined $meta->[0] && $meta->[0] eq 'CUSTOMFIELD' ) {
            my ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $subkey );
            my $cfkey = $cf ? $cf->id : "$object.$field";

commit d9c9d676486698056ef0e862d08fe1f1b938b4d6
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Nov 27 00:55:45 2019 +0800

    Test order by watcher's custom fields for ticket search

diff --git a/t/ticket/sort-by-user.t b/t/ticket/sort-by-user.t
index 42f95fc258..bde06d2ef3 100644
--- a/t/ticket/sort-by-user.t
+++ b/t/ticket/sort-by-user.t
@@ -1,5 +1,5 @@
 
-use RT::Test nodata => 1, tests => 52;
+use RT::Test nodata => 1, tests => undef;
 
 use strict;
 use warnings;
@@ -33,6 +33,7 @@ foreach my $u (qw(Z A)) {
     my $user = RT::User->new( RT->SystemUser );
     my ($uid) = $user->Create(
         Name => $name,
+        EmailAddress => $name . '@localhost',
         Privileged => 1,
     );
     ok $uid, "created user #$uid";
@@ -117,7 +118,7 @@ run_tests();
 run_tests();
 
 @data = (
-    { Subject => 'RT' },
+    { Subject => 'Nobody' },
     { Subject => 'Z', LastUpdatedBy => $uids[0] },
     { Subject => 'A', LastUpdatedBy => $uids[1] },
 );
@@ -127,4 +128,50 @@ run_tests();
 );
 run_tests();
 
+my $cf = RT::Test->load_or_create_custom_field(
+    Name  => 'Department',
+    Type  => 'FreeformSingle',
+    LookupType => RT::User->CustomFieldLookupType,
+);
+my ( $ret, $msg ) = $cf->AddToObject( RT::User->new( RT->SystemUser ) );
+ok( $ret, "Added CF globally: $msg" );
+
+( $ret, $msg ) = $users[0]->AddCustomFieldValue( Field => $cf, Value => 'Bar' );
+ok( $ret, "Added CF value 'Foo' to users[0]: $msg" );
+
+( $ret, $msg ) = $users[1]->AddCustomFieldValue( Field => $cf, Value => 'Foo' );
+ok( $ret, "Added CF value 'Bar' to users[1]: $msg" );
+
+ at data = (
+    { Subject => '-' },
+    { Subject => 'Z', Owner => $uids[1] },
+    { Subject => 'A', Owner => $uids[0] },
+);
+ at tickets = RT::Test->create_tickets( { Queue => $queue->id }, @data );
+ at test = (
+    { Order => "Owner.CustomField.{Department}" },
+);
+run_tests();
+
+my $cr = RT::CustomRole->new( RT->SystemUser );
+( $ret, $msg ) = $cr->Create(
+    Name      => 'Engineer',
+    MaxValues => 0,
+);
+ok( $ret, "Created custom role: $msg" );
+
+( $ret, $msg ) = $cr->AddToObject( ObjectId => $queue->id );
+ok( $ret, "Added CR to queue: $msg" );
+
+ at data = (
+    { Subject => '-' },
+    { Subject => 'Z', $cr->GroupType => $uids[1] },
+    { Subject => 'A', $cr->GroupType => $uids[0] },
+);
+ at tickets = RT::Test->create_tickets( { Queue => $queue->id }, @data );
+ at test = ( { Order => "CustomRole.{Engineer}.CustomField.{Department}" }, );
+run_tests();
+
 @tickets = ();
+
+done_testing;

commit 7abb7900e4f547c6bea1d2399c6ad07fad9b94d7
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Apr 22 05:38:19 2020 +0800

    Support more watcher fields including user cfs in search result format

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 10a4a4192a..41d937ed9a 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -4004,7 +4004,7 @@ sub GetColumnMapEntry {
 
     # complex things
     elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.(.+)$/ ) {
-        $subkey =~ s/^\{(.*)\}$/$1/;
+        $subkey =~ s/^\{(.*)\}/$1/ unless $mainkey eq 'CustomRole';
         return undef unless $args{'Map'}->{$mainkey};
         return $args{'Map'}{$mainkey}{ $args{'Attribute'} }
             unless ref $args{'Map'}{$mainkey}{ $args{'Attribute'} } eq 'CODE';
diff --git a/share/html/Elements/ColumnMap b/share/html/Elements/ColumnMap
index 836d658d8c..50082ef37d 100644
--- a/share/html/Elements/ColumnMap
+++ b/share/html/Elements/ColumnMap
@@ -54,6 +54,104 @@ $Attr  => undef
 
 use Scalar::Util;
 
+my $role_value = sub {
+    my $role   = shift;
+    my $object = shift;
+
+    # $[0] is the index number of current row
+    my $field  = $_[1] || '';
+
+    my ( $role_type, $attr, $cf_name );
+
+
+    if ( $role eq 'CustomRole' ) {
+        my $role_name;
+        if ( $field =~ /^\{(.+)\}\.CustomField\.\{(.+)\}/ ) {
+
+            # {test}.CustomField.{foo}
+            $role_name = $1;
+            $cf_name   = $2;
+        }
+        elsif ( $field =~ /^\{(.+)\}(?:\.(\w+))?$/ ) {
+
+            # {test}.Name or {test}
+            $role_name = $1;
+            $attr      = $2;
+        }
+
+        # Cache the role object on a per-request basis, to avoid
+        # having to load it for every row
+        my $key = "RT::CustomRole-" . $role_name;
+
+        $role_type = $m->notes($key);
+        if ( !$role_type ) {
+            my $role_obj = RT::CustomRole->new( $object->CurrentUser );
+            $role_obj->Load($role_name);
+
+            RT->Logger->notice("Unable to load custom role $role_name")
+                unless $role_obj->Id;
+
+            $role_type = $role_obj->GroupType;
+            $m->notes( $key, $role_type );
+        }
+    }
+    else {
+        if ( $field =~ /^CustomField\.\{(.+)\}/ ) {
+            $cf_name = $1;
+        }
+        elsif ( $field =~ /^(\w+)$/ ) {
+            $attr = $1;
+        }
+        $role_type = $role;
+    }
+
+    return if !$role_type;
+
+    my $role_group = $object->RoleGroup($role_type);
+    if ( $cf_name || $attr ) {
+        # TODO Show direct members only?
+        my $users = $role_group->UserMembersObj;
+        my @values;
+
+        while ( my $user = $users->Next ) {
+            if ($cf_name) {
+                my $key = join( "-", "CF", $user->CustomFieldLookupType, $cf_name );
+                my $cf = $m->notes($key);
+                if ( !$cf ) {
+                    $cf = $user->LoadCustomFieldByIdentifier($cf_name);
+                    RT->Logger->debug( "Unable to load $cf_name for " . $user->CustomFieldLookupType )
+                        unless $cf->Id;
+                    $m->notes( $key, $cf );
+                }
+
+                my $ocfvs = $cf->ValuesForObject($user)->ItemsArrayRef;
+                my $comp
+                    = $m->comp_exists( "/Elements/ShowCustomField" . $cf->Type )
+                    ? "/Elements/ShowCustomField" . $cf->Type
+                    : undef;
+
+                push @values, map { $comp ? \( $m->scomp( $comp, Object => $_ ) ) : $_->Content } @$ocfvs;
+
+            }
+            elsif ( $user->_Accessible( $attr, 'read' ) ) {
+                push @values, $user->$attr || ();
+            }
+        }
+        return @values if @values <= 1;
+
+        if ($cf_name) {
+            @values = map { \"<li>", $_, \"</li>" } @values;
+            @values = ( \"<ul class='cf-values'>", @values, \"</ul>" );
+        }
+        else {
+            return join ', ', @values;
+        }
+    }
+    else {
+        return \( $m->scomp( "/Elements/ShowPrincipal", Object => $role_group ) );
+    }
+};
+
 my ($COLUMN_MAP, $WCOLUMN_MAP);
 $WCOLUMN_MAP = $COLUMN_MAP = {
     id => {
@@ -143,34 +241,21 @@ $WCOLUMN_MAP = $COLUMN_MAP = {
     },
     CustomRole => {
         attribute => sub { return shift @_ },
-        title     => sub { return pop @_ },
-        value     => sub {
-            my $object = shift;
-            my $role_name = pop;
-
-            my $role_type = do {
-                # Cache the role object on a per-request basis, to avoid
-                # having to load it for every row
-                my $key = "RT::CustomRole-" . $role_name;
-
-                my $role_type = $m->notes($key);
-                if (!$role_type) {
-                    my $role_obj = RT::CustomRole->new($object->CurrentUser);
-                    $role_obj->Load($role_name);
-
-                    RT->Logger->notice("Unable to load custom role $role_name")
-                        unless $role_obj->Id;
-
-                    $role_type = $role_obj->GroupType;
-                    $m->notes($key, $role_type);
-                }
-
-                $role_type;
-            };
-
-            return if !$role_type;
-            return \($m->scomp("/Elements/ShowPrincipal", Object => $object->RoleGroup($role_type) ) );
+        title     => sub {
+            my $field = pop @_;
+            if (   $field =~ /^\{(.+)\}\.CustomField\.\{(.+)\}/
+                || $field =~ /^\{(.+)\}\.(.+)/ )
+            {
+                return "$1.$2";
+            }
+            elsif ( $field =~ /^\{(.+)\}$/ ) {
+                return $1;
+            }
+            else {
+                return $field;
+            }
         },
+        value => sub { return $role_value->('CustomRole', @_) },
     },
 
     CheckBox => {
@@ -254,9 +339,19 @@ if ($RecordClass->DOES("RT::Record::Role::Roles")) {
         for my $role ($RecordClass->Roles(UserDefined => 0)) {
             my $attrs = $RecordClass->Role($role);
             $ROLE_MAP->{$RecordClass}{$role} = {
-                title => $role,
-                attribute => $attrs->{Column} || "$role.EmailAddress",
-                value => sub { return \($m->scomp("/Elements/ShowPrincipal", Object => $_[0]->RoleGroup($role) ) ) },
+                attribute => sub { return shift @_ },
+                title => sub {
+                    my $field = pop @_;
+                    if (   $field =~ /^CustomField\.\{(.+)\}/
+                        || $field =~ /^(?!$role)(.+)/ )
+                    {
+                        return "$role.$1";
+                    }
+                    else {
+                        return $role;
+                    }
+                },
+                value => sub { return $role_value->($role, @_, @_ == 2 ? '' : () ) },
             };
             $ROLE_MAP->{$RecordClass}{$role . "s"} = $ROLE_MAP->{$RecordClass}{$role}
                 unless $attrs->{Single};

commit 8b5c8d1b28dc06b5259fb7d07b6da0df8efd6731
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Nov 27 02:17:51 2019 +0800

    Add more watcher fields including user cfs to OrderBy/Columns in search builder
    
    Besides user cfs, the following core fields are added: id, Name,
    EmailAddress, RealName, Organization, City and Country.
    
    The previous ambiguous "Owner" in OrderBy select is renamed to "Owner.Name".

diff --git a/share/html/Search/Elements/BuildFormatString b/share/html/Search/Elements/BuildFormatString
index ce541f889e..d5e8718960 100644
--- a/share/html/Search/Elements/BuildFormatString
+++ b/share/html/Search/Elements/BuildFormatString
@@ -128,8 +128,29 @@ foreach my $id (keys %queues) {
     next unless $queue->Id;
     $CustomRoles->LimitToObjectId($queue->Id);
 }
-while ( my $Role = $CustomRoles->Next ) {
-    push @fields, "CustomRole.{" . $Role->Name . "}";
+
+my @user_fields = qw/id Name EmailAddress Organization RealName City Country/;
+my $user_cfs    = RT::CustomFields->new( $session{CurrentUser} );
+$user_cfs->Limit( FIELD => 'LookupType', VALUE => RT::User->CustomFieldLookupType );
+while ( my $user_cf = $user_cfs->Next ) {
+    push @user_fields, join '.', 'CustomField', '{' . $user_cf->Name . '}';
+}
+
+for my $watcher (qw/AdminCc Cc Owner Requestor/) {
+    for my $user_field (@user_fields) {
+        my $field = join '.', $watcher, $user_field;
+        push @fields, $field;
+    }
+}
+
+# Add all available CustomRoles to the list of sortable columns.
+while ( my $role = $CustomRoles->Next ) {
+    push @fields, 'CustomRole.{' . $role->Name . '}';
+
+    # Add all available CustomRoles to the list of sortable columns.
+    for my $user_field (@user_fields) {
+        push @fields, join '.', 'CustomRole.{' . $role->Name . '}', $user_field;
+    }
 }
 
 $m->callback( Fields => \@fields, ARGSRef => \%ARGS );
diff --git a/share/html/Search/Elements/EditSort b/share/html/Search/Elements/EditSort
index 7b70b923e3..9d9a15aaaf 100644
--- a/share/html/Search/Elements/EditSort
+++ b/share/html/Search/Elements/EditSort
@@ -108,20 +108,26 @@ for my $field (keys %FieldDescriptions) {
     $fields{$field} = $field;
 }
 
-$fields{'Owner'} = 'Owner';
-$fields{ $_ . '.EmailAddress' } = $_ . '.EmailAddress'
-    for qw(Requestor Cc AdminCc);
-
 # Add all available CustomFields to the list of sortable columns.
 my @cfs = grep /^CustomField/, @{$ARGS{AvailableColumns}};
 $fields{$_} = $_ for @cfs;
 
+# Add all available core roles to the list of sortable columns.
+my @roles = grep /^(?:Owner|Requestor|Cc|AdminCc)\./, @{$ARGS{AvailableColumns}};
+$fields{$_} = $_ for @roles;
+
 # Add all available CustomRoles to the list of sortable columns.
-my @roles = grep /^CustomRole/, @{$ARGS{AvailableColumns}};
-for my $role (@roles) {
-    my ($label) = $role =~ /^CustomRole.\{(.*)\}$/;
-    my $value = $role;
-    $fields{$label . '.EmailAddress' } = $value . '.EmailAddress';
+my @custom_roles = grep /^CustomRole\./, @{$ARGS{AvailableColumns}};
+for my $role ( @custom_roles ) {
+    my $label = $role;
+    # In case custom role contains "{}" in name.
+    if ( $label =~ /\.CustomField/ ) {
+        $label =~ s!^CustomRole\.\{(.*)\}(?=\.CustomField\.)!$1!;
+    }
+    else {
+        $label =~ s!^CustomRole\.\{(.*)\}!$1!;
+    }
+    $fields{$label} = $role;
 }
 
 # Add PAW sort

commit fd29f7e1cf500c9b2b757b012c06f6aa34287b13
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Nov 27 02:22:14 2019 +0800

    Upgrade OrderBy "Owner" to new version "Owner.Name" in saved searchs
    
    As we added more watcher fields to OrderBy, previous ambiguous "Owner"
    has been removed.

diff --git a/etc/upgrade/4.4.5/content b/etc/upgrade/4.4.5/content
index 45b13c1eee..68e0a73997 100644
--- a/etc/upgrade/4.4.5/content
+++ b/etc/upgrade/4.4.5/content
@@ -1,6 +1,40 @@
 use warnings;
 use strict;
 
+our @Initial = (
+    sub {
+        my $searches = RT::Attributes->new( RT->SystemUser );
+        $searches->Limit( FIELD => 'Name', VALUE => 'SavedSearch' );
+        $searches->OrderBy( FIELD => 'id' );
+
+        while ( my $search = $searches->Next ) {
+            my $content = $search->Content;
+            next unless ref $content eq 'HASH';
+
+            if ( $content->{OrderBy} ) {
+                my @order_by = split /\|/, $content->{OrderBy};
+                my @new_order_by;
+                my $changed;
+                for my $order_by (@order_by) {
+                    if ( $order_by eq 'Owner' ) {
+                        push @new_order_by, 'Owner.Name';
+                        $changed = 1;
+                    }
+                    else {
+                        push @new_order_by, $order_by;
+                    }
+                }
+                if ($changed) {
+                    $content->{OrderBy} = join '|', @new_order_by;
+                    my ( $ok, $msg ) = $search->SetContent($content);
+                    RT->Logger->error("Unable to upgrade saved chart #@{[$search->id]}: $msg")
+                        unless $ok;
+                }
+            }
+        }
+    }
+);
+
 our @ScripConditions = (
     {
         Name                 => 'On Create Via Email',

commit 5d7039ff0ddfa6af780c362e6389d2d708b2060a
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Nov 27 02:31:26 2019 +0800

    Update query builder tests as now we support more watcher fields

diff --git a/t/web/cf_access.t b/t/web/cf_access.t
index aa707f5dc6..505269b599 100644
--- a/t/web/cf_access.t
+++ b/t/web/cf_access.t
@@ -231,7 +231,7 @@ $m->submit_form(
 
 $m->form_name('BuildQuery');
 
-my $col = ($m->current_form->find_input('SelectDisplayColumns'))[-1];
+my ($col) = grep { ($_->possible_values)[-1] =~ /CustomField/ } $m->find_all_inputs( name => 'SelectDisplayColumns' );
 $col->value( ($col->possible_values)[-1] );
 
 $m->click('AddCol');
diff --git a/t/web/query_builder.t b/t/web/query_builder.t
index 89e89f5707..94a167041a 100644
--- a/t/web/query_builder.t
+++ b/t/web/query_builder.t
@@ -326,13 +326,25 @@ diag "make sure skipped order by field doesn't break search";
     ), "link to the ticket" );
 }
 
-diag "make sure the list of columns available in the 'Order by' dropdowns are complete";
+diag "make sure the list of columns available in the 'Order by'/'Add Columns' dropdowns are complete";
 {
     $agent->get_ok($url . 'Search/Build.html');
 
     my @orderby = qw(
+        AdminCc.City
+        AdminCc.Country
         AdminCc.EmailAddress
+        AdminCc.Name
+        AdminCc.Organization
+        AdminCc.RealName
+        AdminCc.id
+        Cc.City
+        Cc.Country
         Cc.EmailAddress
+        Cc.Name
+        Cc.Organization
+        Cc.RealName
+        Cc.id
         Created
         Creator
         Custom.Ownership
@@ -341,10 +353,22 @@ diag "make sure the list of columns available in the 'Order by' dropdowns are co
         InitialPriority
         LastUpdated
         LastUpdatedBy
-        Owner
+        Owner.City
+        Owner.Country
+        Owner.EmailAddress
+        Owner.Name
+        Owner.Organization
+        Owner.RealName
+        Owner.id
         Priority
         Queue
+        Requestor.City
+        Requestor.Country
         Requestor.EmailAddress
+        Requestor.Name
+        Requestor.Organization
+        Requestor.RealName
+        Requestor.id
         Resolved
         SLA
         Started
@@ -370,17 +394,115 @@ diag "make sure the list of columns available in the 'Order by' dropdowns are co
             is ($scraped_orderby, '[none] '.$orderby);
     }
 
+    my @formats = qw(
+        id
+        QueueName
+        Subject
+        Status
+        ExtendedStatus
+        UpdateStatus
+        Type
+        OwnerName
+        Requestors
+        Cc
+        AdminCc
+        CreatedBy
+        LastUpdatedBy
+        Priority
+        InitialPriority
+        FinalPriority
+        TimeWorked
+        TimeLeft
+        TimeEstimated
+        Starts
+        StartsRelative
+        Started
+        StartedRelative
+        Created
+        CreatedRelative
+        LastUpdated
+        LastUpdatedRelative
+        Told
+        ToldRelative
+        Due
+        DueRelative
+        Resolved
+        ResolvedRelative
+        SLA
+        RefersTo
+        ReferredToBy
+        DependsOn
+        DependedOnBy
+        MemberOf
+        Members
+        Parents
+        Children
+        Bookmark
+        Timer
+        NEWLINE
+        NBSP
+        AdminCc.id
+        AdminCc.Name
+        AdminCc.EmailAddress
+        AdminCc.Organization
+        AdminCc.RealName
+        AdminCc.City
+        AdminCc.Country
+        Cc.id
+        Cc.Name
+        Cc.EmailAddress
+        Cc.Organization
+        Cc.RealName
+        Cc.City
+        Cc.Country
+        Owner.id
+        Owner.Name
+        Owner.EmailAddress
+        Owner.Organization
+        Owner.RealName
+        Owner.City
+        Owner.Country
+        Requestor.id
+        Requestor.Name
+        Requestor.EmailAddress
+        Requestor.Organization
+        Requestor.RealName
+        Requestor.City
+        Requestor.Country
+        );
+    my $formats = join(' ', @formats);
+    my $scraped_formats = $agent->scrape_text_by_attr('name', 'SelectDisplayColumns');
+    is( $scraped_formats, $formats, 'Default format' );
+
     my $cf = RT::Test->load_or_create_custom_field(
         Name  => 'Location',
         Queue => 'General',
         Type  => 'FreeformSingle', );
     isa_ok( $cf, 'RT::CustomField' );
 
+    my $user_cf = RT::Test->load_or_create_custom_field(
+        Name       => 'Employee ID',
+        LookupType => RT::User->CustomFieldLookupType,
+        Type       => 'FreeformSingle',
+    );
+
+    my $cr = RT::CustomRole->new( RT->SystemUser );
+    my ( $ret, $msg ) = $cr->Create(
+        Name      => 'Engineer',
+        MaxValues => 0,
+    );
+    ok( $ret, "Created custom role: $msg" );
+
+    ( $ret, $msg ) = $cr->AddToObject( ObjectId => 'General' );
+    ok( $ret, "Added CR to queue: $msg" );
+
     ok($agent->form_name('BuildQuery'), "found the form");
     $agent->field("ValueOfQueue", "General");
     $agent->submit;
 
     push @orderby, 'CustomField.{Location}';
+    push @orderby, 'Engineer', map { "Engineer.$_" } qw/City Country EmailAddress Name Organization RealName id/;
+    push @orderby, map {"$_.CustomField.{Employee ID}"} qw/AdminCc Cc Requestor Owner Engineer/;
 
     $orderby = join(' ', sort @orderby);
 
@@ -393,6 +515,23 @@ diag "make sure the list of columns available in the 'Order by' dropdowns are co
         is ($scraped_orderby, '[none] '.$orderby);
     }
 
+    # Formats are not sorted, we need to insert new items accordingly
+    my @new_formats;
+    for my $format ( @formats ) {
+        push @new_formats, $format;
+        if ( $format eq 'NBSP' ) {
+            push @new_formats, 'CustomField.{Location}';
+        }
+        elsif ( $format =~ /(\w+)\.Country/ ) {
+            push @new_formats, "$1.CustomField.{Employee ID}";
+        }
+    }
+    push @new_formats, 'CustomRole.{Engineer}',
+        map {"CustomRole.{Engineer}.$_"} qw/id Name EmailAddress Organization RealName City Country/, 'CustomField.{Employee ID}';
+    $formats = join(' ', @new_formats);
+    $scraped_formats = $agent->scrape_text_by_attr('name', 'SelectDisplayColumns');
+    is( $scraped_formats, $formats, 'Format with custom fields and custom roles' );
+
     $cf->SetDisabled(1);
 }
 

commit 19aa86b44020e048ad7c24b28e681dbbdf2a900e
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Jun 4 16:21:04 2021 -0400

    Document the database updates for the Owner.Name change

diff --git a/docs/UPGRADING-4.4 b/docs/UPGRADING-4.4
index f7a925d6bb..20aa146ddd 100644
--- a/docs/UPGRADING-4.4
+++ b/docs/UPGRADING-4.4
@@ -678,6 +678,18 @@ so batch runs only once for the outermost updates. All transactions
 performed for that batch are available from the C<TransactionBatch> method
 as expected.
 
+=item * Ambigious Owner order by option in search replaced with Owner.Name
+
+RT 4.4.5 adds a bunch of new order by and format options for users and roles
+to the Query Builder. For example, you can order by user fields on a user
+like Owner.EmailAddress, Owner.RealName, or even Owner.Organization.
+As part of this change, the previous Owner entry has been renamed to
+Owner.Name.
+
+The upgrade scripts include a step to make this change in any saved searches
+in the database automatically. If you have Owner as an order by field in
+searches stored elsewhere or as a link, you can update to Owner.Name manually.
+
 =back
 
 =cut

commit 14af0211a5d60a66c6b6962a4c921d3e99ce26b2
Merge: 43acfb645b 19aa86b440
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Jun 4 16:21:25 2021 -0400

    Merge branch '4.4/ticket-search-order-by-watcher-cfs' into 4.4-trunk


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


More information about the rt-commit mailing list