first pass RT4 merge, RT#13852
[freeside.git] / rt / lib / RT / Scrips_Overlay.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2011 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 =head1 NAME
50
51   RT::Scrips - a collection of RT Scrip objects
52
53 =head1 SYNOPSIS
54
55   use RT::Scrips;
56
57 =head1 DESCRIPTION
58
59
60 =head1 METHODS
61
62
63
64 =cut
65
66
67 package RT::Scrips;
68
69 use strict;
70 no warnings qw(redefine);
71
72 # {{{ sub LimitToQueue 
73
74 =head2 LimitToQueue
75
76 Takes a queue id (numerical) as its only argument. Makes sure that 
77 Scopes it pulls out apply to this queue (or another that you've selected with
78 another call to this method
79
80 =cut
81
82 sub LimitToQueue  {
83    my $self = shift;
84   my $queue = shift;
85  
86   $self->Limit (ENTRYAGGREGATOR => 'OR',
87                 FIELD => 'Queue',
88                 VALUE => "$queue")
89       if defined $queue;
90   
91 }
92 # }}}
93
94 # {{{ sub LimitToGlobal
95
96 =head2 LimitToGlobal
97
98 Makes sure that 
99 Scopes it pulls out apply to all queues (or another that you've selected with
100 another call to this method or LimitToQueue
101
102 =cut
103
104
105 sub LimitToGlobal  {
106    my $self = shift;
107  
108   $self->Limit (ENTRYAGGREGATOR => 'OR',
109                 FIELD => 'Queue',
110                 VALUE => 0);
111   
112 }
113 # }}}
114
115 # {{{ sub NewItem 
116 sub NewItem  {
117   my $self = shift;
118   
119   return(new RT::Scrip($self->CurrentUser));
120 }
121 # }}}
122
123 # {{{ sub Next 
124
125 =head2 Next
126
127 Returns the next scrip that this user can see.
128
129 =cut
130   
131 sub Next {
132     my $self = shift;
133     
134     
135     my $Scrip = $self->SUPER::Next();
136     if ((defined($Scrip)) and (ref($Scrip))) {
137
138         if ($Scrip->CurrentUserHasRight('ShowScrips')) {
139             return($Scrip);
140         }
141         
142         #If the user doesn't have the right to show this scrip
143         else {  
144             return($self->Next());
145         }
146     }
147     #if there never was any scrip
148     else {
149         return(undef);
150     }   
151     
152 }
153 # }}}
154
155 =head2 Apply
156
157 Run through the relevant scrips.  Scrips will run in order based on 
158 description.  (Most common use case is to prepend a number to the description,
159 forcing the scrips to run in ascending alphanumerical order.)
160
161 =cut
162
163 sub Apply {
164     my $self = shift;
165
166     my %args = ( TicketObj      => undef,
167                  Ticket         => undef,
168                  Transaction    => undef,
169                  TransactionObj => undef,
170                  Stage          => undef,
171                  Type           => undef,
172                  @_ );
173
174     $self->Prepare(%args);
175     $self->Commit();
176
177 }
178
179 =head2 Commit
180
181 Commit all of this object's prepared scrips
182
183 =cut
184
185 sub Commit {
186     my $self = shift;
187
188     
189     foreach my $scrip (@{$self->Prepared}) {
190         $RT::Logger->debug(
191             "Committing scrip #". $scrip->id
192             ." on txn #". $self->{'TransactionObj'}->id
193             ." of ticket #". $self->{'TicketObj'}->id
194         );
195
196         $scrip->Commit( TicketObj      => $self->{'TicketObj'},
197                         TransactionObj => $self->{'TransactionObj'} );
198     }
199 }
200
201
202 =head2 Prepare
203
204 Only prepare the scrips, returning an array of the scrips we're interested in
205 in order of preparation, not execution
206
207 =cut
208
209 sub Prepare { 
210     my $self = shift;
211     my %args = ( TicketObj      => undef,
212                  Ticket         => undef,
213                  Transaction    => undef,
214                  TransactionObj => undef,
215                  Stage          => undef,
216                  Type           => undef,
217                  @_ );
218
219     #We're really going to need a non-acled ticket for the scrips to work
220     $self->_SetupSourceObjects( TicketObj      => $args{'TicketObj'},
221                                 Ticket         => $args{'Ticket'},
222                                 TransactionObj => $args{'TransactionObj'},
223                                 Transaction    => $args{'Transaction'} );
224
225
226     $self->_FindScrips( Stage => $args{'Stage'}, Type => $args{'Type'} );
227
228
229     #Iterate through each script and check it's applicability.
230     while ( my $scrip = $self->Next() ) {
231
232           unless ( $scrip->IsApplicable(
233                                      TicketObj      => $self->{'TicketObj'},
234                                      TransactionObj => $self->{'TransactionObj'}
235                    ) ) {
236                    $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it isn't applicable");
237                    next;
238                }
239
240         #If it's applicable, prepare and commit it
241           unless ( $scrip->Prepare( TicketObj      => $self->{'TicketObj'},
242                                     TransactionObj => $self->{'TransactionObj'}
243                    ) ) {
244                    $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it didn't Prepare");
245                    next;
246                }
247         push @{$self->{'prepared_scrips'}}, $scrip;
248
249     }
250
251     return (@{$self->Prepared});
252
253 };
254
255 =head2 Prepared
256
257 Returns an arrayref of the scrips this object has prepared
258
259
260 =cut
261
262 sub Prepared {
263     my $self = shift;
264     return ($self->{'prepared_scrips'} || []);
265 }
266
267
268 # {{{ sup _SetupSourceObjects
269
270 =head2  _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj }
271
272 Setup a ticket and transaction for this Scrip collection to work with as it runs through the 
273 relevant scrips.  (Also to figure out which scrips apply)
274
275 Returns: nothing
276
277 =cut
278
279
280 sub _SetupSourceObjects {
281
282     my $self = shift;
283     my %args = ( 
284             TicketObj => undef,
285             Ticket => undef,
286             Transaction => undef,
287             TransactionObj => undef,
288             @_ );
289
290
291     if ( $args{'TicketObj'} ) {
292         # clone the ticket here as we need to change CurrentUser
293         $self->{'TicketObj'} = bless { %{$args{'TicketObj'} } }, 'RT::Ticket';
294         $self->{'TicketObj'}->CurrentUser( $self->CurrentUser );
295     }
296     else {
297         $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser );
298         $self->{'TicketObj'}->Load( $args{'Ticket'} )
299           || $RT::Logger->err("$self couldn't load ticket $args{'Ticket'}");
300     }
301
302     if ( ( $self->{'TransactionObj'} = $args{'TransactionObj'} ) ) {
303         $self->{'TransactionObj'}->CurrentUser( $self->CurrentUser );
304     }
305     else {
306         $self->{'TransactionObj'} = RT::Transaction->new( $self->CurrentUser );
307         $self->{'TransactionObj'}->Load( $args{'Transaction'} )
308           || $RT::Logger->err( "$self couldn't load transaction $args{'Transaction'}");
309     }
310
311
312 # }}}
313
314 # {{{ sub _FindScrips;
315
316 =head2 _FindScrips
317
318 Find only the apropriate scrips for whatever we're doing now.  Order them 
319 by their description.  (Most common use case is to prepend a number to the
320 description, forcing the scrips to display and run in ascending alphanumerical 
321 order.)
322
323 =cut
324
325 sub _FindScrips {
326     my $self = shift;
327     my %args = (
328                  Stage => undef,
329                  Type => undef,
330                  @_ );
331
332
333     $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id )
334       ;    #Limit it to  $Ticket->QueueObj->Id
335     $self->LimitToGlobal();
336       # or to "global"
337
338     $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} );
339
340     my $ConditionsAlias = $self->NewAlias('ScripConditions');
341
342     $self->Join(
343         ALIAS1 => 'main',
344         FIELD1 => 'ScripCondition',
345         ALIAS2 => $ConditionsAlias,
346         FIELD2 => 'id'
347     );
348
349     #We only want things where the scrip applies to this sort of transaction
350     # TransactionBatch stage can define list of transaction
351     foreach( split /\s*,\s*/, ($args{'Type'} || '') ) {
352         $self->Limit(
353             ALIAS           => $ConditionsAlias,
354             FIELD           => 'ApplicableTransTypes',
355             OPERATOR        => 'LIKE',
356             VALUE           => $_,
357             ENTRYAGGREGATOR => 'OR',
358         )
359     }
360
361     # Or where the scrip applies to any transaction
362     $self->Limit(
363         ALIAS           => $ConditionsAlias,
364         FIELD           => 'ApplicableTransTypes',
365         OPERATOR        => 'LIKE',
366         VALUE           => "Any",
367         ENTRYAGGREGATOR => 'OR',
368     );
369
370     # Promise some kind of ordering
371     $self->OrderBy( FIELD => 'Description' );
372
373     # we call Count below, but later we always do search
374     # so just do search and get count from results
375     $self->_DoSearch if $self->{'must_redo_search'};
376
377     $RT::Logger->debug(
378         "Found ". $self->Count ." scrips for $args{'Stage'} stage"
379         ." with applicable type(s) $args{'Type'}"
380         ." for txn #".$self->{TransactionObj}->Id
381         ." on ticket #".$self->{TicketObj}->Id
382     );
383 }
384
385 # }}}
386
387 1;
388