[Rt-commit] r4551 - in rt/branches/CHALDEA-EXPERIMENTAL: lib/RT

jesse at bestpractical.com jesse at bestpractical.com
Thu Feb 16 21:29:48 EST 2006


Author: jesse
Date: Thu Feb 16 21:29:47 2006
New Revision: 4551

Modified:
   rt/branches/CHALDEA-EXPERIMENTAL/   (props changed)
   rt/branches/CHALDEA-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm

Log:
 r23826 at truegrounds:  jesse | 2006-02-16 18:25:05 -0800
 * Backport tickets_overlay from 3.5. Fix a bug that stopped search on global custom fields


Modified: rt/branches/CHALDEA-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm
==============================================================================
--- rt/branches/CHALDEA-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm	(original)
+++ rt/branches/CHALDEA-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm	Thu Feb 16 21:29:47 2006
@@ -316,8 +316,8 @@
 Handle fields which deal with links between tickets.  (MemberOf, DependsOn)
 
 Meta Data:
-  1: Direction (From,To)
-  2: Link Type (MemberOf, DependsOn,RefersTo)
+  1: Direction (From, To)
+  2: Link Type (MemberOf, DependsOn, RefersTo)
 
 =cut
 
@@ -325,17 +325,15 @@
     my ( $sb, $field, $op, $value, @rest ) = @_;
 
     my $meta = $FIELD_METADATA{$field};
-    die "Invalid Operator $op for $field" unless $op =~ /^(=|!=|IS)/io;
-
     die "Incorrect Metadata for $field"
-        unless ( defined $meta->[1] and defined $meta->[2] );
+        unless defined $meta->[1] && defined $meta->[2];
+
+    die "Invalid Operator $op for $field" unless $op =~ /^(=|!=|IS|IS NOT)$/io;
 
     my $direction = $meta->[1];
 
     my $matchfield;
     my $linkfield;
-    my $is_local = 1;
-    my $is_null  = 0;
     if ( $direction eq 'To' ) {
         $matchfield = "Target";
         $linkfield  = "Base";
@@ -350,84 +348,105 @@
         die "Invalid link direction '$meta->[1]' for $field\n";
     }
 
