]>
Commit | Line | Data |
---|---|---|
1 | # BEGIN BPS TAGGED BLOCK {{{ | |
2 | # | |
3 | # COPYRIGHT: | |
4 | # | |
5 | # This software is Copyright (c) 1996-2012 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 | use warnings; | |
71 | ||
72 | use RT::Scrip; | |
73 | ||
74 | use base 'RT::SearchBuilder'; | |
75 | ||
76 | sub Table { 'Scrips'} | |
77 | ||
78 | ||
79 | =head2 LimitToQueue | |
80 | ||
81 | Takes a queue id (numerical) as its only argument. Makes sure that | |
82 | Scopes it pulls out apply to this queue (or another that you've selected with | |
83 | another call to this method | |
84 | ||
85 | =cut | |
86 | ||
87 | sub LimitToQueue { | |
88 | my $self = shift; | |
89 | my $queue = shift; | |
90 | ||
91 | $self->Limit (ENTRYAGGREGATOR => 'OR', | |
92 | FIELD => 'Queue', | |
93 | VALUE => "$queue") | |
94 | if defined $queue; | |
95 | ||
96 | } | |
97 | ||
98 | ||
99 | =head2 LimitToGlobal | |
100 | ||
101 | Makes sure that | |
102 | Scopes it pulls out apply to all queues (or another that you've selected with | |
103 | another call to this method or LimitToQueue | |
104 | ||
105 | =cut | |
106 | ||
107 | ||
108 | sub LimitToGlobal { | |
109 | my $self = shift; | |
110 | ||
111 | $self->Limit (ENTRYAGGREGATOR => 'OR', | |
112 | FIELD => 'Queue', | |
113 | VALUE => 0); | |
114 | ||
115 | } | |
116 | ||
117 | # {{{ sub Next | |
118 | ||
119 | =head2 Next | |
120 | ||
121 | Returns the next scrip that this user can see. | |
122 | ||
123 | =cut | |
124 | ||
125 | sub Next { | |
126 | my $self = shift; | |
127 | ||
128 | ||
129 | my $Scrip = $self->SUPER::Next(); | |
130 | if ((defined($Scrip)) and (ref($Scrip))) { | |
131 | ||
132 | if ($Scrip->CurrentUserHasRight('ShowScrips')) { | |
133 | return($Scrip); | |
134 | } | |
135 | ||
136 | #If the user doesn't have the right to show this scrip | |
137 | else { | |
138 | return($self->Next()); | |
139 | } | |
140 | } | |
141 | #if there never was any scrip | |
142 | else { | |
143 | return(undef); | |
144 | } | |
145 | ||
146 | } | |
147 | ||
148 | =head2 Apply | |
149 | ||
150 | Run through the relevant scrips. Scrips will run in order based on | |
151 | description. (Most common use case is to prepend a number to the description, | |
152 | forcing the scrips to run in ascending alphanumerical order.) | |
153 | ||
154 | =cut | |
155 | ||
156 | sub Apply { | |
157 | my $self = shift; | |
158 | ||
159 | my %args = ( TicketObj => undef, | |
160 | Ticket => undef, | |
161 | Transaction => undef, | |
162 | TransactionObj => undef, | |
163 | Stage => undef, | |
164 | Type => undef, | |
165 | @_ ); | |
166 | ||
167 | $self->Prepare(%args); | |
168 | $self->Commit(); | |
169 | ||
170 | } | |
171 | ||
172 | =head2 Commit | |
173 | ||
174 | Commit all of this object's prepared scrips | |
175 | ||
176 | =cut | |
177 | ||
178 | sub Commit { | |
179 | my $self = shift; | |
180 | ||
181 | foreach my $scrip (@{$self->Prepared}) { | |
182 | $RT::Logger->debug( | |
183 | "Committing scrip #". $scrip->id | |
184 | ." on txn #". $self->{'TransactionObj'}->id | |
185 | ." of ticket #". $self->{'TicketObj'}->id | |
186 | ); | |
187 | ||
188 | $scrip->Commit( TicketObj => $self->{'TicketObj'}, | |
189 | TransactionObj => $self->{'TransactionObj'} ); | |
190 | } | |
191 | ||
192 | } | |
193 | ||
194 | ||
195 | =head2 Prepare | |
196 | ||
197 | Only prepare the scrips, returning an array of the scrips we're interested in | |
198 | in order of preparation, not execution | |
199 | ||
200 | =cut | |
201 | ||
202 | sub Prepare { | |
203 | my $self = shift; | |
204 | my %args = ( TicketObj => undef, | |
205 | Ticket => undef, | |
206 | Transaction => undef, | |
207 | TransactionObj => undef, | |
208 | Stage => undef, | |
209 | Type => undef, | |
210 | @_ ); | |
211 | ||
212 | #We're really going to need a non-acled ticket for the scrips to work | |
213 | $self->_SetupSourceObjects( TicketObj => $args{'TicketObj'}, | |
214 | Ticket => $args{'Ticket'}, | |
215 | TransactionObj => $args{'TransactionObj'}, | |
216 | Transaction => $args{'Transaction'} ); | |
217 | ||
218 | ||
219 | $self->_FindScrips( Stage => $args{'Stage'}, Type => $args{'Type'} ); | |
220 | ||
221 | ||
222 | #Iterate through each script and check it's applicability. | |
223 | while ( my $scrip = $self->Next() ) { | |
224 | ||
225 | unless ( $scrip->IsApplicable( | |
226 | TicketObj => $self->{'TicketObj'}, | |
227 | TransactionObj => $self->{'TransactionObj'} | |
228 | ) ) { | |
229 | $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it isn't applicable"); | |
230 | next; | |
231 | } | |
232 | ||
233 | #If it's applicable, prepare and commit it | |
234 | unless ( $scrip->Prepare( TicketObj => $self->{'TicketObj'}, | |
235 | TransactionObj => $self->{'TransactionObj'} | |
236 | ) ) { | |
237 | $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it didn't Prepare"); | |
238 | next; | |
239 | } | |
240 | push @{$self->{'prepared_scrips'}}, $scrip; | |
241 | ||
242 | } | |
243 | ||
244 | return (@{$self->Prepared}); | |
245 | ||
246 | }; | |
247 | ||
248 | =head2 Prepared | |
249 | ||
250 | Returns an arrayref of the scrips this object has prepared | |
251 | ||
252 | ||
253 | =cut | |
254 | ||
255 | sub Prepared { | |
256 | my $self = shift; | |
257 | return ($self->{'prepared_scrips'} || []); | |
258 | } | |
259 | ||
260 | =head2 _SetupSourceObjects { TicketObj , Ticket, Transaction, TransactionObj } | |
261 | ||
262 | Setup a ticket and transaction for this Scrip collection to work with as it runs through the | |
263 | relevant scrips. (Also to figure out which scrips apply) | |
264 | ||
265 | Returns: nothing | |
266 | ||
267 | =cut | |
268 | ||
269 | ||
270 | sub _SetupSourceObjects { | |
271 | ||
272 | my $self = shift; | |
273 | my %args = ( | |
274 | TicketObj => undef, | |
275 | Ticket => undef, | |
276 | Transaction => undef, | |
277 | TransactionObj => undef, | |
278 | @_ ); | |
279 | ||
280 | ||
281 | if ( $args{'TicketObj'} ) { | |
282 | # This loads a clean copy of the Ticket object to ensure that we | |
283 | # don't accidentally escalate the privileges of the passed in | |
284 | # ticket (this function can be invoked from the UI). | |
285 | # We copy the TransactionBatch transactions so that Scrips | |
286 | # running against the new Ticket will have access to them. We | |
287 | # use RanTransactionBatch to guard against running | |
288 | # TransactionBatch Scrips more than once. | |
289 | $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser ); | |
290 | $self->{'TicketObj'}->Load( $args{'TicketObj'}->Id ); | |
291 | if ( $args{'TicketObj'}->TransactionBatch ) { | |
292 | # try to ensure that we won't infinite loop if something dies, triggering DESTROY while | |
293 | # we have the _TransactionBatch objects; | |
294 | $self->{'TicketObj'}->RanTransactionBatch(1); | |
295 | $self->{'TicketObj'}->{'_TransactionBatch'} = $args{'TicketObj'}->{'_TransactionBatch'}; | |
296 | } | |
297 | } | |
298 | else { | |
299 | $self->{'TicketObj'} = RT::Ticket->new( $self->CurrentUser ); | |
300 | $self->{'TicketObj'}->Load( $args{'Ticket'} ) | |
301 | || $RT::Logger->err("$self couldn't load ticket $args{'Ticket'}"); | |
302 | } | |
303 | ||
304 | if ( ( $self->{'TransactionObj'} = $args{'TransactionObj'} ) ) { | |
305 | $self->{'TransactionObj'}->CurrentUser( $self->CurrentUser ); | |
306 | } | |
307 | else { | |
308 | $self->{'TransactionObj'} = RT::Transaction->new( $self->CurrentUser ); | |
309 | $self->{'TransactionObj'}->Load( $args{'Transaction'} ) | |
310 | || $RT::Logger->err( "$self couldn't load transaction $args{'Transaction'}"); | |
311 | } | |
312 | } | |
313 | ||
314 | ||
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 | ||
388 | =head2 NewItem | |
389 | ||
390 | Returns an empty new RT::Scrip item | |
391 | ||
392 | =cut | |
393 | ||
394 | sub NewItem { | |
395 | my $self = shift; | |
396 | return(RT::Scrip->new($self->CurrentUser)); | |
397 | } | |
398 | RT::Base->_ImportOverlays(); | |
399 | ||
400 | 1; |