1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
51 RT::SearchBuilder - a baseclass for RT collection objects
65 package RT::SearchBuilder;
68 use DBIx::SearchBuilder "1.50";
74 use base qw(DBIx::SearchBuilder RT::Base);
79 $self->{'user'} = shift;
80 unless(defined($self->CurrentUser)) {
82 Carp::confess("$self was created without a CurrentUser");
83 $RT::Logger->err("$self was created without a CurrentUser");
86 $self->SUPER::_Init( 'Handle' => $RT::Handle);
89 sub _Handle { return $RT::Handle }
93 $self->{'_sql_aliases'} = {};
94 delete $self->{'handled_disabled_column'};
95 delete $self->{'find_disabled_rows'};
96 return $self->SUPER::CleanSlate(@_);
99 sub JoinTransactions {
101 my %args = ( New => 0, @_ );
103 return $self->{'_sql_aliases'}{'transactions'}
104 if !$args{'New'} && $self->{'_sql_aliases'}{'transactions'};
106 my $alias = $self->Join(
109 TABLE2 => 'Transactions',
110 FIELD2 => 'ObjectId',
113 my $item = $self->NewItem;
114 my $object_type = $item->can('ObjectType') ? $item->ObjectType : ref $item;
116 $self->RT::SearchBuilder::Limit(
118 FIELD => 'ObjectType',
119 VALUE => $object_type,
121 $self->{'_sql_aliases'}{'transactions'} = $alias
131 next if defined $s->{FIELD} and $s->{FIELD} =~ /\W/;
132 $s->{FIELD} = $s->{FUNCTION} if $s->{FUNCTION};
135 return $self->SUPER::OrderByCols( @sort );
138 # If we're setting RowsPerPage or FirstRow, ensure we get a natural number or undef.
141 return if @_ and defined $_[0] and $_[0] =~ /\D/;
142 return $self->SUPER::RowsPerPage(@_);
147 return if @_ and defined $_[0] and $_[0] =~ /\D/;
148 return $self->SUPER::FirstRow(@_);
151 =head2 LimitToEnabled
153 Only find items that haven't been disabled
160 $self->{'handled_disabled_column'} = 1;
161 $self->Limit( FIELD => 'Disabled', VALUE => '0' );
164 =head2 LimitToDeleted
166 Only find items that have been deleted.
173 $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1;
174 $self->Limit( FIELD => 'Disabled', VALUE => '1' );
179 Find all matching rows, regardless of whether they are disabled or not
184 shift->{'find_disabled_rows'} = 1;
187 =head2 LimitCustomField
189 Takes a paramhash of key/value pairs with the following keys:
193 =item CUSTOMFIELD - CustomField id. Optional
195 =item OPERATOR - The usual Limit operators
197 =item VALUE - The value to compare against
205 my $class = ref($self);
206 $class =~ s/s$// or die "Cannot deduce SingularClass for $class";
210 sub LimitCustomField {
212 my %args = ( VALUE => undef,
213 CUSTOMFIELD => undef,
217 my $alias = $self->Join(
221 TABLE2 => 'ObjectCustomFieldValues',
226 FIELD => 'CustomField',
228 VALUE => $args{'CUSTOMFIELD'},
229 ) if ($args{'CUSTOMFIELD'});
232 FIELD => 'ObjectType',
234 VALUE => $self->_SingularClass,
239 OPERATOR => $args{'OPERATOR'},
240 VALUE => $args{'VALUE'},
250 =head2 Limit PARAMHASH
252 This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus
253 making sure that by default lots of things don't do extra work trying to
254 match lower(colname) agaist lc($val);
256 We also force VALUE to C<NULL> when the OPERATOR is C<IS> or C<IS NOT>.
257 This ensures that we don't pass invalid SQL to the database or allow SQL
258 injection attacks when we pass through user specified values.
270 # We use the same regex here that DBIx::SearchBuilder uses to exclude
271 # values from quoting
272 if ( $ARGS{'OPERATOR'} =~ /IS/i ) {
273 # Don't pass anything but NULL for IS and IS NOT
274 $ARGS{'VALUE'} = 'NULL';
277 if ($ARGS{FUNCTION}) {
278 ($ARGS{ALIAS}, $ARGS{FIELD}) = split /\./, delete $ARGS{FUNCTION}, 2;
279 $self->SUPER::Limit(%ARGS);
280 } elsif ($ARGS{FIELD} =~ /\W/
281 or $ARGS{OPERATOR} !~ /^(=|<|>|!=|<>|<=|>=
283 |(NOT\s*)?(STARTS|ENDS)WITH
288 $RT::Logger->crit("Possible SQL injection attack: $ARGS{FIELD} $ARGS{OPERATOR}");
296 $self->SUPER::Limit(%ARGS);
302 If it has a SortOrder attribute, sort the array by SortOrder.
303 Otherwise, if it has a "Name" attribute, sort alphabetically by Name
304 Otherwise, just give up and return it in the order it came from the
313 if ($self->NewItem()->_Accessible('SortOrder','read')) {
314 $items = [ sort { $a->SortOrder <=> $b->SortOrder } @{$items} ];
316 elsif ($self->NewItem()->_Accessible('Name','read')) {
317 $items = [ sort { lc($a->Name) cmp lc($b->Name) } @{$items} ];
325 Return this object's ItemsArray, in the order that ItemsOrderBy sorts
332 return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef());
335 # make sure that Disabled rows never get seen unless
336 # we're explicitly trying to see them.
341 if ( $self->{'with_disabled_column'}
342 && !$self->{'handled_disabled_column'}
343 && !$self->{'find_disabled_rows'}
345 $self->LimitToEnabled;
347 return $self->SUPER::_DoSearch(@_);
352 if ( $self->{'with_disabled_column'}
353 && !$self->{'handled_disabled_column'}
354 && !$self->{'find_disabled_rows'}
356 $self->LimitToEnabled;
358 return $self->SUPER::_DoCount(@_);
361 =head2 ColumnMapClassName
363 ColumnMap needs a Collection name to load the correct list display.
364 Depluralization is hard, so provide an easy way to correct the naive
365 algorithm that this code uses.
369 sub ColumnMapClassName {
371 my $Class = ref $self;
377 RT::Base->_ImportOverlays();