+sub LimitToGrouping {
+ my $self = shift;
+ my $obj = shift;
+ my $grouping = shift;
+
+ my $grouping_class = $self->NewItem->_GroupingClass($obj);
+
+ my $config = RT->Config->Get('CustomFieldGroupings');
+ $config = {} unless ref($config) eq 'HASH';
+ $config = $config->{$grouping_class} || [];
+ my %h = ref $config eq "ARRAY" ? @{$config} : %{$config};
+
+ if ( $grouping ) {
+ my $list = $h{$grouping};
+ unless ( $list and ref($list) eq 'ARRAY' and @$list ) {
+ return $self->Limit( FIELD => 'id', VALUE => 0, ENTRYAGGREGATOR => 'AND' );
+ }
+ $self->Limit(
+ FIELD => 'Name',
+ FUNCTION => 'LOWER(?)',
+ OPERATOR => 'IN',
+ VALUE => [map {lc $_} @{$list}],
+ CASESENSITIVE => 1,
+ );
+ } else {
+ my @list = map {@$_} grep defined && ref($_) eq 'ARRAY',
+ values %h;
+
+ return unless @list;
+
+ $self->Limit(
+ FIELD => 'Name',
+ FUNCTION => 'LOWER(?)',
+ OPERATOR => 'NOT IN',
+ VALUE => [ map {lc $_} @list ],
+ CASESENSITIVE => 1,
+ );
+ }
+ return;
+}
+
+
+=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 $lookup = shift;
+
+ $self->Limit( FIELD => 'LookupType', VALUE => "$lookup", OPERATOR => "ENDSWITH" );
+}
+
+
+=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", OPERATOR => "STARTSWITH" );
+}
+
+=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
+
+Takes list of object IDs and limits collection to custom
+fields that are added to these objects or globally.
+
+=cut
+
+sub LimitToGlobalOrObjectId {
+ my $self = shift;
+ my $global_only = 1;
+
+
+ foreach my $id (@_) {
+ $self->LimitToObjectId($id);
+ $global_only = 0 if $id;
+ }
+
+ $self->LimitToObjectId(0) unless $global_only;
+}
+
+=head2 LimitToNotAdded
+
+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 LimitToNotAdded {
+ my $self = shift;
+ return RT::ObjectCustomFields->new( $self->CurrentUser )
+ ->LimitTargetToNotAdded( $self => @_ );
+}
+
+=head2 LimitToAdded
+
+Limits collection to custom fields to listed objects or any corespondingly. Use
+zero to mean global.
+
+=cut
+
+sub LimitToAdded {
+ my $self = shift;
+ return RT::ObjectCustomFields->new( $self->CurrentUser )
+ ->LimitTargetToAdded( $self => @_ );
+}
+
+=head2 LimitToGlobalOrQueue QUEUEID
+
+Limits the set of custom fields found to global custom fields or those
+tied to the queue C<QUEUEID>, similar to L</LimitToGlobalOrObjectId>.
+
+Note that this will cause the collection to only return ticket CFs.
+
+=cut
+
+sub LimitToGlobalOrQueue {
+ my $self = shift;
+ my $queue = shift;
+ $self->LimitToGlobalOrObjectId( $queue );
+ $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+}
+
+
+=head2 LimitToQueue QUEUEID
+
+Takes a numeric C<QUEUEID>, and limits the Custom Field collection to
+those only applied directly to it; this limit is OR'd with other
+L</LimitToQueue> and L</LimitToGlobal> limits.
+
+Note that this will cause the collection to only return ticket CFs.
+
+=cut
+
+sub LimitToQueue {
+ my $self = shift;
+ my $queue = shift;
+
+ $self->Limit (ALIAS => $self->_OCFAlias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => "$queue")
+ if defined $queue;
+ $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+}
+
+
+=head2 LimitToGlobal
+
+Limits the Custom Field collection to global ticket CFs; this limit is
+OR'd with L</LimitToQueue> limits.
+
+Note that this will cause the collection to only return ticket CFs.
+
+=cut
+
+sub LimitToGlobal {
+ my $self = shift;
+
+ $self->Limit (ALIAS => $self->_OCFAlias,
+ ENTRYAGGREGATOR => 'OR',
+ FIELD => 'ObjectId',
+ VALUE => 0);
+ $self->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+}
+
+
+=head2 ApplySortOrder
+
+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
+
+sub ApplySortOrder {