X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Fbin%2Frt.in;h=c80577f4a3b5b0624d0e2148003fdce80d403647;hp=90369b5b385eebabbc2b717d1620cc431ddc77b8;hb=9c68254528b6f2c7d8c1921b452fa56064783782;hpb=289340780927b5bac2c7604d7317c3063c6dd8cc diff --git a/rt/bin/rt.in b/rt/bin/rt.in index 90369b5b3..c80577f4a 100644 --- a/rt/bin/rt.in +++ b/rt/bin/rt.in @@ -1,9 +1,15 @@ #!@PERL@ -w -# BEGIN LICENSE BLOCK +# BEGIN BPS TAGGED BLOCK {{{ # -# Copyright (c) 1996-2003 Jesse Vincent +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC +# # -# (Except where explictly superceded by other copyright notices) +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have @@ -15,13 +21,32 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# +# CONTRIBUTION SUBMISSION POLICY: # +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) # -# END LICENSE BLOCK +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} + +# Designed and implemented for Best Practical Solutions, LLC by +# Abhijit Menon-Sen use strict; @@ -30,6 +55,7 @@ use strict; use Cwd; use LWP; +use Text::ParseWords; use HTTP::Request::Common; # We derive configuration information from hardwired defaults, dotfiles, @@ -46,6 +72,8 @@ my %config = ( user => eval{(getpwuid($<))[0]} || $ENV{USER} || $ENV{USERNAME}, passwd => undef, server => 'http://localhost/rt/', + query => undef, + orderby => undef, ), config_from_file($ENV{RTCONFIG} || ".rtrc"), config_from_env() @@ -75,6 +103,7 @@ my $idlist = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+'; my %handlers = ( # handler => [ ...aliases... ], version => ["version", "ver"], + shell => ["shell"], logout => ["logout"], help => ["help", "man"], show => ["show", "cat"], @@ -86,18 +115,25 @@ my %handlers = ( grant => ["grant", "revoke"], ); -# Once we find and call an appropriate handler, we're done. - -my (%actions, $action); +my %actions; foreach my $fn (keys %handlers) { foreach my $alias (@{ $handlers{$fn} }) { $actions{$alias} = \&{"$fn"}; } } -if (@ARGV && exists $actions{$ARGV[0]}) { - $action = shift @ARGV; + +# Once we find and call an appropriate handler, we're done. + +sub handler { + my $action; + + if (@ARGV && exists $actions{$ARGV[0]}) { + $action = shift @ARGV; + } + $actions{$action || "help"}->($action || ()); } -$actions{$action || "help"}->($action || ()); + +handler(); exit; # Handler functions. @@ -105,6 +141,20 @@ exit; # # The following subs are handlers for each entry in %actions. +sub shell { + $|=1; + print "rt> "; + while (<>) { + chomp; + next if /^#/ || /^\s*$/; + + @ARGV = shellwords($_); + handler(); + print "rt> "; + } + print "\n"; +} + sub version { print "rt $VERSION\n"; } @@ -113,18 +163,21 @@ sub logout { submit("$REST/logout") if defined $session->cookie; } +my %help; sub help { my ($action, $type) = @_; - my (%help, $key); + my $key; # What help topics do we know about? - local $/ = undef; - foreach my $item (@{ Form::parse() }) { - my $title = $item->[2]{Title}; - my @titles = ref $title eq 'ARRAY' ? @$title : $title; + if (!%help) { + local $/ = undef; + foreach my $item (@{ Form::parse() }) { + my $title = $item->[2]{Title}; + my @titles = ref $title eq 'ARRAY' ? @$title : $title; - foreach $title (grep $_, @titles) { - $help{$title} = $item->[2]{Text}; + foreach $title (grep $_, @titles) { + $help{$title} = $item->[2]{Text}; + } } } @@ -166,7 +219,12 @@ sub help { # Displays a list of objects that match some specified condition. sub list { - my ($q, $type, %data, $orderby); + my ($q, $type, %data); + my $orderby = $config{orderby}; + + if ($config{orderby}) { + $data{orderby} = $config{orderby}; + } my $bad = 0; while (@ARGV) { @@ -179,7 +237,7 @@ sub list { $bad = 1, last unless get_var_argument(\%data); } elsif (/^-o$/) { - $orderby = shift @ARGV; + $data{'orderby'} = shift @ARGV; } elsif (/^-([isl])$/) { $data{format} = $1; @@ -200,7 +258,10 @@ sub list { $bad = 1; last; } } - + if (!defined $q) { + $q = $config{query}; + } + $type ||= "ticket"; unless ($type && defined $q) { my $item = $type ? "query string" : "object type"; @@ -209,7 +270,7 @@ sub list { } return help("list", $type) if $bad; - my $r = submit("$REST/search/$type", { query => $q, %data, orderby => $orderby || "" }); + my $r = submit("$REST/search/$type", { query => $q, %data }); print $r->content; } @@ -451,7 +512,7 @@ sub edit { if ($output) { print $text; - exit; + return; } my $synerr = 0; @@ -477,7 +538,7 @@ EDIT: } else { print $r->content; - exit -1; + return; } } print $r->content; @@ -506,7 +567,7 @@ sub comment { if (/-a/) { unless (-f $ARGV[0] && -r $ARGV[0]) { whine "Cannot read attachment: '$ARGV[0]'."; - exit -1; + return; } push @files, shift @ARGV; } @@ -514,7 +575,14 @@ sub comment { my $a = $_ eq "-b" ? \@bcc : \@cc; @$a = split /\s*,\s*/, shift @ARGV; } - elsif (/-m/) { $msg = shift @ARGV } + elsif (/-m/) { + $msg = shift @ARGV; + if ( $msg =~ /^-$/ ) { + undef $msg; + while () { $msg .= $_ } + } + } + elsif (/-w/) { $wtime = shift @ARGV } } elsif (!$id && m|^(?:ticket/)?($idlist)$|) { @@ -544,6 +612,7 @@ sub comment { Attachment => [ @files ], TimeWorked => $wtime || '', Text => $msg || '', + Status => '' } ]; @@ -555,7 +624,7 @@ sub comment { do { my $ntext = vi($text); - exit if ($error && $ntext eq $text); + return if ($error && $ntext eq $text); $text = $ntext; $form = Form::parse($text); $error = 0; @@ -567,7 +636,7 @@ sub comment { goto NEXT; } elsif (!@$o) { - exit; + return; } @files = @{ vsplit($k->{Attachment}) }; @@ -643,7 +712,7 @@ sub link { $bad = 1; } unless (exists $ltypes{lc $rel}) { - whine "Invalid relationship '$rel' specified."; + whine "Invalid link '$rel' specified."; $bad = 1; } %data = (id => $from, rel => $rel, to => $to, del => $del); @@ -733,10 +802,10 @@ sub submit { my ($head, $text) = split /\n\n/, $res->content, 2; my ($status, @headers) = split /\n/, $head; - $text =~ s/\n*$/\n/; + $text =~ s/\n*$/\n/ if ($text); # "RT/3.0.1 401 Credentials required" - if ($status !~ m#^RT/\d+(?:\.\d+)+(?:-?\w+)? (\d+) ([\w\s]+)$#) { + if ($status !~ m#^RT/\d+(?:\S+) (\d+) ([\w\s]+)$#) { warn "rt: Malformed RT response from $config{server}.\n"; warn "(Rerun with RTDEBUG=3 for details.)\n" if $config{debug} < 3; exit -1; @@ -812,7 +881,7 @@ sub submit { sub cookie { my ($self) = @_; my $cookie = $self->{sids}{$s}{$u}; - return defined $cookie ? "RT_SID=$cookie" : undef; + return defined $cookie ? "RT_SID_$cookie" : undef; } # Deletes the current session cookie. @@ -835,7 +904,7 @@ sub submit { my ($self, $response) = @_; my $cookie = $response->header("Set-Cookie"); - if (defined $cookie && $cookie =~ /^RT_SID=([0-9A-Fa-f]+);/) { + if (defined $cookie && $cookie =~ /^RT_SID_(.[^;,\s]+=[0-9A-Fa-f]+);/) { $self->{sids}{$s}{$u} = $1; } } @@ -852,7 +921,7 @@ sub submit { while () { chomp; next if /^$/ || /^#/; - next unless m#^https?://[^ ]+ \w+ [0-9A-Fa-f]+$#; + next unless m#^https?://[^ ]+ \w+ [^;,\s]+=[0-9A-Fa-f]+$#; my ($server, $user, $cookie) = split / /, $_; $sids->{$server}{$user} = $cookie; } @@ -1061,7 +1130,7 @@ sub Form::compose { sub config_from_env { my %env; - foreach my $k ("DEBUG", "USER", "PASSWD", "SERVER") { + foreach my $k ("DEBUG", "USER", "PASSWD", "SERVER", "QUERY", "ORDERBY") { if (exists $ENV{"RT$k"}) { $env{lc $k} = $ENV{"RT$k"}; } @@ -1106,13 +1175,14 @@ sub config_from_file { sub parse_config_file { my %cfg; my ($file) = @_; + local $_; # $_ may be aliased to a constant, from line 1163 open(CFG, $file) && do { while () { chomp; next if (/^#/ || /^\s*$/); - if (/^(user|passwd|server)\s+([^ ]+)$/) { + if (/^(user|passwd|server|query|orderby)\s+(.*)\s?$/) { $cfg{$1} = $2; } else { @@ -1189,7 +1259,7 @@ sub vsplit { my @values = ref $val eq 'ARRAY' ? @$val : $val; foreach my $line (map {split /\n/} @values) { - # XXX: This should become a real parser, à la Text::ParseWords. + # XXX: This should become a real parser, à la Text::ParseWords. $line =~ s/^\s+//; $line =~ s/\s+$//; push @words, split /\s*,\s*/, $line; @@ -1343,6 +1413,8 @@ Text: - server URL to RT server. - user RT username. - passwd RT user's password. + - query Default RT Query for list action + - orderby Default RT order for list action Blank and #-commented lines are ignored. @@ -1357,6 +1429,8 @@ Text: - RTDEBUG Numeric debug level. (Set to 3 for full logs.) - RTCONFIG Specifies a name other than ".rtrc" for the configuration file. + - RTQUERY Default RT Query for rt list + - RTORDERBY Default order for rt list -- @@ -1380,7 +1454,7 @@ Text: be used to specify more than one object of the same type. Note that the list must be a single argument (i.e., no spaces). For example, "user/root,1-3,5,7-10,ams" is a list of ten users; the same list - can also be written as "user/ams,root,1,2,3,5,7,8-20". + can also be written as "user/ams,root,1,2,3,5,7,8-10". Examples: @@ -1599,13 +1673,15 @@ Text: -t type Specifies object type. -f a,b,c Restrict the display to the specified fields. -S var=val Submits the specified variable with the request. - + -v Verbose display Examples: rt show -t ticket -f id,subject,status 1-3 rt show ticket/3/attachments/29 rt show ticket/3/attachments/29/content rt show ticket/1-3/links + rt show ticket/3/history + rt show -v ticket/3/history rt show -t user 2 -- @@ -1701,7 +1777,7 @@ Text: Examples: - rt comment -t 'Not worth fixing.' -a stddisclaimer.h 23 + rt comment -m 'Not worth fixing.' -a stddisclaimer.h 23 -- @@ -1721,16 +1797,16 @@ Text: Syntax: - rt link [-d] + rt link [-d] Creates (or, with -d, deletes) a link between the specified tickets. - The relationship can (irrespective of case) be any of: + The link can (irrespective of case) be any of: DependsOn/DependedOnBy: A depends upon B (or vice versa). RefersTo/ReferredToBy: A refers to B (or vice versa). MemberOf/HasMember: A is a member of B (or vice versa). - To view a ticket's relationships, use "rt show ticket/3/links". (See + To view a ticket's links, use "rt show ticket/3/links". (See "rt help ticket" and "rt help show".) Options: