fix cpu/mem sucking when service labels are very long, RT#11452
[freeside.git] / rt / lib / RT / CustomFields_Overlay.pm
index 0f117c6..a672b3c 100644 (file)
 package RT::CustomFields;
 
 use strict;
 package RT::CustomFields;
 
 use strict;
+use warnings;
 no warnings qw(redefine);
 use DBIx::SearchBuilder::Unique;
 
 no warnings qw(redefine);
 use DBIx::SearchBuilder::Unique;
 
+sub _Init {
+    my $self = shift;
+    $self->{'table'} = 'CustomFields';
+    $self->{'primary_key'} = 'id';
+    $self->{'with_disabled_column'} = 1;
 
 
-sub _OCFAlias {
+    return $self->SUPER::_Init(@_);
+}
+
+
+=head2 LimitToLookupType
+
+Takes LookupType and limits collection.
+
+=cut
+
+sub LimitToLookupType  {
+    my $self = shift;
+    my $lookup = shift;
+
+    $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+}
+
+=head2 LimitToChildType
+
+Takes partial LookupType and limits collection to records
+where LookupType is equal or ends with the value.
+
+=cut
+
+sub LimitToChildType  {
     my $self = shift;
     my $self = shift;
-    unless ($self->{_sql_ocfalias}) {
+    my $lookup = shift;
+
+    $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+    $self->Limit( FIELD => 'LookupType', ENDSWITH => "$lookup" );
+}
+
+
+=head2 LimitToParentType
+
+Takes partial LookupType and limits collection to records
+where LookupType is equal or starts with the value.
+
+=cut
+
+sub LimitToParentType  {
+    my $self = shift;
+    my $lookup = shift;
+
+    $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+    $self->Limit( FIELD => 'LookupType', STARTSWITH => "$lookup" );
+}
+
+
+=head2 LimitToGlobalOrObjectId
 
 
-        $self->{'_sql_ocfalias'} = $self->NewAlias('ObjectCustomFields');
-    $self->Join( ALIAS1 => 'main',
-                FIELD1 => 'id',
-                ALIAS2 => $self->_OCFAlias,
-                FIELD2 => 'CustomField' );
+Takes list of object IDs and limits collection to custom
+fields that are applied to these objects or globally.
+
+=cut
+
+sub LimitToGlobalOrObjectId {
+    my $self = shift;
+    my $global_only = 1;
+
+
+    foreach my $id (@_) {
+       $self->Limit( ALIAS           => $self->_OCFAlias,
+                   FIELD           => 'ObjectId',
+                   OPERATOR        => '=',
+                   VALUE           => $id || 0,
+                   ENTRYAGGREGATOR => 'OR' );
+       $global_only = 0 if $id;
     }
     }
-    return($self->{_sql_ocfalias});
+
+    $self->Limit( ALIAS           => $self->_OCFAlias,
+                 FIELD           => 'ObjectId',
+                 OPERATOR        => '=',
+                 VALUE           => 0,
+                 ENTRYAGGREGATOR => 'OR' ) unless $global_only;
 }
 
 }
 
+=head2 LimitToNotApplied
+
+Takes either list of object ids or nothing. Limits collection
+to custom fields to listed objects or any corespondingly. Use
+zero to mean global.
+
+=cut
+
+sub LimitToNotApplied {
+    my $self = shift;
+    my @ids = @_;
+
+    my $ocfs_alias = $self->_OCFAlias( New => 1, Left => 1 );
+    if ( @ids ) {
+        # XXX: we need different EA in join clause, but DBIx::SB
+        # doesn't support them, use IN (X) instead
+        my $dbh = $self->_Handle->dbh;
+        $self->Limit(
+            LEFTJOIN   => $ocfs_alias,
+            ALIAS      => $ocfs_alias,
+            FIELD      => 'ObjectId',
+            OPERATOR   => 'IN',
+            QUOTEVALUE => 0,
+            VALUE      => "(". join( ',', map $dbh->quote($_), @ids ) .")",
+        );
+    }
 
 
-# {{{ sub LimitToGlobalOrQueue 
+    $self->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        ALIAS    => $ocfs_alias,
+        FIELD    => 'id',
+        OPERATOR => 'IS',
+        VALUE    => 'NULL',
+    );
+}
 
 =head2 LimitToGlobalOrQueue QUEUEID
 
 
 =head2 LimitToGlobalOrQueue QUEUEID
 
