X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FSearchBuilder.pm;h=178b66b4395008ca20eb3e757ff72770d652414b;hp=22c9aff8cf11a14408f46aaf7e2d382448a329c9;hb=ef20b2b6b1feb47ad02b5ff7525f1a0fd11d0fa4;hpb=945721f48f74d5cfffef7c7cf3a3d6bc2521f5dd diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm index 22c9aff8c..178b66b43 100644 --- a/rt/lib/RT/SearchBuilder.pm +++ b/rt/lib/RT/SearchBuilder.pm @@ -1,8 +1,14 @@ -# BEGIN LICENSE BLOCK +# BEGIN BPS TAGGED BLOCK {{{ # -# Copyright (c) 1996-2003 Jesse Vincent +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC +# # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have @@ -14,13 +20,31 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 or visit their web page on the internet at +# http://www.gnu.org/copyleft/gpl.html. +# +# +# CONTRIBUTION SUBMISSION POLICY: # +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # -# END LICENSE BLOCK +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} =head1 NAME RT::SearchBuilder - a baseclass for RT collection objects @@ -45,7 +69,7 @@ ok (require RT::SearchBuilder); package RT::SearchBuilder; use RT::Base; -use DBIx::SearchBuilder; +use DBIx::SearchBuilder "1.40"; use strict; use vars qw(@ISA); @@ -102,6 +126,158 @@ sub LimitToDeleted { } # }}} +# {{{ sub LimitAttribute + +=head2 LimitAttribute PARAMHASH + +Takes NAME, OPERATOR and VALUE to find records that has the +matching Attribute. + +If EMPTY is set, also select rows with an empty string as +Attribute's Content. + +If NULL is set, also select rows without the named Attribute. + +=cut + +my %Negate = qw( + = != + != = + > <= + < >= + >= < + <= > + LIKE NOT LIKE + NOT LIKE LIKE + IS IS NOT + IS NOT IS +); + +sub LimitAttribute { + my ($self, %args) = @_; + my $clause = 'ALIAS'; + my $operator = ($args{OPERATOR} || '='); + + if ($args{NULL} and exists $args{VALUE}) { + $clause = 'LEFTJOIN'; + $operator = $Negate{$operator}; + } + elsif ($args{NEGATE}) { + $operator = $Negate{$operator}; + } + + my $alias = $self->Join( + TYPE => 'left', + ALIAS1 => $args{ALIAS} || 'main', + FIELD1 => 'id', + TABLE2 => 'Attributes', + FIELD2 => 'ObjectId' + ); + + my $type = ref($self); + $type =~ s/(?:s|Collection)$//; # XXX - Hack! + + $self->Limit( + $clause => $alias, + FIELD => 'ObjectType', + OPERATOR => '=', + VALUE => $type, + ); + $self->Limit( + $clause => $alias, + FIELD => 'Name', + OPERATOR => '=', + VALUE => $args{NAME}, + ) if exists $args{NAME}; + + return unless exists $args{VALUE}; + + $self->Limit( + $clause => $alias, + FIELD => 'Content', + OPERATOR => $operator, + VALUE => $args{VALUE}, + ); + + # Capture rows with the attribute defined as an empty string. + $self->Limit( + $clause => $alias, + FIELD => 'Content', + OPERATOR => '=', + VALUE => '', + ENTRYAGGREGATOR => $args{NULL} ? 'AND' : 'OR', + ) if $args{EMPTY}; + + # Capture rows without the attribute defined + $self->Limit( + %args, + ALIAS => $alias, + FIELD => 'id', + OPERATOR => ($args{NEGATE} ? 'IS NOT' : 'IS'), + VALUE => 'NULL', + ) if $args{NULL}; +} +# }}} + +# {{{ sub LimitCustomField + +=head2 LimitCustomField + +Takes a paramhash of key/value pairs with the following keys: + +=over 4 + +=item CUSTOMFIELD - CustomField id. Optional + +=item OPERATOR - The usual Limit operators + +=item VALUE - The value to compare against + +=back + +=cut + +sub _SingularClass { + my $self = shift; + my $class = ref($self); + $class =~ s/s$// or die "Cannot deduce SingularClass for $class"; + return $class; +} + +sub LimitCustomField { + my $self = shift; + my %args = ( VALUE => undef, + CUSTOMFIELD => undef, + OPERATOR => '=', + @_ ); + + my $alias = $self->Join( + TYPE => 'left', + ALIAS1 => 'main', + FIELD1 => 'id', + TABLE2 => 'ObjectCustomFieldValues', + FIELD2 => 'ObjectId' + ); + $self->Limit( + ALIAS => $alias, + FIELD => 'CustomField', + OPERATOR => '=', + VALUE => $args{'CUSTOMFIELD'}, + ) if ($args{'CUSTOMFIELD'}); + $self->Limit( + ALIAS => $alias, + FIELD => 'ObjectType', + OPERATOR => '=', + VALUE => $self->_SingularClass, + ); + $self->Limit( + ALIAS => $alias, + FIELD => 'Content', + OPERATOR => $args{'OPERATOR'}, + VALUE => $args{'VALUE'}, + ); +} + # {{{ sub FindAllRows =head2 FindAllRows @@ -111,7 +287,7 @@ Find all matching rows, regardless of whether they are disabled or not =cut sub FindAllRows { - shift->{'find_disabled_rows'} = 1; + shift->{'find_disabled_rows'} = 1; } # {{{ sub Limit @@ -125,24 +301,48 @@ match lower(colname) agaist lc($val); =cut sub Limit { - my $self = shift; - my %args = ( CASESENSITIVE => 1, - @_ ); + my $self = shift; + my %args = ( CASESENSITIVE => 1, + @_ ); - return $self->SUPER::Limit(%args); + return $self->SUPER::Limit(%args); } # }}} -# {{{ sub ItemsArrayRef +# {{{ sub ItemsOrderBy -=item ItemsArrayRef +=head2 ItemsOrderBy -Return this object's ItemsArray. If it has a SortOrder attribute, sort the array by SortOrder. Otherwise, if it has a "Name" attribute, sort alphabetically by Name -Otherwise, just give up and return it in the order it came from the db. +Otherwise, just give up and return it in the order it came from the +db. +=cut + +sub ItemsOrderBy { + my $self = shift; + my $items = shift; + + if ($self->NewItem()->_Accessible('SortOrder','read')) { + $items = [ sort { $a->SortOrder <=> $b->SortOrder } @{$items} ]; + } + elsif ($self->NewItem()->_Accessible('Name','read')) { + $items = [ sort { lc($a->Name) cmp lc($b->Name) } @{$items} ]; + } + + return $items; +} + +# }}} + +# {{{ sub ItemsArrayRef + +=head2 ItemsArrayRef + +Return this object's ItemsArray, in the order that ItemsOrderBy sorts +it. =begin testing @@ -174,18 +374,7 @@ sub ItemsArrayRef { my $self = shift; my @items; - if ($self->NewItem()->_Accessible('SortOrder','read')) { - @items = sort { $a->SortOrder <=> $b->SortOrder } @{$self->SUPER::ItemsArrayRef()}; - } - elsif ($self->NewItem()->_Accessible('Name','read')) { - @items = sort { lc($a->Name) cmp lc($b->Name) } @{$self->SUPER::ItemsArrayRef()}; - } - else { - @items = @{$self->SUPER::ItemsArrayRef()}; - } - - return(\@items); - + return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef()); } # }}}