2 # BEGIN BPS TAGGED BLOCK {{{
6 # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
7 # <sales@bestpractical.com>
9 # (Except where explicitly superseded by other copyright notices)
14 # This work is made available to you under the terms of Version 2 of
15 # the GNU General Public License. A copy of that license should have
16 # been provided with this software, but in any event can be snarfed
19 # This work is distributed in the hope that it will be useful, but
20 # WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 # General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 # 02110-1301 or visit their web page on the internet at
28 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
31 # CONTRIBUTION SUBMISSION POLICY:
33 # (The following paragraph is not intended to limit the rights granted
34 # to you to modify and distribute this software under the terms of
35 # the GNU General Public License and is only of importance to you if
36 # you choose to contribute your changes and enhancements to the
37 # community by submitting them to Best Practical Solutions, LLC.)
39 # By intentionally submitting any modifications, corrections or
40 # derivatives to this work, or any other work intended for use with
41 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
42 # you are the copyright holder for those contributions and you grant
43 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44 # royalty-free, perpetual, license to use, copy, create derivative
45 # works based on those contributions, and sublicense and distribute
46 # those contributions and any derivatives thereof.
48 # END BPS TAGGED BLOCK }}}
53 use lib '/www/data/rt/rt-perl/current-perl10/share/perl5';
54 use lib '/www/data/rt/rt-perl/current-perl10/lib/perl5';
55 use lib '/www/data/rt/rt-perl/current-perl10/lib64/perl5';
57 use lib ("/www/var/rt/local/lib", "/www/var/rt/lib");
59 # fix lib paths, some may be relative
62 my @libs = ("lib", "local/lib");
66 unless ( File::Spec->file_name_is_absolute($lib) ) {
68 if ( File::Spec->file_name_is_absolute(__FILE__) ) {
69 $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
74 $bin_path = $FindBin::Bin;
77 $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
88 use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
90 #Clean out all the nasties from the environment
93 my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
94 $template, $template_id, $transaction, $transaction_type, $help, $log, $verbose );
96 "search=s" => \$search,
97 "search-arg=s" => \$search_arg,
98 "condition=s" => \$condition,
99 "condition-arg=s" => \$condition_arg,
100 "action-arg=s" => \$action_arg,
101 "action=s" => \$action,
102 "template=s" => \$template,
103 "template-id=s" => \$template_id,
104 "transaction=s" => \$transaction,
105 "transaction-type=s" => \$transaction_type,
107 "verbose|v" => \$verbose,
111 # Load the config file
114 # adjust logging to the screen according to options
115 RT->Config->Set( LogToScreen => $log ) if $log;
117 #Connect to the database and get RT::SystemUser and RT::Nobody loaded
121 require RT::Template;
123 #Get the current user all loaded
124 my $CurrentUser = GetCurrentUser();
126 # show help even if there is no current user
129 unless ( $CurrentUser->Id ) {
130 print loc("No RT user found. Please consult your RT administrator.");
134 help() unless $search && $action;
136 $transaction = lc( $transaction||'' );
137 if ( $transaction && $transaction !~ /^(first|all|last)$/i ) {
138 print STDERR loc("--transaction argument could be only 'first', 'last' or 'all'");
142 if ( $template && $template_id ) {
143 print STDERR loc("--template-id is deprecated argument and can not be used with --template");
146 elsif ( $template_id ) {
148 $template = $template_id;
151 # We _must_ have a search object
152 load_module($search);
153 load_module($action) if ($action);
154 load_module($condition) if ($condition);
156 my $void_scrip = RT::Scrip->new( $CurrentUser );
157 my $void_scrip_action = RT::ScripAction->new( $CurrentUser );
159 #At the appointed time:
161 #find a bunch of tickets
162 my $tickets = RT::Tickets->new($CurrentUser);
163 $search = $search->new(
164 TicketsObj => $tickets,
165 Argument => $search_arg,
166 CurrentUser => $CurrentUser
170 #for each ticket we've found
171 while ( my $ticket = $tickets->Next() ) {
172 print $ticket->Id() . ":\n" if ($verbose);
174 my $template_obj = get_template( $ticket );
176 if ( $transaction ) {
177 my $txns = get_transactions($ticket);
179 while ( my $txn = $txns->Next ) {
180 print "\t".loc("Using transaction #[_1]...", $txn->id)."\n"
182 process($ticket, $txn, $template_obj);
185 print "\t".loc("Couldn't find suitable transaction, skipping")."\n"
186 if $verbose && !$found;
188 print "\t".loc("Processing without transaction, some conditions and actions may fail. Consider using --transaction argument")."\n"
191 process($ticket, undef, $template_obj);
197 my $transaction = shift;
198 my $template_obj = shift;
200 # perform some more advanced check
202 my $condition_obj = $condition->new(
203 TransactionObj => $transaction,
204 TicketObj => $ticket,
205 ScripObj => $void_scrip,
206 TemplateObj => $template_obj,
207 Argument => $condition_arg,
208 CurrentUser => $CurrentUser,
211 # if the condition doesn't apply, get out of here
213 return unless $condition_obj->IsApplicable;
214 print "\t".loc("Condition matches...")."\n" if $verbose;
218 my $action_obj = $action->new(
219 TicketObj => $ticket,
220 TransactionObj => $transaction,
221 TemplateObj => $template_obj,
222 Argument => $action_arg,
223 ScripObj => $void_scrip,
224 ScripActionObj => $void_scrip_action,
225 CurrentUser => $CurrentUser,
228 #if our preparation, move onto the next ticket
229 return unless $action_obj->Prepare;
230 print "\t".loc("Action prepared...")."\n" if $verbose;
233 return unless $action_obj->Commit;
234 print "\t".loc("Action committed.")."\n" if $verbose;
237 # =head2 get_transactions
239 # Takes ticket and returns L<RT::Transactions> object with transactions
240 # of the ticket according to command line arguments C<--transaction>
241 # and <--transaction-type>.
245 sub get_transactions {
247 my $txns = $ticket->Transactions;
248 my $order = $transaction eq 'last'? 'DESC': 'ASC';
250 { FIELD => 'Created', ORDER => $order },
251 { FIELD => 'id', ORDER => $order },
253 if ( $transaction_type ) {
254 $transaction_type =~ s/^\s+//;
255 $transaction_type =~ s/\s+$//;
256 foreach my $type ( split /\s*,\s*/, $transaction_type ) {
257 $txns->Limit( FIELD => 'Type', VALUE => $type, ENTRYAGGREGATOR => 'OR' );
260 $txns->RowsPerPage(1) unless $transaction eq 'all';
264 # =head2 get_template
266 # Takes a ticket and returns a template according to command line options.
273 return undef unless $template;
275 unless ( $template =~ /\D/ ) {
277 return $cache if $cache;
279 my $cache = RT::Template->new( RT->SystemUser );
280 $cache->Load( $template );
281 die "Failed to load template '$template'"
286 my $queue = $ticket->Queue;
287 return $cache->{ $queue } if $cache->{ $queue };
289 my $res = RT::Template->new( RT->SystemUser );
290 $res->LoadQueueTemplate( Queue => $queue, Name => $template );
291 unless ( $res->id ) {
292 $res->LoadGlobalTemplate( $template );
293 die "Failed to load template '$template', either for queue #$queue or global"
296 return $cache->{ $queue } = $res;
302 # Loads a perl module, dying nicely if it can't find it.
308 eval "require $modname";
310 die loc( "Failed to load module [_1]. ([_2])", $modname, $@ );
318 print loc( "[_1] is a tool to act on tickets from an external scheduling tool, such as cron.", $0 )
320 print loc("It takes several arguments:") . "\n\n";
323 . loc( "[_1] - Specify the search module you want to use", "--search" )
326 . loc( "[_1] - An argument to pass to [_2]", "--search-arg", "--search" )
330 . loc( "[_1] - Specify the condition module you want to use", "--condition" )
333 . loc( "[_1] - An argument to pass to [_2]", "--condition-arg", "--condition" )
336 . loc( "[_1] - Specify the action module you want to use", "--action" )
339 . loc( "[_1] - An argument to pass to [_2]", "--action-arg", "--action" )
342 . loc( "[_1] - Specify name or id of template(s) you want to use", "--template" )
345 . loc( "[_1] - Specify if you want to use either 'first', 'last' or 'all' transactions", "--transaction" )
348 . loc( "[_1] - Specify the comma separated list of transactions' types you want to use", "--transaction-type" )
351 . loc( "[_1] - Adjust LogToScreen config option", "--log" ) . "\n";
353 . loc( "[_1] - Output status updates to STDOUT", "--verbose" ) . "\n";
356 print loc("Security:")."\n";
357 print loc("This tool allows the user to run arbitrary perl modules from within RT.")." ".
358 loc("If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT.")." ".
359 loc("It is incredibly important that nonprivileged users not be allowed to run this tool."). " " .
360 loc("It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool.")."\n";
362 print loc("Example:");
365 . loc( "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they are overdue:"
369 print " bin/rt-crontool \\\n";
370 print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
371 print " --condition RT::Condition::Overdue \\\n";
372 print " --action RT::Action::SetPriority --action-arg 99 \\\n";
373 print " --verbose\n";
376 print loc("Escalate tickets"). "\n";
377 print " bin/rt-crontool \\\n";
378 print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
379 print" --action RT::Action::EscalatePriority\n";
393 rt-crontool - a tool to act on tickets from an external scheduling tool
397 # find all active tickets in the queue 'general' and set their priority to 99 if they are overdue:
399 --search RT::Search::ActiveTicketsInQueue --search-arg general \
400 --condition RT::Condition::Overdue \
401 --action RT::Action::SetPriority --action-arg 99 \
406 --search RT::Search::ActiveTicketsInQueue --search-arg general \
407 --action RT::Action::EscalatePriority
411 This script is a tool to act on tickets from an external scheduling tool, such
416 This tool allows the user to run arbitrary perl modules from within RT. If
417 this tool were setgid, a hostile local user could use this tool to gain
418 administrative access to RT. It is incredibly important that nonprivileged
419 users not be allowed to run this tool. It is suggested that you create a
420 non-privileged unix user with the correct group membership and RT access to
430 Specify the search module you want to use
434 An argument to pass to --search
438 Specify the condition module you want to use
442 An argument to pass to --condition
446 Specify the action module you want to use
450 An argument to pass to --action
454 Specify name or id of template(s) you want to use
458 Specify if you want to use either 'first', 'last' or 'all' transactions
461 =item transaction-type
463 Specify the comma separated list of transactions' types you want to use
467 Adjust LogToScreen config option
471 Output status updates to STDOUT