1# BEGIN BPS TAGGED BLOCK {{{ 2# 3# COPYRIGHT: 4# 5# This software is Copyright (c) 1996-2021 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 49package RT::Interface::CLI; 50use strict; 51use warnings; 52 53use RT::Base; 54 55use base 'Exporter'; 56our @EXPORT_OK = qw(CleanEnv GetCurrentUser debug loc Init); 57 58=head1 NAME 59 60RT::Interface::CLI - helper functions for creating a commandline RT interface 61 62=head1 SYNOPSIS 63 64 use lib "/opt/rt4/local/lib", "/opt/rt4/lib"; 65 66 use RT::Interface::CLI qw(GetCurrentUser Init loc); 67 68 # Create a hash to hold parsed values 69 my %OPT = ( 70 "id" => 1, 71 ); 72 73 # Process command-line arguments, load the configuration, and connect 74 # to the database. See below for options provided by default. 75 Init( 76 \%OPT, 77 "id=i", # Getopt::Long options 78 ); 79 80 print "Got an id: " . $OPT{'id'}; 81 82 # Get the current user all loaded 83 my $CurrentUser = GetCurrentUser(); 84 85 print loc('Hello!'); # Synonym of $CurrentUser->loc('Hello!'); 86 87=head1 DESCRIPTION 88 89The following methods can be loaded in your RT CLI script. 90 91=head1 METHODS 92 93 94=cut 95 96{ 97 98 my $CurrentUser; # shared betwen GetCurrentUser and loc 99 100 101=head2 GetCurrentUser 102 103Figures out the uid of the current user and returns an RT::CurrentUser object 104loaded with that user. if the current user isn't found, returns a copy of RT::Nobody. 105 106=cut 107 108sub GetCurrentUser { 109 110 require RT::CurrentUser; 111 112 #Instantiate a user object 113 114 my $Gecos= (getpwuid($<))[0]; 115 116 #If the current user is 0, then RT will assume that the User object 117 #is that of the currentuser. 118 119 $CurrentUser = RT::CurrentUser->new(); 120 $CurrentUser->LoadByGecos($Gecos); 121 122 unless ($CurrentUser->Id) { 123 $RT::Logger->error("No user with a GECOS (unix login) of '$Gecos' was found."); 124 } 125 126 return($CurrentUser); 127} 128 129=head2 loc 130 131 Synonym of $CurrentUser->loc(). 132 133=cut 134 135sub loc { 136 die "No current user yet" unless $CurrentUser ||= RT::CurrentUser->new; 137 return $CurrentUser->loc(@_); 138} 139 140} 141 142sub ShowHelp { 143 my $self = shift; 144 my %args = @_; 145 require Pod::Usage; 146 Pod::Usage::pod2usage( 147 -message => $args{'Message'}, 148 -exitval => $args{'ExitValue'} || 0, 149 -verbose => 99, 150 -sections => $args{'Sections'} || ($args{'ExitValue'} 151 ? 'NAME|USAGE' 152 : 'NAME|USAGE|OPTIONS|DESCRIPTION' 153 ), 154 ); 155} 156 157=head2 Init 158 159A shim for L<Getopt::Long/GetOptions> which automatically adds a 160C<--help> option if it is not supplied. It then calls L<RT/LoadConfig> 161and L<RT/Init>. 162 163It sets the C<LogToSTDERR> setting to C<warning>, to ensure that the 164user sees all relevant warnings. It also adds C<--quiet> and 165C<--verbose> options, which adjust the C<LogToSTDERR> value to C<error> 166or C<debug>, respectively. 167 168If C<debug> is provided as a parameter, it added as an alias for 169C<--verbose>. 170 171C<statement-log> provides a command-line version of the C<$StatementLog> 172option in the main RT config file. This allows users to log SQL 173for queries run in a CLI script in the same manner as the web UI. 174It accepts log levels like C<$StatementLog>: 175 176 --statement-log=debug 177 178=cut 179 180sub Init { 181 require Getopt::Long; 182 require Pod::Usage; 183 184 my %exists; 185 my @args; 186 my $hash; 187 if (ref $_[0]) { 188 $hash = shift(@_); 189 for (@_) { 190 m/^([a-zA-Z0-9-]+)/; 191 $exists{$1}++; 192 push @args, $_ => \($hash->{$1}); 193 } 194 } else { 195 $hash = {}; 196 @args = @_; 197 while (@_) { 198 my $key = shift(@_); 199 $exists{$key}++; 200 shift(@_); 201 } 202 } 203 204 push @args, "help|h!" => \($hash->{help}) 205 unless $exists{help}; 206 207 push @args, "verbose|v!" => \($hash->{verbose}) 208 unless ( $exists{verbose} || $exists{'verbose|v'} ); 209 210 push @args, "debug!" => \($hash->{verbose}) 211 if $exists{debug}; 212 213 push @args, "quiet|q!" => \($hash->{quiet}) 214 unless $exists{quiet}; 215 216 push @args, "statement-log=s" => \($hash->{'statement-log'}) 217 unless $exists{'statement-log'}; 218 219 my $ok = Getopt::Long::GetOptions( @args ); 220 Pod::Usage::pod2usage(1) if not $ok and not defined wantarray; 221 222 return unless $ok; 223 224 Pod::Usage::pod2usage({ verbose => 2}) 225 if not $exists{help} and $hash->{help}; 226 227 require RT; 228 RT::LoadConfig(); 229 230 if (not $exists{quiet} and $hash->{quiet}) { 231 RT->Config->Set(LogToSTDERR => "error"); 232 } elsif (not $exists{verbose} and $hash->{verbose}) { 233 RT->Config->Set(LogToSTDERR => "debug"); 234 } else { 235 RT->Config->Set(LogToSTDERR => "warning"); 236 } 237 238 RT->Config->Set( 'StatementLog', $hash->{'statement-log'} ) if defined $hash->{'statement-log'}; 239 RT::Init(); 240 $RT::Handle->LogSQLStatements(1) if RT->Config->Get('StatementLog'); 241 242 $| = 1; 243 244 return $ok; 245} 246 247RT::Base->_ImportOverlays(); 248 249END { 250 251 # When pod2usage is called (e.g. with --help), RT.pm won't be 252 # required and directly calling RT->Config will error out. 253 RT::Interface::Web::LogRecordedSQLStatements( RequestData => { Path => '/' } ) 254 if RT->can('Config') && RT->Config->Get('StatementLog'); 255} 256 2571; 258