[Rt-commit] r4722 - in rt/branches/QUEBEC-EXPERIMENTAL: .

alexmv at bestpractical.com alexmv at bestpractical.com
Thu Mar 9 18:13:24 EST 2006


Author: alexmv
Date: Thu Mar  9 18:13:22 2006
New Revision: 4722

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

Log:
 r8473 at zoq-fot-pik:  chmrr | 2006-03-09 18:13:13 -0500
  * Backport from 3.7 for CustomFields fixes


Modified: rt/branches/QUEBEC-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm
==============================================================================
--- rt/branches/QUEBEC-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm	(original)
+++ rt/branches/QUEBEC-EXPERIMENTAL/lib/RT/Tickets_Overlay.pm	Thu Mar  9 18:13:22 2006
@@ -160,7 +160,7 @@
     LINKFIELD       => \&_LinkFieldLimit,
     CUSTOMFIELD     => \&_CustomFieldLimit,
 );
-my %can_bundle = ( WATCHERFIELD => "yes", );
+my %can_bundle = ();# WATCHERFIELD => "yes", );
 
 # Default EntryAggregator per type
 # if you specify OP, you must specify all valid OPs
@@ -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();
     }
 }
@@ -871,8 +890,8 @@
     # we cache joins chain per watcher type
     # if we limit by requestor then we shouldn't join requestors again
     # for sort or limit on other requestors
-    if ( $self->{'_watcher_join_users_alias'}{ $type || 'any' } ) {
-        return $self->{'_watcher_join_users_alias'}{ $type || 'any' };
+    if ( $self->{'_sql_watcher_join_users_alias'}{ $type || 'any' } ) {
+        return $self->{'_sql_watcher_join_users_alias'}{ $type || 'any' };
     }
 
 # we always have watcher groups for ticket
@@ -919,7 +938,7 @@
     # RT doesn't allow to add groups as members of the
     # ticket roles, so we just hide entries in CGM table
     # with MemberId == GroupId from results
-    my $groupmembers = $self->SUPER::Limit(
+    $self->SUPER::Limit(
         LEFTJOIN   => $groupmembers,
         FIELD      => 'GroupId',
         OPERATOR   => '!=',
@@ -933,7 +952,7 @@
         TABLE2 => 'Users',
         FIELD2 => 'id'
     );
-    return $self->{'_watcher_join_users_alias'}{ $type || 'any' } = $users;
+    return $self->{'_sql_watcher_join_users_alias'}{ $type || 'any' } = $users;
 }
 
 =head2 _WatcherMembershipLimit
@@ -1142,66 +1161,59 @@
     }
 }
 
-=head2 KeywordLimit
 
-Limit based on Keywords
+=head2 _CustomFieldDecipher
 
-Meta Data:
-  none
+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
+sub _CustomFieldDecipher {
+    my ($self, $field) = @_;
+ 
     my $queue = 0;
-
     if ( $field =~ /^(.+?)\.{(.+)}$/ ) {
-        $queue = $1;
-        $field = $2;
+        ($queue, $field) = ($1, $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 $cfid;
+    if ( $queue ) {
         my $q = RT::Queue->new( $self->CurrentUser );
-        $q->Load($queue) if ($queue);
+        $q->Load( $queue ) if $queue;
 
         my $cf;
         if ( $q->id ) {
-            $cf = $q->CustomField($field);
+            # $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 );
+            $cf->LoadByNameAndQueue( Queue => 0, Name => $field );
         }
+        $cfid = $cf->id if $cf;
+    }
+ 
+  return ($queue, $field, $cfid);
+ 
+}
+ 
+=head2 _CustomFieldJoin
 
-        $cfid = $cf->id;
+Factor out the Join of custom fields so we can use it for sorting too
 
-    }
+=cut
 
+sub _CustomFieldJoin {
+    my ($self, $cfkey, $cfid, $field) = @_;
+ 
     my $TicketCFs;
     my $CFs;
-    my $cfkey = $cfid ? $cfid : "$queue.$field";
-
     # Perform one Join per CustomField
+
     if ( $self->{_sql_object_cf_alias}{$cfkey} ) {
         $TicketCFs = $self->{_sql_object_cf_alias}{$cfkey};
+        $CFs = $self->{_sql_cf_alias}{$cfkey};
     }
     else {
         if ($cfid) {
@@ -1226,7 +1238,17 @@
                 TABLE2     => 'ObjectCustomFields',
                 FIELD2     => 'ObjectId',
             );
-            $CFs = $self->Join(
+
+            $self->SUPER::Limit(
+                    LEFTJOIN => $ocfalias,
+                    ENTRYAGGREGATOR => 'OR',
+                    FIELD => 'ObjectId',
+                    VALUE => '0',
+
+                    );
+
+
+            $CFs = $self->{_sql_cf_alias}{$cfkey} = $self->Join(
                 TYPE       => 'left',
                 ALIAS1     => $ocfalias,
                 FIELD1     => 'CustomField',
@@ -1264,16 +1286,55 @@
         );
     }
 
-    $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, $cfid);
+    ($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;
+
+    unless ($cfid) {
+        $self->SUPER::Limit(
+            ALIAS           => $CFs,
+            FIELD           => 'name',
+            VALUE           => $field,
+            ENTRYAGGREGATOR => 'AND',
+        );
+    }
+    
     $self->_OpenParen if $null_columns_ok;
+    
     $self->_SQLLimit(
         ALIAS      => $TicketCFs,
         FIELD      => 'Content',
@@ -1292,8 +1353,8 @@
             QUOTEVALUE      => 0,
             ENTRYAGGREGATOR => 'OR',
         );
+        $self->_CloseParen;
     }
-    $self->_CloseParen if $null_columns_ok;
 
     $self->_CloseParen;
 
@@ -1324,15 +1385,69 @@
             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 };
-        }
-        else {
-            push @res, $row;
-        }
+        
+
+       } 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;
+       }
     }
     return $self->SUPER::OrderByCols(@res);
 }
@@ -2035,11 +2150,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 +2163,8 @@
 sub LimitHasMember {
     my $self      = shift;
     my $ticket_id = shift;
-    $self->LimitLinkedFrom(
+    return $self->LimitLinkedFrom(
+        @_,
         BASE => "$ticket_id",
         TYPE => 'HasMember',
     );
@@ -2062,8 +2178,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 +2193,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 +2208,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 +2223,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