-Limits the set of custom fields found to global custom fields or those tied to the queue with ID QUEUEID 
+DEPRECATED since CFs are applicable not only to tickets these days.
+
+Limits the set of custom fields found to global custom fields or those tied to the queue with ID QUEUEID
 
 =cut
 
 
 =cut
 
@@ -99,13 +204,12 @@ sub LimitToGlobalOrQueue {
     $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
 }
 
     $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
 }
 
-# }}}
-
-# {{{ sub LimitToQueue 
 
 =head2 LimitToQueue QUEUEID
 
 
 =head2 LimitToQueue QUEUEID
 
-Takes a queue id (numerical) as its only argument. Makes sure that 
+DEPRECATED since CFs are applicable not only to tickets these days.
+
+Takes a queue id (numerical) as its only argument. Makes sure that
 Scopes it pulls out apply to this queue (or another that you've selected with
 another call to this method
 
 Scopes it pulls out apply to this queue (or another that you've selected with
 another call to this method
 
@@ -114,7 +218,7 @@ another call to this method
 sub LimitToQueue  {
    my $self = shift;
   my $queue = shift;
 sub LimitToQueue  {
    my $self = shift;
   my $queue = shift;
+
   $self->Limit (ALIAS => $self->_OCFAlias,
                 ENTRYAGGREGATOR => 'OR',
                FIELD => 'ObjectId',
   $self->Limit (ALIAS => $self->_OCFAlias,
                 ENTRYAGGREGATOR => 'OR',
                FIELD => 'ObjectId',
@@ -122,146 +226,122 @@ sub LimitToQueue  {
       if defined $queue;
   $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
 }
       if defined $queue;
   $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
 }
-# }}}
 
 
-# {{{ sub LimitToGlobal
 
 =head2 LimitToGlobal
 
 
 =head2 LimitToGlobal
 
-Makes sure that 
-Scopes it pulls out apply to all queues (or another that you've selected with
-another call to this method or LimitToQueue
+DEPRECATED since CFs are applicable not only to tickets these days.
 
 
-=cut
+Makes sure that Scopes it pulls out apply to all queues
+(or another that you've selected with
+another call to this method or LimitToQueue)
 
 
+=cut
 
 sub LimitToGlobal  {
    my $self = shift;
 
 sub LimitToGlobal  {
    my $self = shift;
+
   $self->Limit (ALIAS => $self->_OCFAlias,
                 ENTRYAGGREGATOR => 'OR',
                FIELD => 'ObjectId',
                VALUE => 0);
   $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
 }
   $self->Limit (ALIAS => $self->_OCFAlias,
                 ENTRYAGGREGATOR => 'OR',
                FIELD => 'ObjectId',
                VALUE => 0);
   $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
 }
-# }}}
-
 
 
-# {{{ sub _DoSearch 
 
 
-=head2 _DoSearch
+=head2 ApplySortOrder
 
 
-A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that 
- _Disabled rows never get seen unless we're explicitly trying to see 
-them.
+Sort custom fields according to thier order application to objects. It's
+expected that collection contains only records of one
+L<RT::CustomField/LookupType> and applied to one object or globally
+(L</LimitToGlobalOrObjectId>), otherwise sorting makes no sense.
 
 =cut
 
 
 =cut
 
-sub _DoSearch {
+sub ApplySortOrder {
     my $self = shift;
     my $self = shift;
-    
-    #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
-    unless($self->{'find_disabled_rows'}) {
-        $self->LimitToEnabled();
-    }
-    
-    return($self->SUPER::_DoSearch(@_));
-    
+    my $order = shift || 'ASC';
+    $self->OrderByCols( {
+        ALIAS => $self->_OCFAlias,
+        FIELD => 'SortOrder',
+        ORDER => $order,
+    } );
 }
 
 }
 
-# }}}
 
 
-# {{{ sub Next 
-
-=head2 Next
+=head2 ContextObject
 
 
-Returns the next custom field that this user can see.
+Returns context object for this collection of custom fields,
+but only if it's defined.
 
 =cut
 
 =cut
-  
-sub Next {
+
+sub ContextObject {
     my $self = shift;
     my $self = shift;
-    
-    my $CF = $self->SUPER::Next();
-    return $CF unless $CF;
+    return $self->{'context_object'};
+}
 
 
-    $CF->SetContextOject( $self->ContextObject );
 
 
-    return $self->Next unless $CF->CurrentUserHasRight('SeeCustomField');
-    return $CF;
-}
+=head2 SetContextObject
+
+Sets context object for this collection of custom fields.
+
+=cut
 
 sub SetContextObject {
     my $self = shift;
     return $self->{'context_object'} = shift;
 }
 
 sub SetContextObject {
     my $self = shift;
     return $self->{'context_object'} = shift;
 }
-  
-sub ContextObject {
-    my $self = shift;
-    return $self->{'context_object'};
-}
 
 
-sub NewItem {
+
+sub _OCFAlias {
     my $self = shift;
     my $self = shift;
-    my $res = RT::CustomField->new($self->CurrentUser);
-    $res->SetContextObject($self->ContextObject);
-    return $res;
-}
+    my %args = ( New => 0, Left => 0, @_ );
 
 
-# }}}
+    return $self->{'_sql_ocfalias'} if $self->{'_sql_ocfalias'} && !$args{'New'};
 
 
-sub LimitToLookupType  {
-    my $self = shift;
-    my $lookup = shift;
-    $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
+    my $alias = $self->Join(
+        $args{'Left'} ? (TYPE => 'LEFT') : (),
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => 'ObjectCustomFields',
+        FIELD2 => 'CustomField'
+    );
+    return $alias if $args{'New'};
+    return $self->{'_sql_ocfalias'} = $alias;
 }
 
 }
 
-sub LimitToChildType  {
-    my $self = shift;
-    my $lookup = shift;
-    $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
-    $self->Limit( FIELD => 'LookupType', ENDSWITH => "$lookup" );
-}
 
 
-sub LimitToParentType  {
-    my $self = shift;
-    my $lookup = shift;
-    $self->Limit( FIELD => 'LookupType', VALUE => "$lookup" );
-    $self->Limit( FIELD => 'LookupType', STARTSWITH => "$lookup" );
-}
+=head2 Next
 
 
-sub LimitToGlobalOrObjectId {
+Returns the next custom field that this user can see.
+
+=cut
+
+sub Next {
     my $self = shift;
     my $self = shift;
-    my $global_only = 1;
 
 
+    my $CF = $self->SUPER::Next();
+    return $CF unless $CF;
 
 
-    foreach my $id (@_) {
-       $self->Limit( ALIAS           => $self->_OCFAlias,
-                   FIELD           => 'ObjectId',
-                   OPERATOR        => '=',
-                   VALUE           => $id || 0,
-                   ENTRYAGGREGATOR => 'OR' );
-       $global_only = 0 if $id;
-    }
+    $CF->SetContextOject( $self->ContextObject );
 
 
-    $self->Limit( ALIAS           => $self->_OCFAlias,
-                 FIELD           => 'ObjectId',
-                 OPERATOR        => '=',
-                 VALUE           => 0,
-                 ENTRYAGGREGATOR => 'OR' ) unless $global_only;
+    return $self->Next unless $CF->CurrentUserHasRight('SeeCustomField');
+    return $CF;
+}
 
 
-    $self->OrderByCols(
-       { ALIAS => $self->_OCFAlias, FIELD => 'ObjectId', ORDER => 'DESC' },
-       { ALIAS => $self->_OCFAlias, FIELD => 'SortOrder' },
-    );
-    
-    # This doesn't work on postgres. 
-    #$self->OrderBy( ALIAS => $class_cfs , FIELD => "SortOrder", ORDER => 'ASC');
+=head2 Next
+
+Overrides <RT::SearchBuilder/Next> to make sure </ContextObject>
+is inherited.
 
 
+=cut
+
+sub NewItem {
+    my $self = shift;
+    my $res = RT::CustomField->new($self->CurrentUser);
+    $res->SetContextObject($self->ContextObject);
+    return $res;
 }
 
 1;
 }
 
 1;
-