-    if ( $op eq '=' || $op =~ /^is/oi ) {
-        if ( $value eq '' || $value =~ /^null$/io ) {
-            $is_null = 1;
-        }
-        elsif ( $value =~ /\D/o ) {
-            $is_local = 0;
-        }
-        else {
-            $is_local = 1;
-        }
+    my ($is_local, $is_null) = (1, 0);
+    if ( !$value || $value =~ /^null$/io ) {
+        $is_null = 1;
+        $op = ($op =~ /^(=|IS)$/)? 'IS': 'IS NOT';
+    }
+    elsif ( $value =~ /\D/o ) {
+        $is_local = 0;
+    }
+    $matchfield = "Local$matchfield" if $is_local;
+
+    my $is_negative = 0;
+    if ( $op eq '!=' ) {
+        $is_negative = 1;
+        $op = '=';
     }
 
 #For doing a left join to find "unlinked tickets" we want to generate a query that looks like this
 #    SELECT main.* FROM Tickets main
 #        LEFT JOIN Links Links_1 ON (     (Links_1.Type = 'MemberOf')
 #                                      AND(main.id = Links_1.LocalTarget))
-#        WHERE   ((main.EffectiveId = main.id))
-#            AND ((main.Status != 'deleted'))
-#            AND (Links_1.LocalBase IS NULL);
+#        WHERE Links_1.LocalBase IS NULL;
 
     if ($is_null) {
         my $linkalias = $sb->Join(
-            TYPE   => 'left',
+            TYPE   => 'LEFT',
             ALIAS1 => 'main',
             FIELD1 => 'id',
             TABLE2 => 'Links',
             FIELD2 => 'Local' . $linkfield
         );
-
         $sb->SUPER::Limit(
             LEFTJOIN => $linkalias,
             FIELD    => 'Type',
             OPERATOR => '=',
             VALUE    => $meta->[2],
+        );
+        $sb->_SQLLimit(
             @rest,
+            ALIAS      => $linkalias,
+            FIELD      => $matchfield,
+            OPERATOR   => $op,
+            VALUE      => 'NULL',
+            QUOTEVALUE => 0,
+        );
+    }
+    elsif ( $is_negative ) {
+        my $linkalias = $sb->Join(
+            TYPE   => 'LEFT',
+            ALIAS1 => 'main',
+            FIELD1 => 'id',
+            TABLE2 => 'Links',
+            FIELD2 => 'Local' . $linkfield
+        );
+        $sb->SUPER::Limit(
+            LEFTJOIN => $linkalias,
+            FIELD    => 'Type',
+            OPERATOR => '=',
+            VALUE    => $meta->[2],
+        );
+        $sb->SUPER::Limit(
+            LEFTJOIN => $linkalias,
+            FIELD    => $matchfield,
+            OPERATOR => $op,
+            VALUE    => $value,
         );
-
         $sb->_SQLLimit(
-            ALIAS           => $linkalias,
-            ENTRYAGGREGATOR => 'AND',
-            FIELD      => ( $is_local ? "Local$matchfield" : $matchfield ),
+            @rest,
+            ALIAS      => $linkalias,
+            FIELD      => $matchfield,
             OPERATOR   => 'IS',
             VALUE      => 'NULL',
-            QUOTEVALUE => '0',
+            QUOTEVALUE => 0,
         );
-
     }
     else {
-
-        $sb->{_sql_linkalias} = $sb->NewAlias('Links')
-            unless defined $sb->{_sql_linkalias};
-
+        my $linkalias = $sb->NewAlias('Links');
         $sb->_OpenParen();
-
         $sb->_SQLLimit(
-            ALIAS    => $sb->{_sql_linkalias},
+            @rest,
+            ALIAS    => $linkalias,
             FIELD    => 'Type',
             OPERATOR => '=',
             VALUE    => $meta->[2],
-            @rest,
         );
-
         $sb->_SQLLimit(
-            ALIAS           => $sb->{_sql_linkalias},
+            ALIAS           => $linkalias,
+            FIELD           => 'Local' . $linkfield,
+            OPERATOR        => '=',
+            VALUE           => 'main.id',
+            QUOTEVALUE      => 0,
             ENTRYAGGREGATOR => 'AND',
-            FIELD    => ( $is_local ? "Local$matchfield" : $matchfield ),
-            OPERATOR => '=',
-            VALUE    => $value,
         );
-
-        #If we're searching on target, join the base to ticket.id
-        $sb->_SQLJoin(
-            ALIAS1 => 'main',
-            FIELD1 => $sb->{'primary_key'},
-            ALIAS2 => $sb->{_sql_linkalias},
-            FIELD2 => 'Local' . $linkfield
+        $sb->_SQLLimit(
+            ALIAS           => $linkalias,
+            FIELD           => $matchfield,
+            OPERATOR        => $op,
+            VALUE           => $value,
+            ENTRYAGGREGATOR => 'AND',
         );
-
         $sb->_CloseParen();
     }
 }
@@ -1142,64 +1161,62 @@
     }
 }
 
-=head2 KeywordLimit
 
-Limit based on Keywords
 
-Meta Data:
-  none
+=head2 _CustomFieldDecipher
+ 
+Try and turn a CF descriptor into (cfid, cfname) object pair.
+ 
 
 =cut
 
