This commit was generated by cvs2svn to compensate for changes in r3921,
[freeside.git] / rt / lib / RT / SearchBuilder.pm
1 # {{{ BEGIN BPS TAGGED BLOCK
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC 
6 #                                          <jesse@bestpractical.com>
7
8 # (Except where explicitly superseded by other copyright notices)
9
10
11 # LICENSE:
12
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
16 # from www.gnu.org.
17
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.
22
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., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27
28 # CONTRIBUTION SUBMISSION POLICY:
29
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
35
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
44
45 # }}} END BPS TAGGED BLOCK
46 =head1 NAME
47
48   RT::SearchBuilder - a baseclass for RT collection objects
49
50 =head1 SYNOPSIS
51
52 =head1 DESCRIPTION
53
54
55 =head1 METHODS
56
57
58 =begin testing
59
60 ok (require RT::SearchBuilder);
61
62 =end testing
63
64
65 =cut
66
67 package RT::SearchBuilder;
68
69 use RT::Base;
70 use DBIx::SearchBuilder;
71
72 use strict;
73 use vars qw(@ISA);
74 @ISA = qw(DBIx::SearchBuilder RT::Base);
75
76 # {{{ sub _Init 
77 sub _Init  {
78     my $self = shift;
79     
80     $self->{'user'} = shift;
81     unless(defined($self->CurrentUser)) {
82         use Carp;
83         Carp::confess("$self was created without a CurrentUser");
84         $RT::Logger->err("$self was created without a CurrentUser");
85         return(0);
86     }
87     $self->SUPER::_Init( 'Handle' => $RT::Handle);
88 }
89 # }}}
90
91 # {{{ sub LimitToEnabled
92
93 =head2 LimitToEnabled
94
95 Only find items that haven\'t been disabled
96
97 =cut
98
99 sub LimitToEnabled {
100     my $self = shift;
101     
102     $self->Limit( FIELD => 'Disabled',
103                   VALUE => '0',
104                   OPERATOR => '=' );
105 }
106 # }}}
107
108 # {{{ sub LimitToDisabled
109
110 =head2 LimitToDeleted
111
112 Only find items that have been deleted.
113
114 =cut
115
116 sub LimitToDeleted {
117     my $self = shift;
118     
119     $self->{'find_disabled_rows'} = 1;
120     $self->Limit( FIELD => 'Disabled',
121                   OPERATOR => '=',
122                   VALUE => '1'
123                 );
124 }
125 # }}}
126
127 # {{{ sub LimitAttribute
128
129 =head2 LimitAttribute PARAMHASH
130
131 Takes NAME, OPERATOR and VALUE to find records that has the
132 matching Attribute.
133
134 =cut
135
136 sub LimitAttribute {
137     my ($self, %args) = @_;
138     
139     my $alias = $self->Join(
140         TYPE   => 'left',
141         ALIAS1 => 'main',
142         FIELD1 => 'id',
143         TABLE2 => 'Attributes',
144         FIELD2 => 'ObjectId'
145     );
146
147     my $type = ref($self);
148     $type =~ s/(?:s|Collection)$//; # XXX - Hack!
149
150     $self->Limit(
151         ALIAS      => $alias,
152         FIELD      => 'ObjectType',
153         OPERATOR   => '=',
154         VALUE      => $type,
155     );
156     $self->Limit(
157         ALIAS      => $alias,
158         FIELD      => 'Name',
159         OPERATOR   => '=',
160         VALUE      => $args{NAME},
161     ) if exists $args{NAME};
162
163     return unless exists $args{VALUE};
164
165     $self->Limit(
166         ALIAS      => $alias,
167         FIELD      => 'Content',
168         OPERATOR   => ($args{OPERATOR} || '='),
169         VALUE      => $args{VALUE},
170         ENTRYAGGREGATOR => 'OR',
171     );
172
173     if ($args{EMPTY}) {
174         # Capture rows without the attribute defined by testing IS NULL.
175         $self->Limit(
176             ALIAS      => $alias,
177             FIELD      => $_,
178             OPERATOR   => 'IS',
179             VALUE      => 'NULL',
180             ENTRYAGGREGATOR => 'OR',
181         ) for qw( ObjectType Name Content );
182     }
183 }
184 # }}}
185
186 1;
187
188 # {{{ sub FindAllRows
189
190 =head2 FindAllRows
191
192 Find all matching rows, regardless of whether they are disabled or not
193
194 =cut
195
196 sub FindAllRows {
197     shift->{'find_disabled_rows'} = 1;
198 }
199
200 # {{{ sub Limit 
201
202 =head2 Limit PARAMHASH
203
204 This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus
205 making sure that by default lots of things don't do extra work trying to 
206 match lower(colname) agaist lc($val);
207
208 =cut
209
210 sub Limit {
211     my $self = shift;
212     my %args = ( CASESENSITIVE => 1,
213                  @_ );
214
215     return $self->SUPER::Limit(%args);
216 }
217
218 # }}}
219
220 # {{{ sub ItemsOrderBy
221
222 =item ItemsOrderBy
223
224 If it has a SortOrder attribute, sort the array by SortOrder.
225 Otherwise, if it has a "Name" attribute, sort alphabetically by Name
226 Otherwise, just give up and return it in the order it came from the
227 db.
228
229 =cut
230
231 sub ItemsOrderBy {
232     my $self = shift;
233     my $items = shift;
234   
235     if ($self->NewItem()->_Accessible('SortOrder','read')) {
236         $items = [ sort { $a->SortOrder <=> $b->SortOrder } @{$items} ];
237     }
238     elsif ($self->NewItem()->_Accessible('Name','read')) {
239         $items = [ sort { lc($a->Name) cmp lc($b->Name) } @{$items} ];
240     }
241
242     return $items;
243 }
244
245 # }}}
246
247 # {{{ sub ItemsArrayRef
248
249 =item ItemsArrayRef
250
251 Return this object's ItemsArray, in the order that ItemsOrderBy sorts
252 it.
253
254 =begin testing
255
256 use_ok(RT::Queues);
257 ok(my $queues = RT::Queues->new($RT::SystemUser), 'Created a queues object');
258 ok( $queues->UnLimit(),'Unlimited the result set of the queues object');
259 my $items = $queues->ItemsArrayRef();
260 my @items = @{$items};
261
262 ok($queues->NewItem->_Accessible('Name','read'));
263 my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items;
264 ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted));
265
266 ok (@items, "We have an array of queues, raw". join(',',map {$_->Name} @items));
267 my @sorted_ids = map {$_->id } @sorted;
268 my @items_ids = map {$_->id } @items;
269
270 is ($#sorted, $#items);
271 is ($sorted[0]->Name, $items[0]->Name);
272 is ($sorted[-1]->Name, $items[-1]->Name);
273 is_deeply(\@items_ids, \@sorted_ids, "ItemsArrayRef sorts alphabetically by name");;
274
275
276 =end testing
277
278 =cut
279
280 sub ItemsArrayRef {
281     my $self = shift;
282     my @items;
283     
284     return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef());
285 }
286
287 # }}}
288
289 eval "require RT::SearchBuilder_Vendor";
290 die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Vendor.pm});
291 eval "require RT::SearchBuilder_Local";
292 die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Local.pm});
293
294 1;
295
296