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