-sub _CustomFieldLimit {
-    my ( $self, $_field, $op, $value, @rest ) = @_;
-
-    my %rest  = @rest;
-    my $field = $rest{SUBKEY} || die "No field specified";
-
-    # For our sanity, we can only limit on one queue at a time
-    my $queue = 0;
-
-    if ( $field =~ /^(.+?)\.{(.+)}$/ ) {
-        $queue = $1;
-        $field = $2;
-    }
-    $field = $1 if $field =~ /^{(.+)}$/;    # trim { }
-
-    # If we're trying to find custom fields that don't match something, we
-    # want tickets where the custom field has no value at all.  Note that
-    # we explicitly don't include the "IS NULL" case, since we would
-    # otherwise end up with a redundant clause.
-
-    my $null_columns_ok;
-    if ( ( $op =~ /^NOT LIKE$/i ) or ( $op eq '!=' ) ) {
-        $null_columns_ok = 1;
-    }
-
-    my $cfid = 0;
-    if ($queue) {
-
-        my $q = RT::Queue->new( $self->CurrentUser );
-        $q->Load($queue) if ($queue);
-
-        my $cf;
-        if ( $q->id ) {
-            $cf = $q->CustomField($field);
-        }
-        else {
-            $cf = RT::CustomField->new( $self->CurrentUser );
-            $cf->LoadByNameAndQueue( Queue => '0', Name => $field );
-        }
-
-        $cfid = $cf->id;
-
+sub _CustomFieldDecipher {
+  my ($self, $field) = @_;
+ 
+  my $queue = 0;
+  if ( $field =~ /^(.+?)\.{(.+)}$/ ) {
+    $queue = $1;
+    $field = $2;
+ }
+  $field = $1 if $field =~ /^{(.+)}$/;    # trim { }
+ 
+  my $cfid;
+ 
+  if ($queue) {
+    my $q = RT::Queue->new( $self->CurrentUser );
+    $q->Load($queue) if ($queue);
+ 
+    my $cf;
+    if ( $q->id ) {
+      # $queue = $q->Name; # should we normalize the queue?
+      $cf = $q->CustomField($field);
     }
+    else {
+      $cf = RT::CustomField->new( $self->CurrentUser );
+      $cf->LoadByNameAndQueue( Queue => '0', Name => $field );
+     }
+    $cfid = $cf->id if $cf;
+  }
+ 
+  return ($queue, $field, $cfid);
+ 
+}
+ 
+
+ 
+=head2 _CustomFieldJoin
+ 
+Factor out the Join of custom fields so we can use it for sorting too
 
-    my $TicketCFs;
-    my $CFs;
-    my $cfkey = $cfid ? $cfid : "$queue.$field";
+=cut
 
+sub _CustomFieldJoin {
+  my ($self, $cfkey, $cfid, $field) = @_;
+ 
+  my $TicketCFs;
+  my $CFs;
     # Perform one Join per CustomField
+
     if ( $self->{_sql_object_cf_alias}{$cfkey} ) {
         $TicketCFs = $self->{_sql_object_cf_alias}{$cfkey};
     }
@@ -1226,6 +1243,16 @@
                 TABLE2     => 'ObjectCustomFields',
                 FIELD2     => 'ObjectId',
             );
+
+            $self->SUPER::Limit(
+                    LEFTJOIN => $ocfalias,
+                    ENTRYAGGREGATOR => 'OR',
+                    FIELD => 'ObjectId',
+                    VALUE => '0',
+
+                    );
+
+
             $CFs = $self->Join(
                 TYPE       => 'left',
                 ALIAS1     => $ocfalias,
@@ -1264,16 +1291,52 @@
         );
     }
 
-    $self->_OpenParen;
+  return ($TicketCFs, $CFs);
+}
 
-    $self->SUPER::Limit(
-        ALIAS           => $CFs,
-        FIELD           => 'name',
-        VALUE           => $field,
-        ENTRYAGGREGATOR => 'AND',
-    );
+=head2 _CustomFieldLimit
+
+Limit based on CustomFields
+
+Meta Data:
+  none
+
+=cut
+
+sub _CustomFieldLimit {
+    my ( $self, $_field, $op, $value, @rest ) = @_;
+
+    my %rest  = @rest;
+    my $field = $rest{SUBKEY} || die "No field specified";
+
+    # For our sanity, we can only limit on one queue at a time
+
+    my ($queue, $field, $cfid ) = $self->_CustomFieldDecipher( $field );
+
+# If we're trying to find custom fields that don't match something, we
+# want tickets where the custom field has no value at all.  Note that
+# we explicitly don't include the "IS NULL" case, since we would
+# otherwise end up with a redundant clause.
+
+    my $null_columns_ok;
+    if ( ( $op =~ /^NOT LIKE$/i ) or ( $op eq '!=' ) ) {
+        $null_columns_ok = 1;
+    }
+
+    my $cfkey = $cfid ? $cfid : "$queue.$field";
+    my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+
+     $self->_OpenParen;
+
+     $self->SUPER::Limit(
+         ALIAS           => $CFs,
+         FIELD           => 'name',
+         VALUE           => $field,
+         ENTRYAGGREGATOR => 'AND',
+     );
+
+     $self->_OpenParen if $null_columns_ok;
 
-    $self->_OpenParen if $null_columns_ok;
     $self->_SQLLimit(
         ALIAS      => $TicketCFs,
         FIELD      => 'Content',
@@ -1324,12 +1387,66 @@
             push @res, $row;
             next;
         }
-        my ( $field, $subkey ) = split /\./, $row->{FIELD};
+        my ( $field, $subkey ) = split /\./, $row->{FIELD}, 2;
         my $meta = $self->FIELDS->{$field};
         if ( $meta->[0] eq 'WATCHERFIELD' ) {
             my $users = $self->_WatcherJoin( $meta->[1], "order" . $order++ );
             push @res, { %$row, ALIAS => $users, FIELD => $subkey };
-        }
+        
+
+       } elsif ( $meta->[0] eq 'CUSTOMFIELD' ) {
+           my ($queue, $field, $cfid ) = $self->_CustomFieldDecipher( $subkey );
+           my $cfkey = $cfid ? $cfid : "$queue.$field";
+           my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
+           unless ($cfid) {
+             # For those cases where we are doing a join against the
+             # CF name, and don't have a CFid, use Unique to make sure
+             # we don't show duplicate tickets.  NOTE: I'm pretty sure
+             # this will stay mixed in for the life of the
+             # class/package, and not just for the life of the object.
+             # Potential performance issue.
+             require DBIx::SearchBuilder::Unique;
+             DBIx::SearchBuilder::Unique->import;
+           }
+           my $CFvs = $self->Join(
+                TYPE   => 'left',
+                ALIAS1 => $TicketCFs,
+                FIELD1 => 'CustomField',
+                TABLE2 => 'CustomFieldValues',
+                FIELD2 => 'CustomField',
+            );
+           $self->SUPER::Limit(
+                LEFTJOIN => $CFvs,
+                FIELD => 'Name',
+                QUOTEVALUE => 0,
+                VALUE => $TicketCFs . ".Content",
+                ENTRYAGGREGATOR => 'AND'
+                              );
+
+          push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' };
+          push @res, { %$row, ALIAS => $TicketCFs, FIELD => 'Content' };
+       } elsif ( $field eq "Custom" && $subkey eq "Ownership") {
+         # PAW logic is "reversed"
+         my $order = "ASC";
+         if (exists $row->{ORDER} ) {
+           my $o = $row->{ORDER};
+           delete $row->{ORDER};
+           $order = "DESC" if $o =~ /asc/i;
+         }
+
+         # Unowned
+         # Else
+
+         # Ticket.Owner  1 0 0
+         my $ownerId = $self->CurrentUser->Id;
+         push @res, { %$row, FIELD => "Owner=$ownerId", ORDER => $order } ;
+
+         # Unowned Tickets 0 1 0
+         my $nobodyId = $RT::Nobody->Id;
+         push @res, { %$row, FIELD => "Owner=$nobodyId", ORDER => $order } ;
+
+         push @res, { %$row, FIELD => "Priority", ORDER => $order } ;
+	}
         else {
             push @res, $row;
         }
