+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 {
+ my $self = shift;
+ my $order = shift || 'ASC';
+ $self->OrderByCols( {
+ ALIAS => $self->_OCFAlias,
+ FIELD => 'SortOrder',
+ ORDER => $order,
+ } );
+}
+
+
+=head2 ContextObject
+
+Returns context object for this collection of custom fields,
+but only if it's defined.
+
+=cut
+
+sub ContextObject {
+ my $self = shift;
+ return $self->{'context_object'};
+}
+
+
+=head2 SetContextObject
+
+Sets context object for this collection of custom fields.
+
+=cut
+
+sub SetContextObject {
+ my $self = shift;
+ return $self->{'context_object'} = shift;
+}
+
+
+sub _OCFAlias {
+ my $self = shift;
+ return RT::ObjectCustomFields->new( $self->CurrentUser )
+ ->JoinTargetToThis( $self => @_ );
+}
+
+
+=head2 AddRecord
+
+Overrides the collection to ensure that only custom fields the user can
+see are returned; also propagates down the L</ContextObject>.
+
+=cut
+
+sub AddRecord {
+ my $self = shift;
+ my ($record) = @_;
+
+ $record->SetContextObject( $self->ContextObject );
+ return unless $record->CurrentUserHasRight('SeeCustomField');
+ return $self->SUPER::AddRecord( $record );
+}
+
+=head2 NewItem
+
+Returns an empty new RT::CustomField item
+Overrides <RT::SearchBuilder/NewItem> 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;
+}
+
+RT::Base->_ImportOverlays();