rt 4.2.16
[freeside.git] / rt / lib / RT / SearchBuilder / AddAndSort.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
6 #                                          <sales@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., 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.
28 #
29 #
30 # CONTRIBUTION SUBMISSION POLICY:
31 #
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.)
37 #
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.
46 #
47 # END BPS TAGGED BLOCK }}}
48
49 use strict;
50 use warnings;
51
52 package RT::SearchBuilder::AddAndSort;
53 use base 'RT::SearchBuilder';
54
55 =head1 NAME
56
57 RT::SearchBuilder::AddAndSort - base class for 'add and sort' collections
58
59 =head1 DESCRIPTION
60
61 Base class for collections where records can be added to objects with order.
62 See also L<RT::Record::AddAndSort>. Used by L<RT::ObjectScrips> and
63 L<RT::ObjectCustomFields>.
64
65 As it's about sorting then collection is sorted by SortOrder field.
66
67 =head1 METHODS
68
69 =cut
70
71 sub _Init {
72     my $self = shift;
73
74     # By default, order by SortOrder
75     $self->OrderByCols(
76          { ALIAS => 'main',
77            FIELD => 'SortOrder',
78            ORDER => 'ASC' },
79          { ALIAS => 'main',
80            FIELD => 'id',
81            ORDER => 'ASC' },
82     );
83
84     return $self->SUPER::_Init(@_);
85 }
86
87 =head2 LimitToObjectId
88
89 Takes id of an object and limits collection.
90
91 =cut
92
93 sub LimitToObjectId {
94     my $self = shift;
95     my $id = shift || 0;
96     $self->Limit( FIELD => 'ObjectId', VALUE => $id );
97 }
98
99 =head1 METHODS FOR TARGETS
100
101 Rather than implementing a base class for targets (L<RT::Scrip>,
102 L<RT::CustomField>) and its collections. This class provides
103 class methods to limit target collections.
104
105 =head2 LimitTargetToNotAdded
106
107 Takes a collection object and optional list of object ids. Limits the
108 collection to records not added to listed objects or if the list is
109 empty then any object. Use 0 (zero) to mean global.
110
111 =cut
112
113 sub LimitTargetToNotAdded {
114     my $self = shift;
115     my $collection = shift;
116     my @ids = @_;
117
118     my $alias = $self->JoinTargetToAdded($collection => @ids);
119
120     $collection->Limit(
121         ENTRYAGGREGATOR => 'AND',
122         ALIAS    => $alias,
123         FIELD    => 'id',
124         OPERATOR => 'IS',
125         VALUE    => 'NULL',
126     );
127     return $alias;
128 }
129
130 =head2 LimitTargetToAdded
131
132 L</LimitTargetToNotAdded> with reverse meaning. Takes the same
133 arguments.
134
135 =cut
136
137 sub LimitTargetToAdded {
138     my $self = shift;
139     my $collection = shift;
140     my @ids = @_;
141
142     my $alias = $self->JoinTargetToAdded($collection => @ids);
143
144     $collection->Limit(
145         ENTRYAGGREGATOR => 'AND',
146         ALIAS    => $alias,
147         FIELD    => 'id',
148         OPERATOR => 'IS NOT',
149         VALUE    => 'NULL',
150     );
151     return $alias;
152 }
153
154 =head2 JoinTargetToAdded
155
156 Joins collection to this table using left join, limits joined table
157 by ids if those are provided.
158
159 Returns alias of the joined table. Join is cached and re-used for
160 multiple calls.
161
162 =cut
163
164 sub JoinTargetToAdded {
165     my $self = shift;
166     my $collection = shift;
167     my @ids = @_;
168
169     my $alias = $self->JoinTargetToThis( $collection, New => 0, Left => 1 );
170     return $alias unless @ids;
171
172     # XXX: we need different EA in join clause, but DBIx::SB
173     # doesn't support them, use IN (X) instead
174     my $dbh = $self->_Handle->dbh;
175     $collection->Limit(
176         LEFTJOIN   => $alias,
177         ALIAS      => $alias,
178         FIELD      => 'ObjectId',
179         OPERATOR   => 'IN',
180         VALUE      => [ @ids ],
181     );
182
183     return $alias;
184 }
185
186 =head2 JoinTargetToThis
187
188 Joins target collection to this table using TargetField.
189
190 Takes New and Left arguments. Use New to avoid caching and re-using
191 this join. Use Left to create LEFT JOIN rather than inner.
192
193 =cut
194
195 sub JoinTargetToThis {
196     my $self = shift;
197     my $collection = shift;
198     my %args = ( New => 0, Left => 0, Distinct => 0, @_ );
199
200     my $table = $self->Table;
201     my $key = "_sql_${table}_alias";
202
203     return $collection->{ $key } if $collection->{ $key } && !$args{'New'};
204
205     my $alias = $collection->Join(
206         $args{'Left'} ? (TYPE => 'LEFT') : (),
207         ALIAS1 => 'main',
208         FIELD1 => 'id',
209         TABLE2 => $table,
210         FIELD2 => $self->RecordClass->TargetField,
211         DISTINCT => $args{Distinct},
212     );
213     return $alias if $args{'New'};
214     return $collection->{ $key } = $alias;
215 }
216
217 RT::Base->_ImportOverlays();
218
219 1;