@@ -1964,9 +2081,10 @@
 sub LimitLinkedTo {
     my $self = shift;
     my %args = (
-        TICKET => undef,
-        TARGET => undef,
-        TYPE   => undef,
+        TICKET   => undef,
+        TARGET   => undef,
+        TYPE     => undef,
+        OPERATOR => '=',
         @_
     );
 
@@ -1980,6 +2098,7 @@
             $self->loc( $args{'TYPE'} ),
             ( $args{'TARGET'} || $args{'TICKET'} )
         ),
+        OPERATOR    => $args{'OPERATOR'},
     );
 }
 
@@ -2002,9 +2121,10 @@
 sub LimitLinkedFrom {
     my $self = shift;
     my %args = (
-        BASE   => undef,
-        TICKET => undef,
-        TYPE   => undef,
+        BASE     => undef,
+        TICKET   => undef,
+        TYPE     => undef,
+        OPERATOR => '=',
         @_
     );
 
@@ -2026,6 +2146,7 @@
             $self->loc( $args{'TYPE'} ),
             ( $args{'BASE'} || $args{'TICKET'} )
         ),
+        OPERATOR    => $args{'OPERATOR'},
     );
 }
 
@@ -2035,11 +2156,11 @@
 sub LimitMemberOf {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedTo(
-        TARGET => "$ticket_id",
+    return $self->LimitLinkedTo(
+        @_,
+        TARGET => $ticket_id,
         TYPE   => 'MemberOf',
     );
-
 }
 
 # }}}
@@ -2048,7 +2169,8 @@
 sub LimitHasMember {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedFrom(
+    return $self->LimitLinkedFrom(
+        @_,
         BASE => "$ticket_id",
         TYPE => 'HasMember',
     );
@@ -2062,8 +2184,9 @@
 sub LimitDependsOn {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedTo(
-        TARGET => "$ticket_id",
+    return $self->LimitLinkedTo(
+        @_,
+        TARGET => $ticket_id,
         TYPE   => 'DependsOn',
     );
 
@@ -2076,8 +2199,9 @@
 sub LimitDependedOnBy {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedFrom(
-        BASE => "$ticket_id",
+    return $self->LimitLinkedFrom(
+        @_,
+        BASE => $ticket_id,
         TYPE => 'DependentOn',
     );
 
@@ -2090,8 +2214,9 @@
 sub LimitRefersTo {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedTo(
-        TARGET => "$ticket_id",
+    return $self->LimitLinkedTo(
+        @_,
+        TARGET => $ticket_id,
         TYPE   => 'RefersTo',
     );
 
@@ -2104,11 +2229,11 @@
 sub LimitReferredToBy {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedFrom(
-        BASE => "$ticket_id",
+    return $self->LimitLinkedFrom(
+        @_,
+        BASE => $ticket_id,
         TYPE => 'ReferredToBy',
     );
-
 }
 
 # }}}


More information about the Rt-commit mailing list