[Rt-commit] rt branch, 4.0/extensible-ticketsql-cf-limits, created. rt-4.0.13-98-gef1eae9

Thomas Sibley trs at bestpractical.com
Fri Jun 28 17:43:48 EDT 2013


The branch, 4.0/extensible-ticketsql-cf-limits has been created
        at  ef1eae9b27d1f0e2e94bd4cfc357602257b7aae1 (commit)

- Log -----------------------------------------------------------------
commit 1ea10ce041b71633d1c7b5bcfe2ed109b977a05e
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Feb 6 17:25:22 2013 -0800

    Allow CustomFieldLookupType to be called as a class method
    
    (cherry picked from commit 1ff3f2b0a97d1e336ced7542ce2b0201ff5309fc)

diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 61b66d5..1597784 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -1616,7 +1616,7 @@ Returns the path RT uses to figure out which custom fields apply to this object.
 
 sub CustomFieldLookupType {
     my $self = shift;
-    return ref($self);
+    return ref($self) || $self;
 }
 
 

commit 93b4f27f47c51f4dcec0338ea8afcb76fc62b1a5
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Mon Jan 30 17:27:49 2012 -0500

    Add an ObjectTypeFromlookupType method and document RecordClassFromLookupType
    
    RecordClassFromLookupType is useful when joining to ObjectCustomFields,
    but there wasn't an existing method for code that wants to join to
    ObjectCustomFieldValues, so we can borrow the code and tweak the regex a
    bit.

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index a69c6f4..11701b9 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -1205,12 +1205,37 @@ sub FriendlyLookupType {
     return ( $self->loc( $FriendlyObjectTypes[$#types], @types ) );
 }
 
+=head1 RecordClassFromLookupType
+
+Returns the type of Object referred to by ObjectCustomFields' ObjectId column
+
+=cut
+
 sub RecordClassFromLookupType {
     my $self = shift;
     my ($class) = ($self->LookupType =~ /^([^-]+)/);
     unless ( $class ) {
         $RT::Logger->error(
-            "Custom Field #". $self->id 
+            "Custom Field #". $self->id
+            ." has incorrect LookupType '". $self->LookupType ."'"
+        );
+        return undef;
+    }
+    return $class;
+}
+
+=head1 ObjectTypeFromLookupType
+
+Returns the ObjectType used in ObjectCustomFieldValues rows for this CF
+
+=cut
+
+sub ObjectTypeFromLookupType {
+    my $self = shift;
+    my ($class) = ($self->LookupType =~ /([^-]+)$/);
+    unless ( $class ) {
+        $RT::Logger->error(
+            "Custom Field #". $self->id
             ." has incorrect LookupType '". $self->LookupType ."'"
         );
         return undef;

commit 8031dcf0d8d4e630e8ee4d69478b933aa78d78cf
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jun 26 16:06:31 2013 -0700

    ObjectType/RecordTypeFromLookupType as class methods
    
    Where LookupType must be passed as an argument.

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 11701b9..0211815 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -51,7 +51,7 @@ package RT::CustomField;
 use strict;
 use warnings;
 
-
+use Scalar::Util 'blessed';
 
 use base 'RT::Record';
 
@@ -1209,16 +1209,25 @@ sub FriendlyLookupType {
 
 Returns the type of Object referred to by ObjectCustomFields' ObjectId column
 
+Optionally takes a LookupType to use instead of using the value on the loaded
+record.  In this case, the method may be called on the class instead of an
+object.
+
 =cut
 
 sub RecordClassFromLookupType {
     my $self = shift;
-    my ($class) = ($self->LookupType =~ /^([^-]+)/);
+    my $type = shift || $self->LookupType;
+    my ($class) = ($type =~ /^([^-]+)/);
     unless ( $class ) {
-        $RT::Logger->error(
-            "Custom Field #". $self->id
-            ." has incorrect LookupType '". $self->LookupType ."'"
-        );
+        if (blessed($self) and $self->LookupType eq $type) {
+            $RT::Logger->error(
+                "Custom Field #". $self->id
+                ." has incorrect LookupType '$type'"
+            );
+        } else {
+            RT->Logger->error("Invalid LookupType passed as argument: $type");
+        }
         return undef;
     }
     return $class;
@@ -1228,16 +1237,25 @@ sub RecordClassFromLookupType {
 
 Returns the ObjectType used in ObjectCustomFieldValues rows for this CF
 
+Optionally takes a LookupType to use instead of using the value on the loaded
+record.  In this case, the method may be called on the class instead of an
+object.
+
 =cut
 
 sub ObjectTypeFromLookupType {
     my $self = shift;
-    my ($class) = ($self->LookupType =~ /([^-]+)$/);
+    my $type = shift || $self->LookupType;
+    my ($class) = ($type =~ /([^-]+)$/);
     unless ( $class ) {
-        $RT::Logger->error(
-            "Custom Field #". $self->id
-            ." has incorrect LookupType '". $self->LookupType ."'"
-        );
+        if (blessed($self) and $self->LookupType eq $type) {
+            $RT::Logger->error(
+                "Custom Field #". $self->id
+                ." has incorrect LookupType '$type'"
+            );
+        } else {
+            RT->Logger->error("Invalid LookupType passed as argument: $type");
+        }
         return undef;
     }
     return $class;

commit 5b6f9b9513114ce2aee99f495996b2f333b956b8
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jun 26 22:56:11 2013 -0700

    Refactor part of LimitToGlobalOrObjectId into LimitToObjectId
    
    Useful when you only want to find CFs applied to a certain object and
    not global ones too.

diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index 017018e..1ea75da 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -141,6 +141,25 @@ sub LimitToParentType  {
     $self->Limit( FIELD => 'LookupType', STARTSWITH => "$lookup" );
 }
 
+=head2 LimitToObjectId
+
+Takes an ObjectId and limits the collection to CFs applied to said object.
+
+When called multiple times the ObjectId limits are joined with OR.
+
+=cut
+
+sub LimitToObjectId {
+    my $self = shift;
+    my $id = shift;
+    $self->Limit(
+        ALIAS           => $self->_OCFAlias,
+        FIELD           => 'ObjectId',
+        OPERATOR        => '=',
+        VALUE           => $id || 0,
+        ENTRYAGGREGATOR => 'OR'
+    );
+}
 
 =head2 LimitToGlobalOrObjectId
 
@@ -155,19 +174,11 @@ sub LimitToGlobalOrObjectId {
 
 
     foreach my $id (@_) {
-	$self->Limit( ALIAS           => $self->_OCFAlias,
-		    FIELD           => 'ObjectId',
-		    OPERATOR        => '=',
-		    VALUE           => $id || 0,
-		    ENTRYAGGREGATOR => 'OR' );
-	$global_only = 0 if $id;
+        $self->LimitToObjectId($id);
+        $global_only = 0 if $id;
     }
 
-    $self->Limit( ALIAS           => $self->_OCFAlias,
-                 FIELD           => 'ObjectId',
-                 OPERATOR        => '=',
-                 VALUE           => 0,
-                 ENTRYAGGREGATOR => 'OR' ) unless $global_only;
+    $self->LimitToObjectId(0) unless $global_only;
 }
 
 sub _LimitToOCFs {

commit 5a2c4002c0aa30111890b2f8dcef395886eedd76
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Mon Jan 30 17:31:47 2012 -0500

    Rename some variables that will become confusing.
    
    As these join aliases start to refer to transaction cfs and not just
    ticket cfs, it is really weird to have everything named TicketCFs

diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 41aa313..679186b 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1374,9 +1374,9 @@ sub _CustomFieldJoin {
                  $self->{_sql_cf_alias}{$cfkey} );
     }
 
-    my ($TicketCFs, $CFs);
+    my ($ObjectCFs, $CFs);
     if ( $cfid ) {
-        $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+        $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
             TYPE   => 'LEFT',
             ALIAS1 => 'main',
             FIELD1 => 'id',
@@ -1384,7 +1384,7 @@ sub _CustomFieldJoin {
             FIELD2 => 'ObjectId',
         );
         $self->SUPER::Limit(
-            LEFTJOIN        => $TicketCFs,
+            LEFTJOIN        => $ObjectCFs,
             FIELD           => 'CustomField',
             VALUE           => $cfid,
             ENTRYAGGREGATOR => 'AND'
@@ -1425,7 +1425,7 @@ sub _CustomFieldJoin {
             VALUE           => $field,
         );
 
-        $TicketCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
+        $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
             TYPE   => 'LEFT',
             ALIAS1 => $CFs,
             FIELD1 => 'id',
@@ -1433,7 +1433,7 @@ sub _CustomFieldJoin {
             FIELD2 => 'CustomField',
         );
         $self->SUPER::Limit(
-            LEFTJOIN        => $TicketCFs,
+            LEFTJOIN        => $ObjectCFs,
             FIELD           => 'ObjectId',
             VALUE           => 'main.id',
             QUOTEVALUE      => 0,
@@ -1441,20 +1441,20 @@ sub _CustomFieldJoin {
         );
     }
     $self->SUPER::Limit(
-        LEFTJOIN        => $TicketCFs,
+        LEFTJOIN        => $ObjectCFs,
         FIELD           => 'ObjectType',
         VALUE           => 'RT::Ticket',
         ENTRYAGGREGATOR => 'AND'
     );
     $self->SUPER::Limit(
-        LEFTJOIN        => $TicketCFs,
+        LEFTJOIN        => $ObjectCFs,
         FIELD           => 'Disabled',
         OPERATOR        => '=',
         VALUE           => '0',
         ENTRYAGGREGATOR => 'AND'
     );
 
-    return ($TicketCFs, $CFs);
+    return ($ObjectCFs, $CFs);
 }
 
 =head2 _CustomFieldLimit
@@ -1580,10 +1580,10 @@ sub _CustomFieldLimit {
         # IS[ NOT] NULL without column is the same as has[ no] any CF value,
         # we can reuse our default joins for this operation
         # with column specified we have different situation
-        my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
         $self->_OpenParen;
         $self->_SQLLimit(
-            ALIAS    => $TicketCFs,
+            ALIAS    => $ObjectCFs,
             FIELD    => 'id',
             OPERATOR => $op,
             VALUE    => $value,
@@ -1643,7 +1643,7 @@ sub _CustomFieldLimit {
     } 
     elsif ( !$negative_op || $single_value ) {
         $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if !$single_value && !$range_op;
-        my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
 
         $self->_OpenParen;
 
@@ -1654,7 +1654,7 @@ sub _CustomFieldLimit {
         # otherwise search in Content and in LargeContent
         if ( $column ) {
             $self->_SQLLimit( $fix_op->(
-                ALIAS      => $TicketCFs,
+                ALIAS      => $ObjectCFs,
                 FIELD      => $column,
                 OPERATOR   => $op,
                 VALUE      => $value,
@@ -1680,7 +1680,7 @@ sub _CustomFieldLimit {
                     $self->_OpenParen;
 
                     $self->_SQLLimit(
-                        ALIAS    => $TicketCFs,
+                        ALIAS    => $ObjectCFs,
                         FIELD    => 'Content',
                         OPERATOR => ">=",
                         VALUE    => $daystart,
@@ -1688,7 +1688,7 @@ sub _CustomFieldLimit {
                     );
 
                     $self->_SQLLimit(
-                        ALIAS    => $TicketCFs,
+                        ALIAS    => $ObjectCFs,
                         FIELD    => 'Content',
                         OPERATOR => "<",
                         VALUE    => $dayend,
@@ -1701,7 +1701,7 @@ sub _CustomFieldLimit {
             elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) {
                 if ( length( Encode::encode_utf8($value) ) < 256 ) {
                     $self->_SQLLimit(
-                        ALIAS    => $TicketCFs,
+                        ALIAS    => $ObjectCFs,
                         FIELD    => 'Content',
                         OPERATOR => $op,
                         VALUE    => $value,
@@ -1712,14 +1712,14 @@ sub _CustomFieldLimit {
                 else {
                     $self->_OpenParen;
                     $self->_SQLLimit(
-                        ALIAS           => $TicketCFs,
+                        ALIAS           => $ObjectCFs,
                         FIELD           => 'Content',
                         OPERATOR        => '=',
                         VALUE           => '',
                         ENTRYAGGREGATOR => 'OR'
                     );
                     $self->_SQLLimit(
-                        ALIAS           => $TicketCFs,
+                        ALIAS           => $ObjectCFs,
                         FIELD           => 'Content',
                         OPERATOR        => 'IS',
                         VALUE           => 'NULL',
@@ -1727,7 +1727,7 @@ sub _CustomFieldLimit {
                     );
                     $self->_CloseParen;
                     $self->_SQLLimit( $fix_op->(
-                        ALIAS           => $TicketCFs,
+                        ALIAS           => $ObjectCFs,
                         FIELD           => 'LargeContent',
                         OPERATOR        => $op,
                         VALUE           => $value,
@@ -1738,7 +1738,7 @@ sub _CustomFieldLimit {
             }
             else {
                 $self->_SQLLimit(
-                    ALIAS    => $TicketCFs,
+                    ALIAS    => $ObjectCFs,
                     FIELD    => 'Content',
                     OPERATOR => $op,
                     VALUE    => $value,
@@ -1749,14 +1749,14 @@ sub _CustomFieldLimit {
                 $self->_OpenParen;
                 $self->_OpenParen;
                 $self->_SQLLimit(
-                    ALIAS           => $TicketCFs,
+                    ALIAS           => $ObjectCFs,
                     FIELD           => 'Content',
                     OPERATOR        => '=',
                     VALUE           => '',
                     ENTRYAGGREGATOR => 'OR'
                 );
                 $self->_SQLLimit(
-                    ALIAS           => $TicketCFs,
+                    ALIAS           => $ObjectCFs,
                     FIELD           => 'Content',
                     OPERATOR        => 'IS',
                     VALUE           => 'NULL',
@@ -1764,7 +1764,7 @@ sub _CustomFieldLimit {
                 );
                 $self->_CloseParen;
                 $self->_SQLLimit( $fix_op->(
-                    ALIAS           => $TicketCFs,
+                    ALIAS           => $ObjectCFs,
                     FIELD           => 'LargeContent',
                     OPERATOR        => $op,
                     VALUE           => $value,
@@ -1798,7 +1798,7 @@ sub _CustomFieldLimit {
 
             if ($negative_op) {
                 $self->_SQLLimit(
-                    ALIAS           => $TicketCFs,
+                    ALIAS           => $ObjectCFs,
                     FIELD           => $column || 'Content',
                     OPERATOR        => 'IS',
                     VALUE           => 'NULL',
@@ -1812,7 +1812,7 @@ sub _CustomFieldLimit {
     }
     else {
         $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
-        my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
 
         # reverse operation
         $op =~ s/!|NOT\s+//i;
@@ -1821,8 +1821,8 @@ sub _CustomFieldLimit {
         # otherwise search in Content and in LargeContent
         if ( $column ) {
             $self->SUPER::Limit( $fix_op->(
-                LEFTJOIN   => $TicketCFs,
-                ALIAS      => $TicketCFs,
+                LEFTJOIN   => $ObjectCFs,
+                ALIAS      => $ObjectCFs,
                 FIELD      => $column,
                 OPERATOR   => $op,
                 VALUE      => $value,
@@ -1831,8 +1831,8 @@ sub _CustomFieldLimit {
         }
         else {
             $self->SUPER::Limit(
-                LEFTJOIN   => $TicketCFs,
-                ALIAS      => $TicketCFs,
+                LEFTJOIN   => $ObjectCFs,
+                ALIAS      => $ObjectCFs,
                 FIELD      => 'Content',
                 OPERATOR   => $op,
                 VALUE      => $value,
@@ -1841,7 +1841,7 @@ sub _CustomFieldLimit {
         }
         $self->_SQLLimit(
             %rest,
-            ALIAS      => $TicketCFs,
+            ALIAS      => $ObjectCFs,
             FIELD      => 'id',
             OPERATOR   => 'IS',
             VALUE      => 'NULL',
@@ -1955,7 +1955,7 @@ sub OrderByCols {
            my ($queue, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
            my $cfkey = $cf_obj ? $cf_obj->id : "$queue.$field";
            $cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1;
-           my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field );
+           my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field );
            # this is described in _CustomFieldLimit
            $self->_SQLLimit(
                ALIAS      => $CFs,
@@ -1977,7 +1977,7 @@ sub OrderByCols {
            }
            my $CFvs = $self->Join(
                TYPE   => 'LEFT',
-               ALIAS1 => $TicketCFs,
+               ALIAS1 => $ObjectCFs,
                FIELD1 => 'CustomField',
                TABLE2 => 'CustomFieldValues',
                FIELD2 => 'CustomField',
@@ -1986,12 +1986,12 @@ sub OrderByCols {
                LEFTJOIN        => $CFvs,
                FIELD           => 'Name',
                QUOTEVALUE      => 0,
-               VALUE           => $TicketCFs . ".Content",
+               VALUE           => $ObjectCFs . ".Content",
                ENTRYAGGREGATOR => 'AND'
            );
 
            push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' };
-           push @res, { %$row, ALIAS => $TicketCFs, FIELD => 'Content' };
+           push @res, { %$row, ALIAS => $ObjectCFs, FIELD => 'Content' };
        } elsif ( $field eq "Custom" && $subkey eq "Ownership") {
            # PAW logic is "reversed"
            my $order = "ASC";

commit 05840351010103a861e883a76ff863bb1cfb3ae3
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 27 15:25:46 2013 -0700

    Decipher TicketSQL CF identifiers using a passed in LookupType
    
    The LookupType is necessary to find the correct CF by name, as well as
    to support the arcane CF.ObjectId.{CFName} syntax, for arbitrary object
    types (not just ticket CFs on RT::Queue).
    
    Potential syntax made possible, for example:
    
        TxnCF.{CFName}
        TxnCF.Queue.{CFName}
        ArticleCF.Class.{CFName}
    
    The CF lookup behaviour of CF.Queue.{CFName} changes slightly due to
    changes in which custom fields API is used.  Most notably, such syntax
    previously exhibited the following behaviour but no longer does:
    
        • CF names were searched case insensitively.
        • Disabled CFs could be found if no enabled CFs matched.
        • The existence of multiple matching CFs with the same name (& within
          the same queue) was ignored; the first was the only one used.
    
    As CF.Queue.{CFName} is fairly arcane syntax, these changes shouldn't
    affect most usage of TicketSQL.  It is unlikely that anyone using the
    extended syntax is intentionally relying on any of the discontinued
    behaviour noted above.

diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 679186b..102b38d 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1314,33 +1314,49 @@ sub _WatcherMembershipLimit {
 
 Try and turn a CF descriptor into (cfid, cfname) object pair.
 
+Takes an optional second parameter of the CF LookupType, defaults to Ticket CFs.
+
 =cut
 
 sub _CustomFieldDecipher {
-    my ($self, $string) = @_;
+    my ($self, $string, $lookuptype) = @_;
+    $lookuptype ||= $self->_SingularClass->CustomFieldLookupType;
 
-    my ($queue, $field, $column) = ($string =~ /^(?:(.+?)\.)?{(.+)}(?:\.(Content|LargeContent))?$/);
+    my ($object, $field, $column) = ($string =~ /^(?:(.+?)\.)?{(.+)}(?:\.(Content|LargeContent))?$/);
     $field ||= ($string =~ /^{(.*?)}$/)[0] || $string;
 
-    my $cf;
-    if ( $queue ) {
-        my $q = RT::Queue->new( $self->CurrentUser );
-        $q->Load( $queue );
+    my ($cf, $applied_to);
+
+    if ( $object ) {
+        my $record_class = RT::CustomField->RecordClassFromLookupType($lookuptype);
+        $applied_to = $record_class->new( $self->CurrentUser );
+        $applied_to->Load( $object );
 
-        if ( $q->id ) {
-            # $queue = $q->Name; # should we normalize the queue?
-            $cf = $q->CustomField( $field );
+        if ( $applied_to->id ) {
+            RT->Logger->debug("Limiting to CFs identified by '$field' applied to $record_class #@{[$applied_to->id]} (loaded via '$object')");
         }
         else {
-            $RT::Logger->warning("Queue '$queue' doesn't exist, parsed from '$string'");
-            $queue = 0;
+            RT->Logger->warning("$record_class '$object' doesn't exist, parsed from '$string'");
+            $object = 0;
+            undef $applied_to;
         }
     }
-    elsif ( $field =~ /\D/ ) {
-        $queue = '';
+
+    if ( $field =~ /\D/ ) {
+        $object ||= '';
         my $cfs = RT::CustomFields->new( $self->CurrentUser );
         $cfs->Limit( FIELD => 'Name', VALUE => $field );
-        $cfs->LimitToLookupType('RT::Queue-RT::Ticket');
+        $cfs->LimitToLookupType($lookuptype);
+        # XXX Compare to RT::CustomField::LoadByNameAndQueue, previously used
+        # when a specific applied to object (queue) was specified.
+        #   - Explicit case insensitivity
+        #   - Disabled OK
+        #   - No duplicate check
+
+        if ($applied_to) {
+            $cfs->SetContextObject($applied_to);
+            $cfs->LimitToObjectId($applied_to->id);
+        }
 
         # if there is more then one field the current user can
         # see with the same name then we shouldn't return cf object
@@ -1353,9 +1369,11 @@ sub _CustomFieldDecipher {
     else {
         $cf = RT::CustomField->new( $self->CurrentUser );
         $cf->Load( $field );
+        $cf->SetContextObject($applied_to)
+            if $cf->id and $applied_to;
     }
 
-    return ($queue, $field, $cf, $column);
+    return ($object, $field, $cf, $column);
 }
 
 =head2 _CustomFieldJoin
@@ -1477,8 +1495,8 @@ sub _CustomFieldLimit {
 
     # For our sanity, we can only limit on one queue at a time
 
-    my ($queue, $cfid, $cf, $column);
-    ($queue, $field, $cf, $column) = $self->_CustomFieldDecipher( $field );
+    my ($object, $cfid, $cf, $column);
+    ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $field );
     $cfid = $cf ? $cf->id  : 0 ;
 
 # If we're trying to find custom fields that don't match something, we
@@ -1574,7 +1592,7 @@ sub _CustomFieldLimit {
 
     my $single_value = !$cf || !$cfid || $cf->SingleValue;
 
-    my $cfkey = $cfid ? $cfid : "$queue.$field";
+    my $cfkey = $cfid ? $cfid : "$object.$field";
 
     if ( $null_op && !$column ) {
         # IS[ NOT] NULL without column is the same as has[ no] any CF value,
@@ -1952,8 +1970,8 @@ sub OrderByCols {
             }
             push @res, { %$row, ALIAS => $users, FIELD => $subkey };
        } elsif ( defined $meta->[0] && $meta->[0] eq 'CUSTOMFIELD' ) {
-           my ($queue, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
-           my $cfkey = $cf_obj ? $cf_obj->id : "$queue.$field";
+           my ($object, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey );
+           my $cfkey = $cf_obj ? $cf_obj->id : "$object.$field";
            $cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1;
            my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field );
            # this is described in _CustomFieldLimit

commit 333337eb7269ed0e3dbc348c6a7e251a45a9bef4
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jun 26 16:47:43 2013 -0700

    Refactor _CustomFieldJoin to take a LookupType
    
    … so that we can join to the correct table if not Tickets.  An
    extensible data structure is introduced to determine how to join CFs to
    applied objects.
    
    This lets _CustomFieldJoin handle joining for transaction CFs or other
    CFs which may want to be searched.

diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 102b38d..709f985 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1382,8 +1382,14 @@ Factor out the Join of custom fields so we can use it for sorting too
 
 =cut
 
+our %JOIN_ALIAS_FOR_LOOKUP_TYPE = (
+    RT::Ticket->CustomFieldLookupType      => sub { "main" },
+);
+
 sub _CustomFieldJoin {
-    my ($self, $cfkey, $cfid, $field) = @_;
+    my ($self, $cfkey, $cfid, $field, $type) = @_;
+    $type ||= RT::Ticket->CustomFieldLookupType;
+
     # Perform one Join per CustomField
     if ( $self->{_sql_object_cfv_alias}{$cfkey} ||
          $self->{_sql_cf_alias}{$cfkey} )
@@ -1392,11 +1398,15 @@ sub _CustomFieldJoin {
                  $self->{_sql_cf_alias}{$cfkey} );
     }
 
+    my $ObjectAlias = $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type}
+        ? $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type}->($self)
+        : die "We don't know how to join on $type";
+
     my ($ObjectCFs, $CFs);
     if ( $cfid ) {
         $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join(
             TYPE   => 'LEFT',
-            ALIAS1 => 'main',
+            ALIAS1 => $ObjectAlias,
             FIELD1 => 'id',
             TABLE2 => 'ObjectCustomFieldValues',
             FIELD2 => 'ObjectId',
@@ -1434,7 +1444,7 @@ sub _CustomFieldJoin {
             LEFTJOIN        => $CFs,
             ENTRYAGGREGATOR => 'AND',
             FIELD           => 'LookupType',
-            VALUE           => 'RT::Queue-RT::Ticket',
+            VALUE           => $type,
         );
         $self->SUPER::Limit(
             LEFTJOIN        => $CFs,
@@ -1453,15 +1463,16 @@ sub _CustomFieldJoin {
         $self->SUPER::Limit(
             LEFTJOIN        => $ObjectCFs,
             FIELD           => 'ObjectId',
-            VALUE           => 'main.id',
+            VALUE           => "$ObjectAlias.id",
             QUOTEVALUE      => 0,
             ENTRYAGGREGATOR => 'AND',
         );
     }
+
     $self->SUPER::Limit(
         LEFTJOIN        => $ObjectCFs,
         FIELD           => 'ObjectType',
-        VALUE           => 'RT::Ticket',
+        VALUE           => RT::CustomField->ObjectTypeFromLookupType($type),
         ENTRYAGGREGATOR => 'AND'
     );
     $self->SUPER::Limit(

commit 55ce2de15a2472876066b473a0598fc6ecca2c48
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 27 15:50:34 2013 -0700

    Refactor _CustomFieldLimit to use a LookupType in %FIELD_METADATA
    
    Potential new search clauses, such as searching transaction CFs, want to
    match all the different ways that ticket CFs can.  It makes sense to
    just wrap the limits in a LookupType change (especially since
    _CustomFieldLimit happens to be recursive for IP and Date searches).

diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 709f985..3b614e6 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -142,9 +142,9 @@ our %FIELD_METADATA = (
     QueueCc          => [ 'WATCHERFIELD'    => 'Cc'      => 'Queue', ], #loc_left_pair
     QueueAdminCc     => [ 'WATCHERFIELD'    => 'AdminCc' => 'Queue', ], #loc_left_pair
     QueueWatcher     => [ 'WATCHERFIELD'    => undef     => 'Queue', ], #loc_left_pair
-    CustomFieldValue => [ 'CUSTOMFIELD', ], #loc_left_pair
-    CustomField      => [ 'CUSTOMFIELD', ], #loc_left_pair
-    CF               => [ 'CUSTOMFIELD', ], #loc_left_pair
+    CustomFieldValue => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair
+    CustomField      => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair
+    CF               => [ 'CUSTOMFIELD' => 'Ticket' ], #loc_left_pair
     Updated          => [ 'TRANSDATE', ], #loc_left_pair
     RequestorGroup   => [ 'MEMBERSHIPFIELD' => 'Requestor', ], #loc_left_pair
     CCGroup          => [ 'MEMBERSHIPFIELD' => 'Cc', ], #loc_left_pair
@@ -1502,12 +1502,16 @@ use Regexp::Common::net::CIDR;
 sub _CustomFieldLimit {
     my ( $self, $_field, $op, $value, %rest ) = @_;
 
+    my $meta  = $FIELD_METADATA{ $_field };
+    my $class = $meta->[1] || 'Ticket';
+    my $type  = "RT::$class"->CustomFieldLookupType;
+
     my $field = $rest{'SUBKEY'} || die "No field specified";
 
     # For our sanity, we can only limit on one queue at a time
 
     my ($object, $cfid, $cf, $column);
-    ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $field );
+    ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $field, $type );
     $cfid = $cf ? $cf->id  : 0 ;
 
 # If we're trying to find custom fields that don't match something, we
@@ -1603,13 +1607,13 @@ sub _CustomFieldLimit {
 
     my $single_value = !$cf || !$cfid || $cf->SingleValue;
 
-    my $cfkey = $cfid ? $cfid : "$object.$field";
+    my $cfkey = $cfid ? $cfid : "$type-$object.$field";
 
     if ( $null_op && !$column ) {
         # IS[ NOT] NULL without column is the same as has[ no] any CF value,
         # we can reuse our default joins for this operation
         # with column specified we have different situation
-        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
         $self->_OpenParen;
         $self->_SQLLimit(
             ALIAS    => $ObjectCFs,
@@ -1635,11 +1639,11 @@ sub _CustomFieldLimit {
         $self->_OpenParen;
         if ( $op !~ /NOT|!=|<>/i ) { # positive equation
             $self->_CustomFieldLimit(
-                'CF', '<=', $end_ip, %rest,
+                $_field, '<=', $end_ip, %rest,
                 SUBKEY => $rest{'SUBKEY'}. '.Content',
             );
             $self->_CustomFieldLimit(
-                'CF', '>=', $start_ip, %rest,
+                $_field, '>=', $start_ip, %rest,
                 SUBKEY          => $rest{'SUBKEY'}. '.LargeContent',
                 ENTRYAGGREGATOR => 'AND',
             ); 
@@ -1647,20 +1651,20 @@ sub _CustomFieldLimit {
             # estimations and scan less rows
 # have to disable this tweak because of ipv6
 #            $self->_CustomFieldLimit(
-#                $field, '>=', '000.000.000.000', %rest,
+#                $_field, '>=', '000.000.000.000', %rest,
 #                SUBKEY          => $rest{'SUBKEY'}. '.Content',
 #                ENTRYAGGREGATOR => 'AND',
 #            );
 #            $self->_CustomFieldLimit(
-#                $field, '<=', '255.255.255.255', %rest,
+#                $_field, '<=', '255.255.255.255', %rest,
 #                SUBKEY          => $rest{'SUBKEY'}. '.LargeContent',
 #                ENTRYAGGREGATOR => 'AND',
 #            );  
         }       
         else { # negative equation
-            $self->_CustomFieldLimit($field, '>', $end_ip, %rest);
+            $self->_CustomFieldLimit($_field, '>', $end_ip, %rest);
             $self->_CustomFieldLimit(
-                $field, '<', $start_ip, %rest,
+                $_field, '<', $start_ip, %rest,
                 SUBKEY          => $rest{'SUBKEY'}. '.LargeContent',
                 ENTRYAGGREGATOR => 'OR',
             );  
@@ -1672,7 +1676,7 @@ sub _CustomFieldLimit {
     } 
     elsif ( !$negative_op || $single_value ) {
         $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if !$single_value && !$range_op;
-        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
 
         $self->_OpenParen;
 
@@ -1841,7 +1845,7 @@ sub _CustomFieldLimit {
     }
     else {
         $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++;
-        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+        my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type );
 
         # reverse operation
         $op =~ s/!|NOT\s+//i;

commit 09ae727d7729d71a198fdf55bd00bc7695f4ea12
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jun 27 16:40:57 2013 -0700

    Fix horrendous load order of shredder tests
    
    They started failing because of an added
    RT::Ticket->CustomFieldLookupType call.
    
    The RT::Ticket load order is also awful and doesn't setup it's base
    class until very late, including after importing other packages.

diff --git a/t/shredder/utils.pl b/t/shredder/utils.pl
index 7be9513..d5a0589 100644
--- a/t/shredder/utils.pl
+++ b/t/shredder/utils.pl
@@ -8,8 +8,14 @@ require RT::Test;
 
 BEGIN {
 ### after:     push @INC, qw(@RT_LIB_PATH@);
+
+    use RT;
+    RT->LoadConfig;
+    RT->InitPluginPaths;
+    RT->InitClasses;
 }
-use RT::Shredder;
+
+require RT::Shredder;
 
 =head1 DESCRIPTION
 

commit dc545c3ce9065f44aba13baf6dc2d682265d8295
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 28 10:10:49 2013 -0700

    Add callbacks for before and after the query builder criteria picker

diff --git a/share/html/Search/Elements/PickCriteria b/share/html/Search/Elements/PickCriteria
index 7d8a4e9..a15a21a 100644
--- a/share/html/Search/Elements/PickCriteria
+++ b/share/html/Search/Elements/PickCriteria
@@ -50,9 +50,10 @@
 <table width="100%" cellspacing="0" cellpadding="0" border="0">
 
 
-
+% $m->callback( %ARGS, CallbackName => "BeforeBasics" );
 <& PickBasics, queues => \%queues &>
 <& PickCFs, queues => \%queues &>
+% $m->callback( %ARGS, CallbackName => "AfterCFs" );
 
 <tr class="separator"><td colspan="3"><hr /></td></tr>
 <tr>

commit 7928d2d0d24c173ff3345abc9fb9bf5e251931e6
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 28 10:11:38 2013 -0700

    PickTicketCFs refactored from the now-generic PickCFs
    
    PickCFs may now be used for other types of CFs.  Callback semantics
    remain the same, so no extension using the callbacks in PickCFs will be
    broken.

diff --git a/share/html/Search/Elements/PickCFs b/share/html/Search/Elements/PickCFs
index 5d16b9c..9765603 100644
--- a/share/html/Search/Elements/PickCFs
+++ b/share/html/Search/Elements/PickCFs
@@ -49,14 +49,6 @@
 <& ConditionRow, Condition => $_ &>
 % }
 <%INIT>
-my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
-foreach my $id (keys %queues) {
-    # Gotta load up the $queue object, since queues get stored by name now.
-    my $queue = RT::Queue->new($session{'CurrentUser'});
-    $queue->Load($id);
-    $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
-}
-$CustomFields->LimitToGlobal;
 $m->callback(
     CallbackName => 'MassageCustomFields',
     CustomFields => $CustomFields,
@@ -66,7 +58,7 @@ $m->callback(
 my @lines;
 while ( my $CustomField = $CustomFields->Next ) {
     my %line;
-    $line{'Name'} = "'CF.{" . $CustomField->Name . "}'";
+    $line{'Name'} = "'$TicketSQLField.{" . $CustomField->Name . "}'";
     $line{'Field'} = $CustomField->Name;
 
     # Op
@@ -120,4 +112,6 @@ $m->callback( Conditions => \@lines, Queues => \%queues );
 
 <%ARGS>
 %queues => ()
+$CustomFields
+$TicketSQLField => 'CF'
 </%ARGS>
diff --git a/share/html/Search/Elements/PickCriteria b/share/html/Search/Elements/PickCriteria
index a15a21a..485fceb 100644
--- a/share/html/Search/Elements/PickCriteria
+++ b/share/html/Search/Elements/PickCriteria
@@ -52,7 +52,7 @@
 
 % $m->callback( %ARGS, CallbackName => "BeforeBasics" );
 <& PickBasics, queues => \%queues &>
-<& PickCFs, queues => \%queues &>
+<& PickTicketCFs, queues => \%queues &>
 % $m->callback( %ARGS, CallbackName => "AfterCFs" );
 
 <tr class="separator"><td colspan="3"><hr /></td></tr>
diff --git a/share/html/Search/Elements/PickCriteria b/share/html/Search/Elements/PickTicketCFs
similarity index 79%
copy from share/html/Search/Elements/PickCriteria
copy to share/html/Search/Elements/PickTicketCFs
index a15a21a..6b01aa2 100644
--- a/share/html/Search/Elements/PickCriteria
+++ b/share/html/Search/Elements/PickTicketCFs
@@ -45,29 +45,17 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<&| /Widgets/TitleBox, title => loc('Add Criteria')&>
-
-<table width="100%" cellspacing="0" cellpadding="0" border="0">
-
-
-% $m->callback( %ARGS, CallbackName => "BeforeBasics" );
-<& PickBasics, queues => \%queues &>
-<& PickCFs, queues => \%queues &>
-% $m->callback( %ARGS, CallbackName => "AfterCFs" );
-
-<tr class="separator"><td colspan="3"><hr /></td></tr>
-<tr>
-<td class="label"><&|/l&>Aggregator</&></td>
-<td class="operator" colspan="2"><& SelectAndOr, Name => "AndOr" &></td>
-
-</tr>
-
-</table>
-
-</&>
-
 <%ARGS>
-$addquery => 0
-$query => undef
 %queues => ()
 </%ARGS>
+<%init>
+my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
+foreach my $id (keys %queues) {
+    # Gotta load up the $queue object, since queues get stored by name now.
+    my $queue = RT::Queue->new($session{'CurrentUser'});
+    $queue->Load($id);
+    $CustomFields->LimitToQueue($queue->Id) if $queue->Id;
+}
+$CustomFields->LimitToGlobal;
+</%init>
+<& PickCFs, %ARGS, TicketSQLField => 'CF', CustomFields => $CustomFields &>

commit ef1eae9b27d1f0e2e94bd4cfc357602257b7aae1
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Jun 28 12:14:27 2013 -0700

    Parse FooCF.{Bar} when adding a TicketSQL clause in the Query Builder
    
    Extends the parsing from a strict CF.{…} to SomeOptionalWordCF.{…}
    
    In an ideal world we programmatically build up what's recognized from a
    data structure like %RT::Tickets::FIELD_METADATA, but that requires more
    refactoring than we're up for in a stable series.

diff --git a/share/html/Search/Build.html b/share/html/Search/Build.html
index e377622..40b45d4 100644
--- a/share/html/Search/Build.html
+++ b/share/html/Search/Build.html
@@ -190,7 +190,7 @@ my @new_values = ();
 
 # Try to find if we're adding a clause
 foreach my $arg ( keys %ARGS ) {
-    next unless $arg =~ m/^ValueOf(\w+|'CF.{.*?}')$/
+    next unless $arg =~ m/^ValueOf(\w+|'\w*CF\.\{.*?\}')$/
                 && ( ref $ARGS{$arg} eq "ARRAY"
                      ? grep $_ ne '', @{ $ARGS{$arg} }
                      : $ARGS{$arg} ne '' );
@@ -234,10 +234,10 @@ foreach my $arg ( keys %ARGS ) {
             $value = "'$value'";
         }
 
-        if ($keyword =~ /^'CF\.{(.*)}'/) {
-            my $cf = $1;
+        if ($keyword =~ /^'(\w*CF)\.\{(.*)\}'/) {
+            my ($field, $cf) = ($1, $2);
             $cf =~ s/(['\\])/\\$1/g;
-            $keyword = "'CF.{$cf}'";
+            $keyword = "'$field.{$cf}'";
         }
 
         my $clause = {

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


More information about the Rt-commit mailing list