This commit was manufactured by cvs2svn to create tag 'BEFORE_RT_3_2_2'. BEFORE_RT_3_2_2
authorcvs2git <cvs2git>
Thu, 2 Dec 2004 10:18:36 +0000 (10:18 +0000)
committercvs2git <cvs2git>
Thu, 2 Dec 2004 10:18:36 +0000 (10:18 +0000)
24 files changed:
rt/FREESIDE_MODIFIED [new file with mode: 0644]
rt/bin/rt [deleted file]
rt/config.layout [deleted file]
rt/config.layout.in [new file with mode: 0644]
rt/etc/RT_SiteConfig.pm
rt/etc/schema.Oracle [deleted file]
rt/html/Elements/Footer
rt/html/Elements/Header
rt/html/Elements/PageLayout
rt/html/Elements/SimpleSearch
rt/html/Elements/Tabs
rt/html/NoAuth/images/small-logo.png [new file with mode: 0644]
rt/html/NoAuth/webrt.css
rt/html/Ticket/Elements/AddCustomers [new file with mode: 0644]
rt/html/Ticket/Elements/EditCustomers [new file with mode: 0644]
rt/html/Ticket/Elements/ShowCustomers [new file with mode: 0644]
rt/html/Ticket/Elements/ShowSummary
rt/html/Ticket/Elements/Tabs
rt/html/Ticket/ModifyCustomers.html [new file with mode: 0644]
rt/lib/RT/Interface/Web_Vendor.pm [new file with mode: 0644]
rt/lib/RT/URI/freeside.pm [new file with mode: 0644]
rt/sbin/rt-setup-database [deleted file]
rt/sbin/rt-setup-database.in
rt/sbin/rt-test-dependencies [deleted file]

diff --git a/rt/FREESIDE_MODIFIED b/rt/FREESIDE_MODIFIED
new file mode 100644 (file)
index 0000000..67bcfec
--- /dev/null
@@ -0,0 +1,20 @@
+sbin/rt-setup-database
+sbin/rt-setup-database.in
+config.layout
+config.layout.in
+etc/RT_SiteConfig.pm
+lib/RT/Interface/Web_Vendor.pm
+lib/RT/URI/freeside.pm
+html/Elements/Header
+html/Elements/PageLayout
+html/Elements/SimpleSearch
+html/Elements/Tabs
+html/Elements/Footer
+html/Ticket/Elements/AddCustomers
+html/Ticket/Elements/EditCustomers
+html/Ticket/Elements/ShowCustomers
+html/Ticket/Elements/ShowSummary
+html/Ticket/Elements/Tabs
+html/Ticket/ModifyCustomers.html
+html/NoAuth/images/small-logo.png
+html/NoAuth/webrt.css
diff --git a/rt/bin/rt b/rt/bin/rt
deleted file mode 100755 (executable)
index d9f8a3f..0000000
--- a/rt/bin/rt
+++ /dev/null
@@ -1,1816 +0,0 @@
-#!/usr/bin/perl -w
-# BEGIN LICENSE BLOCK
-# 
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-# 
-# (Except where explictly superceded by other copyright notices)
-# 
-# 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
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-# 
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# 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.
-# 
-# 
-# END LICENSE BLOCK
-
-use strict;
-
-# This program is intentionally written to have as few non-core module
-# dependencies as possible. It should stay that way.
-
-use Cwd;
-use LWP;
-use HTTP::Request::Common;
-
-# We derive configuration information from hardwired defaults, dotfiles,
-# and the RT* environment variables (in increasing order of precedence).
-# Session information is stored in ~/.rt_sessions.
-
-my $VERSION = 0.02;
-my $HOME = eval{(getpwuid($<))[7]}
-           || $ENV{HOME} || $ENV{LOGDIR} || $ENV{HOMEPATH}
-           || ".";
-my %config = (
-    (
-        debug   => 0,
-        user    => eval{(getpwuid($<))[0]} || $ENV{USER} || $ENV{USERNAME},
-        passwd  => undef,
-        server  => 'http://localhost/rt/',
-    ),
-    config_from_file($ENV{RTCONFIG} || ".rtrc"),
-    config_from_env()
-);
-my $session = new Session("$HOME/.rt_sessions");
-my $REST = "$config{server}/REST/1.0";
-
-sub whine;
-sub DEBUG { warn @_ if $config{debug} >= shift }
-
-# These regexes are used by command handlers to parse arguments.
-# (XXX: Ask Autrijus how i18n changes these definitions.)
-
-my $name   = '[\w.-]+';
-my $field  = '[a-zA-Z][a-zA-Z0-9_-]*';
-my $label  = '[a-zA-Z0-9@_.+-]+';
-my $labels = "(?:$label,)*$label";
-my $idlist = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+';
-
-# Our command line looks like this:
-#
-#     rt <action> [options] [arguments]
-#
-# We'll parse just enough of it to decide upon an action to perform, and
-# leave the rest to per-action handlers to interpret appropriately.
-
-my %handlers = (
-#   handler     => [ ...aliases... ],
-    version     => ["version", "ver"],
-    logout      => ["logout"],
-    help        => ["help", "man"],
-    show        => ["show", "cat"],
-    edit        => ["create", "edit", "new", "ed"],
-    list        => ["search", "list", "ls"],
-    comment     => ["comment", "correspond"],
-    link        => ["link", "ln"],
-    merge       => ["merge"],
-    grant       => ["grant", "revoke"],
-);
-
-# Once we find and call an appropriate handler, we're done.
-
-my (%actions, $action);
-foreach my $fn (keys %handlers) {
-    foreach my $alias (@{ $handlers{$fn} }) {
-        $actions{$alias} = \&{"$fn"};
-    }
-}
-if (@ARGV && exists $actions{$ARGV[0]}) {
-    $action = shift @ARGV;
-}
-$actions{$action || "help"}->($action || ());
-exit;
-
-# Handler functions.
-# ------------------
-#
-# The following subs are handlers for each entry in %actions.
-
-sub version {
-    print "rt $VERSION\n";
-}
-
-sub logout {
-    submit("$REST/logout") if defined $session->cookie;
-}
-
-sub help {
-    my ($action, $type) = @_;
-    my (%help, $key);
-
-    # What help topics do we know about?
-    local $/ = undef;
-    foreach my $item (@{ Form::parse(<DATA>) }) {
-        my $title = $item->[2]{Title};
-        my @titles = ref $title eq 'ARRAY' ? @$title : $title;
-
-        foreach $title (grep $_, @titles) {
-            $help{$title} = $item->[2]{Text};
-        }
-    }
-
-    # What does the user want help with?
-    undef $action if ($action && $actions{$action} eq \&help);
-    unless ($action || $type) {
-        # If we don't know, we'll look for clues in @ARGV.
-        foreach (@ARGV) {
-            if (exists $help{$_}) { $key = $_; last; }
-        }
-        unless ($key) {
-            # Tolerate possibly plural words.
-            foreach (@ARGV) {
-                if ($_ =~ s/s$// && exists $help{$_}) { $key = $_; last; }
-            }
-        }
-    }
-
-    if ($type && $action) {
-        $key = "$type.$action";
-    }
-    $key ||= $type || $action || "introduction";
-
-    # Find a suitable topic to display.
-    while (!exists $help{$key}) {
-        if ($type && $action) {
-            if ($key eq "$type.$action") { $key = $action;        }
-            elsif ($key eq $action)      { $key = $type;          }
-            else                         { $key = "introduction"; }
-        }
-        else {
-            $key = "introduction";
-        }
-    }
-
-    print STDERR $help{$key}, "\n\n";
-}
-
-# Displays a list of objects that match some specified condition.
-
-sub list {
-    my ($q, $type, %data, $orderby);
-    my $bad = 0;
-
-    while (@ARGV) {
-        $_ = shift @ARGV;
-
-        if (/^-t$/) {
-            $bad = 1, last unless defined($type = get_type_argument());
-        }
-        elsif (/^-S$/) {
-            $bad = 1, last unless get_var_argument(\%data);
-        }
-        elsif (/^-o$/) {
-            $orderby = shift @ARGV;
-        }
-        elsif (/^-([isl])$/) {
-            $data{format} = $1;
-        }
-        elsif (/^-f$/) {
-            if ($ARGV[0] !~ /^(?:(?:$field,)*$field)$/) {
-                whine "No valid field list in '-f $ARGV[0]'.";
-                $bad = 1; last;
-            }
-            $data{fields} = shift @ARGV;
-        }
-        elsif (!defined $q && !/^-/) {
-            $q = $_;
-        }
-        else {
-            my $datum = /^-/ ? "option" : "argument";
-            whine "Unrecognised $datum '$_'.";
-            $bad = 1; last;
-        }
-    }
-
-    $type ||= "ticket";
-    unless ($type && defined $q) {
-        my $item = $type ? "query string" : "object type";
-        whine "No $item specified.";
-        $bad = 1;
-    }
-    return help("list", $type) if $bad;
-
-    my $r = submit("$REST/search/$type", { query => $q, %data, orderby => $orderby || "" });
-    print $r->content;
-}
-
-# Displays selected information about a single object.
-
-sub show {
-    my ($type, @objects, %data);
-    my $slurped = 0;
-    my $bad = 0;
-
-    while (@ARGV) {
-        $_ = shift @ARGV;
-
-        if (/^-t$/) {
-            $bad = 1, last unless defined($type = get_type_argument());
-        }
-        elsif (/^-S$/) {
-            $bad = 1, last unless get_var_argument(\%data);
-        }
-        elsif (/^-([isl])$/) {
-            $data{format} = $1;
-        }
-        elsif (/^-$/ && !$slurped) {
-            chomp(my @lines = <STDIN>);
-            foreach (@lines) {
-                unless (is_object_spec($_, $type)) {
-                    whine "Invalid object on STDIN: '$_'.";
-                    $bad = 1; last;
-                }
-                push @objects, $_;
-            }
-            $slurped = 1;
-        }
-        elsif (/^-f$/) {
-            if ($ARGV[0] !~ /^(?:(?:$field,)*$field)$/) {
-                whine "No valid field list in '-f $ARGV[0]'.";
-                $bad = 1; last;
-            }
-            $data{fields} = shift @ARGV;
-        }
-        elsif (my $spec = is_object_spec($_, $type)) {
-            push @objects, $spec;
-        }
-        else {
-            my $datum = /^-/ ? "option" : "argument";
-            whine "Unrecognised $datum '$_'.";
-            $bad = 1; last;
-        }
-    }
-
-    unless (@objects) {
-        whine "No objects specified.";
-        $bad = 1;
-    }
-    return help("show", $type) if $bad;
-
-    my $r = submit("$REST/show", { id => \@objects, %data });
-    print $r->content;
-}
-
-# To create a new object, we ask the server for a form with the defaults
-# filled in, allow the user to edit it, and send the form back.
-#
-# To edit an object, we must ask the server for a form representing that
-# object, make changes requested by the user (either on the command line
-# or interactively via $EDITOR), and send the form back.
-
-sub edit {
-    my ($action) = @_;
-    my (%data, $type, @objects);
-    my ($cl, $text, $edit, $input, $output);
-
-    use vars qw(%set %add %del);
-    %set = %add = %del = ();
-    my $slurped = 0;
-    my $bad = 0;
-    
-    while (@ARGV) {
-        $_ = shift @ARGV;
-
-        if    (/^-e$/) { $edit = 1 }
-        elsif (/^-i$/) { $input = 1 }
-        elsif (/^-o$/) { $output = 1 }
-        elsif (/^-t$/) {
-            $bad = 1, last unless defined($type = get_type_argument());
-        }
-        elsif (/^-S$/) {
-            $bad = 1, last unless get_var_argument(\%data);
-        }
-        elsif (/^-$/ && !($slurped || $input)) {
-            chomp(my @lines = <STDIN>);
-            foreach (@lines) {
-                unless (is_object_spec($_, $type)) {
-                    whine "Invalid object on STDIN: '$_'.";
-                    $bad = 1; last;
-                }
-                push @objects, $_;
-            }
-            $slurped = 1;
-        }
-        elsif (/^set$/i) {
-            my $vars = 0;
-
-            while (@ARGV && $ARGV[0] =~ /^($field)([+-]?=)(.*)$/) {
-                my ($key, $op, $val) = ($1, $2, $3);
-                my $hash = ($op eq '=') ? \%set : ($op =~ /^\+/) ? \%add : \%del;
-
-                vpush($hash, lc $key, $val);
-                shift @ARGV;
-                $vars++;
-            }
-            unless ($vars) {
-                whine "No variables to set.";
-                $bad = 1; last;
-            }
-            $cl = $vars;
-        }
-        elsif (/^(?:add|del)$/i) {
-            my $vars = 0;
-            my $hash = ($_ eq "add") ? \%add : \%del;
-
-            while (@ARGV && $ARGV[0] =~ /^($field)=(.*)$/) {
-                my ($key, $val) = ($1, $2);
-
-                vpush($hash, lc $key, $val);
-                shift @ARGV;
-                $vars++;
-            }
-            unless ($vars) {
-                whine "No variables to set.";
-                $bad = 1; last;
-            }
-            $cl = $vars;
-        }
-        elsif (my $spec = is_object_spec($_, $type)) {
-            push @objects, $spec;
-        }
-        else {
-            my $datum = /^-/ ? "option" : "argument";
-            whine "Unrecognised $datum '$_'.";
-            $bad = 1; last;
-        }
-    }
-
-    if ($action =~ /^ed(?:it)?$/) {
-        unless (@objects) {
-            whine "No objects specified.";
-            $bad = 1;
-        }
-    }
-    else {
-        if (@objects) {
-            whine "You shouldn't specify objects as arguments to $action.";
-            $bad = 1;
-        }
-        unless ($type) {
-            whine "What type of object do you want to create?";
-            $bad = 1;
-        }
-        @objects = ("$type/new");
-    }
-    return help($action, $type) if $bad;
-
-    # We need a form to make changes to. We usually ask the server for
-    # one, but we can avoid that if we are fed one on STDIN, or if the
-    # user doesn't want to edit the form by hand, and the command line
-    # specifies only simple variable assignments.
-
-    if ($input) {
-        local $/ = undef;
-        $text = <STDIN>;
-    }
-    elsif ($edit || %add || %del || !$cl) {
-        my $r = submit("$REST/show", { id => \@objects, format => 'l' });
-        $text = $r->content;
-    }
-
-    # If any changes were specified on the command line, apply them.
-    if ($cl) {
-        if ($text) {
-            # We're updating forms from the server.
-            my $forms = Form::parse($text);
-
-            foreach my $form (@$forms) {
-                my ($c, $o, $k, $e) = @$form;
-                my ($key, $val);
-
-                next if ($e || !@$o);
-
-                local %add = %add;
-                local %del = %del;
-                local %set = %set;
-
-                # Make changes to existing fields.
-                foreach $key (@$o) {
-                    if (exists $add{lc $key}) {
-                        $val = delete $add{lc $key};
-                        vpush($k, $key, $val);
-                        $k->{$key} = vsplit($k->{$key}) if $val =~ /[,\n]/;
-                    }
-                    if (exists $del{lc $key}) {
-                        $val = delete $del{lc $key};
-                        my %val = map {$_=>1} @{ vsplit($val) };
-                        $k->{$key} = vsplit($k->{$key});
-                        @{$k->{$key}} = grep {!exists $val{$_}} @{$k->{$key}};
-                    }
-                    if (exists $set{lc $key}) {
-                        $k->{$key} = delete $set{lc $key};
-                    }
-                }
-                
-                # Then update the others.
-                foreach $key (keys %set) { vpush($k, $key, $set{$key}) }
-                foreach $key (keys %add) {
-                    vpush($k, $key, $add{$key});
-                    $k->{$key} = vsplit($k->{$key});
-                }
-                push @$o, (keys %add, keys %set);
-            }
-
-            $text = Form::compose($forms);
-        }
-        else {
-            # We're rolling our own set of forms.
-            my @forms;
-            foreach (@objects) {
-                my ($type, $ids, $args) =
-                    m{^($name)/($idlist|$labels)(?:(/.*))?$}o;
-
-                $args ||= "";
-                foreach my $obj (expand_list($ids)) {
-                    my %set = (%set, id => "$type/$obj$args");
-                    push @forms, ["", [keys %set], \%set];
-                }
-            }
-            $text = Form::compose(\@forms);
-        }
-    }
-
-    if ($output) {
-        print $text;
-        exit;
-    }
-
-    my $synerr = 0;
-
-EDIT:
-    # We'll let the user edit the form before sending it to the server,
-    # unless we have enough information to submit it non-interactively.
-    if ($edit || (!$input && !$cl)) {
-        my $newtext = vi($text);
-        # We won't resubmit a bad form unless it was changed.
-        $text = ($synerr && $newtext eq $text) ? undef : $newtext;
-    }
-
-    if ($text) {
-        my $r = submit("$REST/edit", {content => $text, %data});
-        if ($r->code == 409) {
-            # If we submitted a bad form, we'll give the user a chance
-            # to correct it and resubmit.
-            if ($edit || (!$input && !$cl)) {
-                $text = $r->content;
-                $synerr = 1;
-                goto EDIT;
-            }
-            else {
-                print $r->content;
-                exit -1;
-            }
-        }
-        print $r->content;
-    }
-}
-
-# We roll "comment" and "correspond" into the same handler.
-
-sub comment {
-    my ($action) = @_;
-    my (%data, $id, @files, @bcc, @cc, $msg, $wtime, $edit);
-    my $bad = 0;
-
-    while (@ARGV) {
-        $_ = shift @ARGV;
-
-        if (/^-e$/) {
-            $edit = 1;
-        }
-        elsif (/^-[abcmw]$/) {
-            unless (@ARGV) {
-                whine "No argument specified with $_.";
-                $bad = 1; last;
-            }
-
-            if (/-a/) {
-                unless (-f $ARGV[0] && -r $ARGV[0]) {
-                    whine "Cannot read attachment: '$ARGV[0]'.";
-                    exit -1;
-                }
-                push @files, shift @ARGV;
-            }
-            elsif (/-([bc])/) {
-                my $a = $_ eq "-b" ? \@bcc : \@cc;
-                @$a = split /\s*,\s*/, shift @ARGV;
-            }
-            elsif (/-m/) { $msg = shift @ARGV }
-            elsif (/-w/) { $wtime = shift @ARGV }
-        }
-        elsif (!$id && m|^(?:ticket/)?($idlist)$|) {
-            $id = $1;
-        }
-        else {
-            my $datum = /^-/ ? "option" : "argument";
-            whine "Unrecognised $datum '$_'.";
-            $bad = 1; last;
-        }
-    }
-
-    unless ($id) {
-        whine "No object specified.";
-        $bad = 1;
-    }
-    return help($action, "ticket") if $bad;
-
-    my $form = [
-        "",
-        [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Text" ],
-        {
-            Ticket     => $id,
-            Action     => $action,
-            Cc         => [ @cc ],
-            Bcc        => [ @bcc ],
-            Attachment => [ @files ],
-            TimeWorked => $wtime || '',
-            Text       => $msg || '',
-        }
-    ];
-
-    my $text = Form::compose([ $form ]);
-
-    if ($edit || !$msg) {
-        my $error = 0;
-        my ($c, $o, $k, $e);
-
-        do {
-            my $ntext = vi($text);
-            exit if ($error && $ntext eq $text);
-            $text = $ntext;
-            $form = Form::parse($text);
-            $error = 0;
-
-            ($c, $o, $k, $e) = @{ $form->[0] };
-            if ($e) {
-                $error = 1;
-                $c = "# Syntax error.";
-                goto NEXT;
-            }
-            elsif (!@$o) {
-                exit;
-            }
-            @files = @{ vsplit($k->{Attachment}) };
-
-        NEXT:
-            $text = Form::compose([[$c, $o, $k, $e]]);
-        } while ($error);
-    }
-
-    my $i = 1;
-    foreach my $file (@files) {
-        $data{"attachment_$i"} = bless([ $file ], "Attachment");
-        $i++;
-    }
-    $data{content} = $text;
-
-    my $r = submit("$REST/ticket/comment/$id", \%data);
-    print $r->content;
-}
-
-# Merge one ticket into another.
-
-sub merge {
-    my @id;
-    my $bad = 0;
-
-    while (@ARGV) {
-        $_ = shift @ARGV;
-
-        if (/^\d+$/) {
-            push @id, $_;
-        }
-        else {
-            whine "Unrecognised argument: '$_'.";
-            $bad = 1; last;
-        }
-    }
-
-    unless (@id == 2) {
-        my $evil = @id > 2 ? "many" : "few";
-        whine "Too $evil arguments specified.";
-        $bad = 1;
-    }
-    return help("merge", "ticket") if $bad;
-
-    my $r = submit("$REST/ticket/merge/$id[0]", {into => $id[1]});
-    print $r->content;
-}
-
-# Link one ticket to another.
-
-sub link {
-    my ($bad, $del, %data) = (0, 0, ());
-    my %ltypes = map { lc $_ => $_ } qw(DependsOn DependedOnBy RefersTo
-                                        ReferredToBy HasMember MemberOf);
-
-    while (@ARGV && $ARGV[0] =~ /^-/) {
-        $_ = shift @ARGV;
-
-        if (/^-d$/) {
-            $del = 1;
-        }
-        else {
-            whine "Unrecognised option: '$_'.";
-            $bad = 1; last;
-        }
-    }
-
-    if (@ARGV == 3) {
-        my ($from, $rel, $to) = @ARGV;
-        if ($from !~ /^\d+$/ || $to !~ /^\d+$/) {
-            my $bad = $from =~ /^\d+$/ ? $to : $from;
-            whine "Invalid ticket ID '$bad' specified.";
-            $bad = 1;
-        }
-        unless (exists $ltypes{lc $rel}) {
-            whine "Invalid relationship '$rel' specified.";
-            $bad = 1;
-        }
-        %data = (id => $from, rel => $rel, to => $to, del => $del);
-    }
-    else {
-        my $bad = @ARGV < 3 ? "few" : "many";
-        whine "Too $bad arguments specified.";
-        $bad = 1;
-    }
-    return help("link", "ticket") if $bad;
-
-    my $r = submit("$REST/ticket/link", \%data);
-    print $r->content;
-}
-
-# Grant/revoke a user's rights.
-
-sub grant {
-    my ($cmd) = @_;
-
-    my $revoke = 0;
-    while (@ARGV) {
-    }
-
-    $revoke = 1 if $cmd->{action} eq 'revoke';
-}
-
-# Client <-> Server communication.
-# --------------------------------
-#
-# This function composes and sends an HTTP request to the RT server, and
-# interprets the response. It takes a request URI, and optional request
-# data (a string, or a reference to a set of key-value pairs).
-
-sub submit {
-    my ($uri, $content) = @_;
-    my ($req, $data);
-    my $ua = new LWP::UserAgent(agent => "RT/3.0b", env_proxy => 1);
-
-    # Did the caller specify any data to send with the request?
-    $data = [];
-    if (defined $content) {
-        unless (ref $content) {
-            # If it's just a string, make sure LWP handles it properly.
-            # (By pretending that it's a file!)
-            $content = [ content => [undef, "", Content => $content] ];
-        }
-        elsif (ref $content eq 'HASH') {
-            my @data;
-            foreach my $k (keys %$content) {
-                if (ref $content->{$k} eq 'ARRAY') {
-                    foreach my $v (@{ $content->{$k} }) {
-                        push @data, $k, $v;
-                    }
-                }
-                else { push @data, $k, $content->{$k} }
-            }
-            $content = \@data;
-        }
-        $data = $content;
-    }
-
-    # Should we send authentication information to start a new session?
-    if (!defined $session->cookie) {
-        push @$data, ( user => $config{user} );
-        push @$data, ( pass => $config{passwd} || read_passwd() );
-    }
-
-    # Now, we construct the request.
-    if (@$data) {
-        $req = POST($uri, $data, Content_Type => 'form-data');
-    }
-    else {
-        $req = GET($uri);
-    }
-    $session->add_cookie_header($req);
-
-    # Then we send the request and parse the response.
-    DEBUG(3, $req->as_string);
-    my $res = $ua->request($req);
-    DEBUG(3, $res->as_string);
-
-    if ($res->is_success) {
-        # The content of the response we get from the RT server consists
-        # of an HTTP-like status line followed by optional header lines,
-        # a blank line, and arbitrary text.
-
-        my ($head, $text) = split /\n\n/, $res->content, 2;
-        my ($status, @headers) = split /\n/, $head;
-        $text =~ s/\n*$/\n/;
-
-        # "RT/3.0.1 401 Credentials required"
-        if ($status !~ m#^RT/\d+(?:\.\d+)+(?:-?\w+)? (\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;
-        }
-
-        # Our caller can pretend that the server returned a custom HTTP
-        # response code and message. (Doing that directly is apparently
-        # not sufficiently portable and uncomplicated.)
-        $res->code($1);
-        $res->message($2);
-        $res->content($text);
-        $session->update($res) if ($res->is_success || $res->code != 401);
-
-        if (!$res->is_success) {
-            # We can deal with authentication failures ourselves. Either
-            # we sent invalid credentials, or our session has expired.
-            if ($res->code == 401) {
-                my %d = @$data;
-                if (exists $d{user}) {
-                    warn "rt: Incorrect username or password.\n";
-                    exit -1;
-                }
-                elsif ($req->header("Cookie")) {
-                    # We'll retry the request with credentials, unless
-                    # we only wanted to logout in the first place.
-                    $session->delete;
-                    return submit(@_) unless $uri eq "$REST/logout";
-                }
-            }
-            # Conflicts should be dealt with by the handler and user.
-            # For anything else, we just die.
-            elsif ($res->code != 409) {
-                warn "rt: ", $res->content;
-                exit;
-            }
-        }
-    }
-    else {
-        warn "rt: Server error: ", $res->message, " (", $res->code, ")\n";
-        exit -1;
-    }
-
-    return $res;
-}
-
-# Session management.
-# -------------------
-#
-# Maintains a list of active sessions in the ~/.rt_sessions file.
-{
-    package Session;
-    my ($s, $u);
-
-    # Initialises the session cache.
-    sub new {
-        my ($class, $file) = @_;
-        my $self = {
-            file => $file || "$HOME/.rt_sessions",
-            sids => { }
-        };
-       
-        # The current session is identified by the currently configured
-        # server and user.
-        ($s, $u) = @config{"server", "user"};
-
-        bless $self, $class;
-        $self->load();
-
-        return $self;
-    }
-
-    # Returns the current session cookie.
-    sub cookie {
-        my ($self) = @_;
-        my $cookie = $self->{sids}{$s}{$u};
-        return defined $cookie ? "RT_SID=$cookie" : undef;
-    }
-
-    # Deletes the current session cookie.
-    sub delete {
-        my ($self) = @_;
-        delete $self->{sids}{$s}{$u};
-    }
-
-    # Adds a Cookie header to an outgoing HTTP request.
-    sub add_cookie_header {
-        my ($self, $request) = @_;
-        my $cookie = $self->cookie();
-
-        $request->header(Cookie => $cookie) if defined $cookie;
-    }
-
-    # Extracts the Set-Cookie header from an HTTP response, and updates
-    # session information accordingly.
-    sub update {
-        my ($self, $response) = @_;
-        my $cookie = $response->header("Set-Cookie");
-
-        if (defined $cookie && $cookie =~ /^RT_SID=([0-9A-Fa-f]+);/) {
-            $self->{sids}{$s}{$u} = $1;
-        }
-    }
-
-    # Loads the session cache from the specified file.
-    sub load {
-        my ($self, $file) = @_;
-        $file ||= $self->{file};
-        local *F;
-
-        open(F, $file) && do {
-            $self->{file} = $file;
-            my $sids = $self->{sids} = {};
-            while (<F>) {
-                chomp;
-                next if /^$/ || /^#/;
-                next unless m#^https?://[^ ]+ \w+ [0-9A-Fa-f]+$#;
-                my ($server, $user, $cookie) = split / /, $_;
-                $sids->{$server}{$user} = $cookie;
-            }
-            return 1;
-        };
-        return 0;
-    }
-
-    # Writes the current session cache to the specified file.
-    sub save {
-        my ($self, $file) = shift;
-        $file ||= $self->{file};
-        local *F;
-
-        open(F, ">$file") && do {
-            my $sids = $self->{sids};
-            foreach my $server (keys %$sids) {
-                foreach my $user (keys %{ $sids->{$server} }) {
-                    my $sid = $sids->{$server}{$user};
-                    if (defined $sid) {
-                        print F "$server $user $sid\n";
-                    }
-                }
-            }
-            close(F);
-            chmod 0600, $file;
-            return 1;
-        };
-        return 0;
-    }
-
-    sub DESTROY {
-        my $self = shift;
-        $self->save;
-    }
-}
-
-# Form handling.
-# --------------
-#
-# Forms are RFC822-style sets of (field, value) specifications with some
-# initial comments and interspersed blank lines allowed for convenience.
-# Sets of forms are separated by --\n (in a cheap parody of MIME).
-#
-# Each form is parsed into an array with four elements: commented text
-# at the start of the form, an array with the order of keys, a hash with
-# key/value pairs, and optional error text if the form syntax was wrong.
-
-# Returns a reference to an array of parsed forms.
-sub Form::parse {
-    my $state = 0;
-    my @forms = ();
-    my @lines = split /\n/, $_[0];
-    my ($c, $o, $k, $e) = ("", [], {}, "");
-
-    LINE:
-    while (@lines) {
-        my $line = shift @lines;
-
-        next LINE if $line eq '';
-
-        if ($line eq '--') {
-            # We reached the end of one form. We'll ignore it if it was
-            # empty, and store it otherwise, errors and all.
-            if ($e || $c || @$o) {
-                push @forms, [ $c, $o, $k, $e ];
-                $c = ""; $o = []; $k = {}; $e = "";
-            }
-            $state = 0;
-        }
-        elsif ($state != -1) {
-            if ($state == 0 && $line =~ /^#/) {
-                # Read an optional block of comments (only) at the start
-                # of the form.
-                $state = 1;
-                $c = $line;
-                while (@lines && $lines[0] =~ /^#/) {
-                    $c .= "\n".shift @lines;
-                }
-                $c .= "\n";
-            }
-            elsif ($state <= 1 && $line =~ /^($field):(?:\s+(.*))?$/) {
-                # Read a field: value specification.
-                my $f  = $1;
-                my @v  = ($2 || ());
-
-                # Read continuation lines, if any.
-                while (@lines && ($lines[0] eq '' || $lines[0] =~ /^\s+/)) {
-                    push @v, shift @lines;
-                }
-                pop @v while (@v && $v[-1] eq '');
-
-                # Strip longest common leading indent from text.
-                my $ws = "";
-                foreach my $ls (map {/^(\s+)/} @v[1..$#v]) {
-                    $ws = $ls if (!$ws || length($ls) < length($ws));
-                }
-                s/^$ws// foreach @v;
-
-                push(@$o, $f) unless exists $k->{$f};
-                vpush($k, $f, join("\n", @v));
-
-                $state = 1;
-            }
-            elsif ($line !~ /^#/) {
-                # We've found a syntax error, so we'll reconstruct the
-                # form parsed thus far, and add an error marker. (>>)
-                $state = -1;
-                $e = Form::compose([[ "", $o, $k, "" ]]);
-                $e.= $line =~ /^>>/ ? "$line\n" : ">> $line\n";
-            }
-        }
-        else {
-            # We saw a syntax error earlier, so we'll accumulate the
-            # contents of this form until the end.
-            $e .= "$line\n";
-        }
-    }
-    push(@forms, [ $c, $o, $k, $e ]) if ($e || $c || @$o);
-
-    foreach my $l (keys %$k) {
-        $k->{$l} = vsplit($k->{$l}) if (ref $k->{$l} eq 'ARRAY');
-    }
-
-    return \@forms;
-}
-
-# Returns text representing a set of forms.
-sub Form::compose {
-    my ($forms) = @_;
-    my @text;
-
-    foreach my $form (@$forms) {
-        my ($c, $o, $k, $e) = @$form;
-        my $text = "";
-
-        if ($c) {
-            $c =~ s/\n*$/\n/;
-            $text = "$c\n";
-        }
-        if ($e) {
-            $text .= $e;
-        }
-        elsif ($o) {
-            my @lines;
-
-            foreach my $key (@$o) {
-                my ($line, $sp);
-                my $v = $k->{$key};
-                my @values = ref $v eq 'ARRAY' ? @$v : $v;
-
-                $sp = " "x(length("$key: "));
-                $sp = " "x4 if length($sp) > 16;
-
-                foreach $v (@values) {
-                    if ($v =~ /\n/) {
-                        $v =~ s/^/$sp/gm;
-                        $v =~ s/^$sp//;
-
-                        if ($line) {
-                            push @lines, "$line\n\n";
-                            $line = "";
-                        }
-                        elsif (@lines && $lines[-1] !~ /\n\n$/) {
-                            $lines[-1] .= "\n";
-                        }
-                        push @lines, "$key: $v\n\n";
-                    }
-                    elsif ($line &&
-                           length($line)+length($v)-rindex($line, "\n") >= 70)
-                    {
-                        $line .= ",\n$sp$v";
-                    }
-                    else {
-                        $line = $line ? "$line, $v" : "$key: $v";
-                    }
-                }
-
-                $line = "$key:" unless @values;
-                if ($line) {
-                    if ($line =~ /\n/) {
-                        if (@lines && $lines[-1] !~ /\n\n$/) {
-                            $lines[-1] .= "\n";
-                        }
-                        $line .= "\n";
-                    }
-                    push @lines, "$line\n";
-                }
-            }
-
-            $text .= join "", @lines;
-        }
-        else {
-            chomp $text;
-        }
-        push @text, $text;
-    }
-
-    return join "\n--\n\n", @text;
-}
-
-# Configuration.
-# --------------
-
-# Returns configuration information from the environment.
-sub config_from_env {
-    my %env;
-
-    foreach my $k ("DEBUG", "USER", "PASSWD", "SERVER") {
-        if (exists $ENV{"RT$k"}) {
-            $env{lc $k} = $ENV{"RT$k"};
-        }
-    }
-
-    return %env;
-}
-
-# Finds a suitable configuration file and returns information from it.
-sub config_from_file {
-    my ($rc) = @_;
-
-    if ($rc =~ m#^/#) {
-        # We'll use an absolute path if we were given one.
-        return parse_config_file($rc);
-    }
-    else {
-        # Otherwise we'll use the first file we can find in the current
-        # directory, or in one of its (increasingly distant) ancestors.
-
-        my @dirs = split /\//, cwd;
-        while (@dirs) {
-            my $file = join('/', @dirs, $rc);
-            if (-r $file) {
-                return parse_config_file($file);
-            }
-
-            # Remove the last directory component each time.
-            pop @dirs;
-        }
-
-        # Still nothing? We'll fall back to some likely defaults.
-        for ("$HOME/$rc", "/etc/rt.conf") {
-            return parse_config_file($_) if (-r $_);
-        }
-    }
-
-    return ();
-}
-
-# Makes a hash of the specified configuration file.
-sub parse_config_file {
-    my %cfg;
-    my ($file) = @_;
-
-    open(CFG, $file) && do {
-        while (<CFG>) {
-            chomp;
-            next if (/^#/ || /^\s*$/);
-
-            if (/^(user|passwd|server)\s+([^ ]+)$/) {
-                $cfg{$1} = $2;
-            }
-            else {
-                die "rt: $file:$.: unknown configuration directive.\n";
-            }
-        }
-    };
-
-    return %cfg;
-}
-
-# Helper functions.
-# -----------------
-
-sub whine {
-    my $sub = (caller(1))[3];
-    $sub =~ s/^main:://;
-    warn "rt: $sub: @_\n";
-    return;
-}
-
-sub read_passwd {
-    eval 'require Term::ReadKey';
-    if ($@) {
-        die "No password specified (and Term::ReadKey not installed).\n";
-    }
-
-    print "Password: ";
-    Term::ReadKey::ReadMode('noecho');
-    chomp(my $passwd = Term::ReadKey::ReadLine(0));
-    Term::ReadKey::ReadMode('restore');
-    print "\n";
-
-    return $passwd;
-}
-
-sub vi {
-    my ($text) = @_;
-    my $file = "/tmp/rt.form.$$";
-    my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
-
-    local *F;
-    local $/ = undef;
-
-    open(F, ">$file") || die "$file: $!\n"; print F $text; close(F);
-    system($editor, $file) && die "Couldn't run $editor.\n";
-    open(F, $file) || die "$file: $!\n"; $text = <F>; close(F);
-    unlink($file);
-
-    return $text;
-}
-
-# Add a value to a (possibly multi-valued) hash key.
-sub vpush {
-    my ($hash, $key, $val) = @_;
-    my @val = ref $val eq 'ARRAY' ? @$val : $val;
-
-    if (exists $hash->{$key}) {
-        unless (ref $hash->{$key} eq 'ARRAY') {
-            my @v = $hash->{$key} ne '' ? $hash->{$key} : ();
-            $hash->{$key} = \@v;
-        }
-        push @{ $hash->{$key} }, @val;
-    }
-    else {
-        $hash->{$key} = $val;
-    }
-}
-
-# "Normalise" a hash key that's known to be multi-valued.
-sub vsplit {
-    my ($val) = @_;
-    my ($word, @words);
-    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.
-        $line =~ s/^\s+//;
-        $line =~ s/\s+$//;
-        push @words, split /\s*,\s*/, $line;
-    }
-
-    return \@words;
-}
-
-sub expand_list {
-    my ($list) = @_;
-    my ($elt, @elts, %elts);
-
-    foreach $elt (split /,/, $list) {
-        if ($elt =~ /^(\d+)-(\d+)$/) { push @elts, ($1..$2) }
-        else                         { push @elts, $elt }
-    }
-
-    @elts{@elts}=();
-    return sort {$a<=>$b} keys %elts;
-}
-
-sub get_type_argument {
-    my $type;
-
-    if (@ARGV) {
-        $type = shift @ARGV;
-        unless ($type =~ /^[A-Za-z0-9_.-]+$/) {
-            # We want whine to mention our caller, not us.
-            @_ = ("Invalid type '$type' specified.");
-            goto &whine;
-        }
-    }
-    else {
-        @_ = ("No type argument specified with -t.");
-        goto &whine;
-    }
-
-    $type =~ s/s$//; # "Plural". Ugh.
-    return $type;
-}
-
-sub get_var_argument {
-    my ($data) = @_;
-
-    if (@ARGV) {
-        my $kv = shift @ARGV;
-        if (my ($k, $v) = $kv =~ /^($field)=(.*)$/) {
-            push @{ $data->{$k} }, $v;
-        }
-        else {
-            @_ = ("Invalid variable specification: '$kv'.");
-            goto &whine;
-        }
-    }
-    else {
-        @_ = ("No variable argument specified with -S.");
-        goto &whine;
-    }
-}
-
-sub is_object_spec {
-    my ($spec, $type) = @_;
-
-    $spec =~ s|^(?:$type/)?|$type/| if defined $type;
-    return $spec if ($spec =~ m{^$name/(?:$idlist|$labels)(?:/.*)?$}o);
-    return;
-}
-
-__DATA__
-
-Title: intro
-Title: introduction
-Text:
-
-    ** THIS IS AN UNSUPPORTED PREVIEW RELEASE **
-    ** PLEASE REPORT BUGS TO rt-bugs@fsck.com **
-
-    This is a command-line interface to RT 3.
-
-    It allows you to interact with an RT server over HTTP, and offers an
-    interface to RT's functionality that is better-suited to automation
-    and integration with other tools.
-
-    In general, each invocation of this program should specify an action
-    to perform on one or more objects, and any other arguments required
-    to complete the desired action.
-
-    For more information:
-
-        - rt help actions       (a list of possible actions)
-        - rt help objects       (how to specify objects)
-        - rt help usage         (syntax information)
-
-        - rt help config        (configuration details)
-        - rt help examples      (a few useful examples)
-        - rt help topics        (a list of help topics)
-
---
-
-Title: usage
-Title: syntax
-Text:
-
-    Syntax:
-
-        rt <action> [options] [arguments]
-
-    Each invocation of this program must specify an action (e.g. "edit",
-    "create"), options to modify behaviour, and other arguments required
-    by the specified action. (For example, most actions expect a list of
-    numeric object IDs to act upon.)
-
-    The details of the syntax and arguments for each action are given by
-    "rt help <action>". Some actions may be referred to by more than one
-    name ("create" is the same as "new", for example).  
-
-    Objects are identified by a type and an ID (which can be a name or a
-    number, depending on the type). For some actions, the object type is
-    implied (you can only comment on tickets); for others, the user must
-    specify it explicitly. See "rt help objects" for details.
-
-    In syntax descriptions, mandatory arguments that must be replaced by
-    appropriate value are enclosed in <>, and optional arguments are
-    indicated by [] (for example, <action> and [options] above).
-
-    For more information:
-
-        - rt help objects       (how to specify objects)
-        - rt help actions       (a list of actions)
-        - rt help types         (a list of object types)
-
---
-
-Title: conf
-Title: config
-Title: configuration
-Text:
-
-    This program has two major sources of configuration information: its
-    configuration files, and the environment.
-
-    The program looks for configuration directives in a file named .rtrc
-    (or $RTCONFIG; see below) in the current directory, and then in more
-    distant ancestors, until it reaches /. If no suitable configuration
-    files are found, it will also check for ~/.rtrc and /etc/rt.conf.
-
-    Configuration directives:
-
-        The following directives may occur, one per line:
-
-        - server <URL>          URL to RT server.
-        - user <username>       RT username.
-        - passwd <passwd>       RT user's password.
-
-        Blank and #-commented lines are ignored.
-
-    Environment variables:
-
-        The following environment variables override any corresponding
-        values defined in configuration files:
-
-        - RTUSER
-        - RTPASSWD
-        - RTSERVER
-        - RTDEBUG       Numeric debug level. (Set to 3 for full logs.)
-        - RTCONFIG      Specifies a name other than ".rtrc" for the
-                        configuration file.
-
---
-
-Title: objects
-Text:
-
-    Syntax:
-
-        <type>/<id>[/<attributes>]
-
-    Every object in RT has a type (e.g. "ticket", "queue") and a numeric
-    ID. Some types of objects can also be identified by name (like users
-    and queues). Furthermore, objects may have named attributes (such as
-    "ticket/1/history").
-
-    An object specification is like a path in a virtual filesystem, with
-    object types as top-level directories, object IDs as subdirectories,
-    and named attributes as further subdirectories.
-
-    A comma-separated list of names, numeric IDs, or numeric ranges can
-    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".
-    
-    Examples:
-
-        ticket/1
-        ticket/1/attachments
-        ticket/1/attachments/3
-        ticket/1/attachments/3/content
-        ticket/1-3/links
-        ticket/1-3,5-7/history
-
-        user/ams
-        user/ams/rights
-        user/ams,rai,1/rights
-
-    For more information:
-
-        - rt help <action>      (action-specific details)
-        - rt help <type>        (type-specific details)
-
---
-
-Title: actions
-Title: commands
-Text:
-
-    You can currently perform the following actions on all objects:
-
-        - list          (list objects matching some condition)
-        - show          (display object details)
-        - edit          (edit object details)
-        - create        (create a new object)
-
-    Each type may define actions specific to itself; these are listed in
-    the help item about that type.
-
-    For more information:
-
-        - rt help <action>      (action-specific details)
-        - rt help types         (a list of possible types)
-
---
-
-Title: types
-Text:
-
-    You can currently operate on the following types of objects:
-
-        - tickets
-        - users
-        - groups
-        - queues
-
-    For more information:
-
-        - rt help <type>        (type-specific details)
-        - rt help objects       (how to specify objects)
-        - rt help actions       (a list of possible actions)
-
---
-
-Title: ticket
-Text:
-
-    Tickets are identified by a numeric ID.
-
-    The following generic operations may be performed upon tickets:
-
-        - list
-        - show
-        - edit
-        - create
-
-    In addition, the following ticket-specific actions exist:
-
-        - link
-        - merge
-        - comment
-        - correspond
-
-    Attributes:
-
-        The following attributes can be used with "rt show" or "rt edit"
-        to retrieve or edit other information associated with tickets:
-
-        links                      A ticket's relationships with others.
-        history                    All of a ticket's transactions.
-        history/type/<type>        Only a particular type of transaction.
-        history/id/<id>            Only the transaction of the specified id.
-        attachments                A list of attachments.
-        attachments/<id>           The metadata for an individual attachment.
-        attachments/<id>/content   The content of an individual attachment.
-
---
-
-Title: user
-Title: group
-Text:
-
-    Users and groups are identified by name or numeric ID.
-
-    The following generic operations may be performed upon them:
-
-        - list
-        - show
-        - edit
-        - create
-
-    In addition, the following type-specific actions exist:
-
-        - grant
-        - revoke
-
-    Attributes:
-
-        The following attributes can be used with "rt show" or "rt edit"
-        to retrieve or edit other information associated with users and
-        groups:
-
-        rights                  Global rights granted to this user.
-        rights/<queue>          Queue rights for this user.
-
---
-
-Title: queue
-Text:
-
-    Queues are identified by name or numeric ID.
-
-    Currently, they can be subjected to the following actions:
-
-        - show
-        - edit
-        - create
-
---
-
-Title: logout
-Text:
-
-    Syntax:
-
-        rt logout
-
-    Terminates the currently established login session. You will need to
-    provide authentication credentials before you can continue using the
-    server. (See "rt help config" for details about authentication.)
-
---
-
-Title: ls
-Title: list
-Title: search
-Text:
-
-    Syntax:
-
-        rt <ls|list|search> [options] "query string"
-
-    Displays a list of objects matching the specified conditions.
-    ("ls", "list", and "search" are synonyms.)
-
-    Conditions are expressed in the SQL-like syntax used internally by
-    RT3. (For more information, see "rt help query".) The query string
-    must be supplied as one argument.
-
-    (Right now, the server doesn't support listing anything but tickets.
-    Other types will be supported in future; this client will be able to
-    take advantage of that support without any changes.)
-
-    Options:
-
-        The following options control how much information is displayed
-        about each matching object:
-
-        -i      Numeric IDs only. (Useful for |rt edit -; see examples.)
-        -s      Short description.
-        -l      Longer description.
-
-        In addition,
-        
-        -o +/-<field>   Orders the returned list by the specified field.
-        -S var=val      Submits the specified variable with the request.
-        -t type         Specifies the type of object to look for. (The
-                        default is "ticket".)
-
-    Examples:
-
-        rt ls "Priority > 5 and Status='new'"
-        rt ls -o +Subject "Priority > 5 and Status='new'"
-        rt ls -o -Created "Priority > 5 and Status='new'"
-        rt ls -i "Priority > 5"|rt edit - set status=resolved
-        rt ls -t ticket "Subject like '[PATCH]%'"
-
---
-
-Title: show
-Text:
-
-    Syntax:
-
-        rt show [options] <object-ids>
-
-    Displays details of the specified objects.
-
-    For some types, object information is further classified into named
-    attributes (for example, "1-3/links" is a valid ticket specification
-    that refers to the links for tickets 1-3). Consult "rt help <type>"
-    and "rt help objects" for further details.
-
-    This command writes a set of forms representing the requested object
-    data to STDOUT.
-
-    Options:
-
-        -               Read IDs from STDIN instead of the command-line.
-        -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.
-
-    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 -t user 2
-
---
-
-Title: new
-Title: edit
-Title: create
-Text:
-
-    Syntax:
-
-        rt edit [options] <object-ids> set field=value [field=value] ...
-                                       add field=value [field=value] ...
-                                       del field=value [field=value] ...
-
-    Edits information corresponding to the specified objects.
-
-    If, instead of "edit", an action of "new" or "create" is specified,
-    then a new object is created. In this case, no numeric object IDs
-    may be specified, but the syntax and behaviour remain otherwise
-    unchanged.
-
-    This command typically starts an editor to allow you to edit object
-    data in a form for submission. If you specified enough information
-    on the command-line, however, it will make the submission directly.
-
-    The command line may specify field-values in three different ways.
-    "set" sets the named field to the given value, "add" adds a value
-    to a multi-valued field, and "del" deletes the corresponding value.
-    Each "field=value" specification must be given as a single argument.
-
-    For some types, object information is further classified into named
-    attributes (for example, "1-3/links" is a valid ticket specification
-    that refers to the links for tickets 1-3). These attributes may also
-    be edited. Consult "rt help <type>" and "rt help object" for further
-    details.
-
-    Options:
-
-        -       Read numeric IDs from STDIN instead of the command-line.
-                (Useful with rt ls ... | rt edit -; see examples below.)
-        -i      Read a completed form from STDIN before submitting.
-        -o      Dump the completed form to STDOUT instead of submitting.
-        -e      Allows you to edit the form even if the command-line has
-                enough information to make a submission directly.
-        -S var=val
-                Submits the specified variable with the request.
-        -t type Specifies object type.
-
-    Examples:
-
-        # Interactive (starts $EDITOR with a form).
-        rt edit ticket/3
-        rt create -t ticket
-
-        # Non-interactive.
-        rt edit ticket/1-3 add cc=foo@example.com set priority=3
-        rt ls -t tickets -i 'Priority > 5' | rt edit - set status=resolved
-        rt edit ticket/4 set priority=3 owner=bar@example.com \
-                         add cc=foo@example.com bcc=quux@example.net
-        rt create -t ticket subject='new ticket' priority=10 \
-                            add cc=foo@example.com
-
---
-
-Title: comment
-Title: correspond
-Text:
-
-    Syntax:
-
-        rt <comment|correspond> [options] <ticket-id>
-
-    Adds a comment (or correspondence) to the specified ticket (the only
-    difference being that comments aren't sent to the requestors.)
-
-    This command will typically start an editor and allow you to type a
-    comment into a form. If, however, you specified all the necessary
-    information on the command line, it submits the comment directly.
-
-    (See "rt help forms" for more information about forms.)
-
-    Options:
-
-        -m <text>       Specify comment text.
-        -a <file>       Attach a file to the comment. (May be used more
-                        than once to attach multiple files.)
-        -c <addrs>      A comma-separated list of Cc addresses.
-        -b <addrs>      A comma-separated list of Bcc addresses.
-        -w <time>       Specify the time spent working on this ticket.
-        -e              Starts an editor before the submission, even if
-                        arguments from the command line were sufficient.
-
-    Examples:
-
-        rt comment -t 'Not worth fixing.' -a stddisclaimer.h 23
-
---
-
-Title: merge
-Text:
-
-    Syntax:
-
-        rt merge <from-id> <to-id>
-
-    Merges the two specified tickets.
-
---
-
-Title: link
-Text:
-
-    Syntax:
-
-        rt link [-d] <id-A> <relationship> <id-B>
-
-    Creates (or, with -d, deletes) a link between the specified tickets.
-    The relationship 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
-    "rt help ticket" and "rt help show".)
-
-    Options:
-
-        -d      Deletes the specified link.
-
-    Examples:
-
-        rt link 2 dependson 3
-        rt link -d 4 referredtoby 6     # 6 no longer refers to 4
-
---
-
-Title: grant
-Title: revoke
-Text:
-
---
-
-Title: query
-Text:
-
-    RT3 uses an SQL-like syntax to specify object selection constraints.
-    See the <RT:...> documentation for details.
-    
-    (XXX: I'm going to have to write it, aren't I?)
-
---
-
-Title: form
-Title: forms
-Text:
-
-    This program uses RFC822 header-style forms to represent object data
-    in a form that's suitable for processing both by humans and scripts.
-
-    A form is a set of (field, value) specifications, with some initial
-    commented text and interspersed blank lines allowed for convenience.
-    Field names may appear more than once in a form; a comma-separated
-    list of multiple field values may also be specified directly.
-    
-    Field values can be wrapped as in RFC822, with leading whitespace.
-    The longest sequence of leading whitespace common to all the lines
-    is removed (preserving further indentation). There is no limit on
-    the length of a value.
-
-    Multiple forms are separated by a line containing only "--\n".
-
-    (XXX: A more detailed specification will be provided soon. For now,
-    the server-side syntax checking will suffice.)
-
---
-
-Title: topics
-Text:
-
-    Use "rt help <topic>" for help on any of the following subjects:
-
-        - tickets, users, groups, queues.
-        - show, edit, ls/list/search, new/create.
-
-        - query                                 (search query syntax)
-        - forms                                 (form specification)
-
-        - objects                               (how to specify objects)
-        - types                                 (a list of object types)
-        - actions/commands                      (a list of actions)
-        - usage/syntax                          (syntax details)
-        - conf/config/configuration             (configuration details)
-        - examples                              (a few useful examples)
-
---
-
-Title: example
-Title: examples
-Text:
-
-    This section will be filled in with useful examples, once it becomes
-    more clear what examples may be useful.
-
-    For the moment, please consult examples provided with each action.
-
---
diff --git a/rt/config.layout b/rt/config.layout
deleted file mode 100644 (file)
index 23a7775..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-##
-##  config.layout -- Pre-defined Installation Path Layouts
-##
-##  Hints:
-##  - layouts can be loaded with configure's --enable-layout=ID option
-##  - when no --enable-layout option is given, the default layout is `RT'
-##  - a trailing plus character (`+') on paths is replaced with a
-##    `/<target>' suffix where <target> is currently hardcoded to 'rt3'.
-##    (This may become a configurable parameter at some point.)
-##
-##  The following variables must _all_ be set:
-##     prefix exec_prefix bindir sbindir sysconfdir mandir libdir
-##     datadir htmldir localstatedir logfiledir masonstatedir
-##     sessionstatedir customdir customhtmldir customlexdir
-##  (This can be seen in m4/rt_layout.m4.)
-##
-
-#   Default RT3 path layout.
-<Layout RT3>
-  prefix:              /opt/rt3
-  exec_prefix:         ${prefix}
-  bindir:              ${exec_prefix}/bin
-  sbindir:             ${exec_prefix}/sbin
-  sysconfdir:          ${prefix}/etc
-  mandir:              ${prefix}/man
-  libdir:              ${prefix}/lib
-  datadir:             ${prefix}/share
-  htmldir:             ${datadir}/html
-  manualdir:           ${datadir}/doc
-  localstatedir:       ${prefix}/var
-  logfiledir:          ${localstatedir}/log
-  masonstatedir:       ${localstatedir}/mason_data
-  sessionstatedir:     ${localstatedir}/session_data
-  customdir:           ${prefix}/local
-  custometcdir:                ${customdir}/etc
-  customhtmldir:       ${customdir}/html
-  customlexdir:                ${customdir}/po
-  customlibdir:                ${customdir}/lib
-</Layout>
-<Layout inplace>
-  prefix:              `pwd`
-  exec_prefix:         ${prefix}
-  bindir:              ${exec_prefix}/bin
-  sbindir:             ${exec_prefix}/sbin
-  sysconfdir:          ${prefix}/etc
-  mandir:              ${prefix}/man
-  libdir:              ${prefix}/lib
-  datadir:             ${prefix}/share
-  htmldir:             ${prefix}/html
-  manualdir:           ${datadir}/doc
-  localstatedir:       ${prefix}/var
-  logfiledir:          ${localstatedir}/log
-  masonstatedir:       ${localstatedir}/mason_data
-  sessionstatedir:     ${localstatedir}/session_data
-  customdir:           ${prefix}/local
-  custometcdir:                ${customdir}/etc
-  customhtmldir:       ${customdir}/html
-  customlexdir:                ${customdir}/po
-  customlibdir:                ${customdir}/lib
-</Layout>
-
-<Layout FreeBSD>
-  prefix:              /usr/local
-  exec_prefix:         ${prefix}
-  bindir:              ${exec_prefix}/bin
-  sbindir:             ${exec_prefix}/sbin
-  sysconfdir:          ${prefix}/etc+
-  mandir:              ${prefix}/man
-  libdir:              ${prefix}/lib+
-  datadir:             ${prefix}/share+
-  htmldir:             ${datadir}/html
-  manualdir:           ${prefix}/share/doc+
-  logfiledir:          /var/log
-  localstatedir:       /var/run+
-  masonstatedir:       ${localstatedir}/mason_data
-  sessionstatedir:     ${localstatedir}/session_data
-  customdir:           ${prefix}/share+
-  custometcdir:                ${customdir}/local/etc
-  customhtmldir:       ${customdir}/local/html
-  customlexdir:                ${customdir}/local/po
-  customlibdir:                ${customdir}/local/lib
-</Layout>
-
-<Layout Win32>
-  prefix:              C:/Program Files/Request Tracker
-  exec_prefix:         ${prefix}
-  bindir:              ${exec_prefix}/bin
-  sbindir:             ${exec_prefix}/sbin
-  sysconfdir:          ${prefix}/etc
-  mandir:              ${prefix}/man
-  libdir:              ${prefix}/lib
-  datadir:             ${prefix}
-  htmldir:             ${datadir}/html
-  manualdir:           ${datadir}/doc
-  localstatedir:       ${prefix}/var
-  logfiledir:          ${localstatedir}/log
-  masonstatedir:       ${localstatedir}/mason_data
-  sessionstatedir:     ${localstatedir}/session_data
-  customdir:           ${prefix}/local
-  custometcdir:                ${customdir}/etc
-  customhtmldir:       ${customdir}/html
-  customlexdir:                ${customdir}/po
-  customlibdir:                ${customdir}/lib
-</Layout>
diff --git a/rt/config.layout.in b/rt/config.layout.in
new file mode 100644 (file)
index 0000000..a08f489
--- /dev/null
@@ -0,0 +1,127 @@
+##
+##  config.layout -- Pre-defined Installation Path Layouts
+##
+##  Hints:
+##  - layouts can be loaded with configure's --enable-layout=ID option
+##  - when no --enable-layout option is given, the default layout is `RT'
+##  - a trailing plus character (`+') on paths is replaced with a
+##    `/<target>' suffix where <target> is currently hardcoded to 'rt3'.
+##    (This may become a configurable parameter at some point.)
+##
+##  The following variables must _all_ be set:
+##     prefix exec_prefix bindir sbindir sysconfdir mandir libdir
+##     datadir htmldir localstatedir logfiledir masonstatedir
+##     sessionstatedir customdir customhtmldir customlexdir
+##  (This can be seen in m4/rt_layout.m4.)
+##
+
+#   Default RT3 path layout.
+<Layout RT3>
+  prefix:              /opt/rt3
+  exec_prefix:         ${prefix}
+  bindir:              ${exec_prefix}/bin
+  sbindir:             ${exec_prefix}/sbin
+  sysconfdir:          ${prefix}/etc
+  mandir:              ${prefix}/man
+  libdir:              ${prefix}/lib
+  datadir:             ${prefix}/share
+  htmldir:             ${datadir}/html
+  manualdir:           ${datadir}/doc
+  localstatedir:       ${prefix}/var
+  logfiledir:          ${localstatedir}/log
+  masonstatedir:       ${localstatedir}/mason_data
+  sessionstatedir:     ${localstatedir}/session_data
+  customdir:           ${prefix}/local
+  custometcdir:                ${customdir}/etc
+  customhtmldir:       ${customdir}/html
+  customlexdir:                ${customdir}/po
+  customlibdir:                ${customdir}/lib
+</Layout>
+<Layout inplace>
+  prefix:              `pwd`
+  exec_prefix:         ${prefix}
+  bindir:              ${exec_prefix}/bin
+  sbindir:             ${exec_prefix}/sbin
+  sysconfdir:          ${prefix}/etc
+  mandir:              ${prefix}/man
+  libdir:              ${prefix}/lib
+  datadir:             ${prefix}/share
+  htmldir:             ${prefix}/html
+  manualdir:           ${datadir}/doc
+  localstatedir:       ${prefix}/var
+  logfiledir:          ${localstatedir}/log
+  masonstatedir:       ${localstatedir}/mason_data
+  sessionstatedir:     ${localstatedir}/session_data
+  customdir:           ${prefix}/local
+  custometcdir:                ${customdir}/etc
+  customhtmldir:       ${customdir}/html
+  customlexdir:                ${customdir}/po
+  customlibdir:                ${customdir}/lib
+</Layout>
+
+<Layout FreeBSD>
+  prefix:              /usr/local
+  exec_prefix:         ${prefix}
+  bindir:              ${exec_prefix}/bin
+  sbindir:             ${exec_prefix}/sbin
+  sysconfdir:          ${prefix}/etc+
+  mandir:              ${prefix}/man
+  libdir:              ${prefix}/lib+
+  datadir:             ${prefix}/share+
+  htmldir:             ${datadir}/html
+  manualdir:           ${prefix}/share/doc+
+  logfiledir:          /var/log
+  localstatedir:       /var/run+
+  masonstatedir:       ${localstatedir}/mason_data
+  sessionstatedir:     ${localstatedir}/session_data
+  customdir:           ${prefix}/share+
+  custometcdir:                ${customdir}/local/etc
+  customhtmldir:       ${customdir}/local/html
+  customlexdir:                ${customdir}/local/po
+  customlibdir:                ${customdir}/local/lib
+</Layout>
+
+<Layout Win32>
+  prefix:              C:/Program Files/Request Tracker
+  exec_prefix:         ${prefix}
+  bindir:              ${exec_prefix}/bin
+  sbindir:             ${exec_prefix}/sbin
+  sysconfdir:          ${prefix}/etc
+  mandir:              ${prefix}/man
+  libdir:              ${prefix}/lib
+  datadir:             ${prefix}
+  htmldir:             ${datadir}/html
+  manualdir:           ${datadir}/doc
+  localstatedir:       ${prefix}/var
+  logfiledir:          ${localstatedir}/log
+  masonstatedir:       ${localstatedir}/mason_data
+  sessionstatedir:     ${localstatedir}/session_data
+  customdir:           ${prefix}/local
+  custometcdir:                ${customdir}/etc
+  customhtmldir:       ${customdir}/html
+  customlexdir:                ${customdir}/po
+  customlibdir:                ${customdir}/lib
+</Layout>
+
+<Layout Freeside>
+  prefix:              /opt/rt3
+  exec_prefix:         ${prefix}
+  bindir:              ${exec_prefix}/bin
+  sbindir:             ${exec_prefix}/sbin
+  sysconfdir:          ${prefix}/etc
+  mandir:              ${prefix}/man
+  libdir:              ${prefix}/lib
+  datadir:             ${prefix}/share
+  htmldir:             %%%FREESIDE_DOCUMENT_ROOT%%%/rt
+  manualdir:           ${datadir}/doc
+  localstatedir:       ${prefix}/var
+  logfiledir:          ${localstatedir}/log
+  masonstatedir:       %%%MASONDATA%%%
+  sessionstatedir:     ${localstatedir}/session_data
+  customdir:           ${prefix}/local
+  custometcdir:                ${customdir}/etc
+  customhtmldir:       ${customdir}/html
+  customlexdir:                ${customdir}/po
+  customlibdir:                ${customdir}/lib
+</Layout>
+
index 0afc604..572a2ba 100644 (file)
@@ -1 +1,13 @@
+$RT::rtname = '%%%RT_DOMAIN%%%';
+$RT::Organization = '%%%RT_DOMAIN%%%';
+
+$RT::Timezone = '%%%RT_TIMEZONE%%%';
+
+$RT::WebBaseURL = '';
+$RT::WebPath = '/freeside/rt';
+
+$RT::WebExternalAuth = 1;
+$RT::WebFallbackToInternal = 1; #no
+$RT::WebExternalAuto = 1;
+
 1;
diff --git a/rt/etc/schema.Oracle b/rt/etc/schema.Oracle
deleted file mode 100644 (file)
index 95cfda2..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-CREATE SEQUENCE ATTACHMENTS_seq;
-CREATE TABLE Attachments (
-       id              NUMBER(11,0) 
-                       CONSTRAINT Attachments_Key PRIMARY KEY,
-       TransactionId   NUMBER(11,0) NOT NULL,
-       Parent          NUMBER(11,0) DEFAULT 0 NOT NULL, 
-       MessageId       VARCHAR2(160),
-       Subject         VARCHAR2(255),
-       Filename        VARCHAR2(255),
-       ContentType     VARCHAR2(80),
-       ContentEncoding VARCHAR2(80),
-       Content         CLOB,
-       Headers         CLOB,
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE
-);
-CREATE INDEX Attachments2 ON Attachments (TransactionId);
-CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId);
-
-
-CREATE SEQUENCE QUEUES_seq;
-CREATE TABLE Queues (
-       id                      NUMBER(11,0) 
-               CONSTRAINT Queues_Key PRIMARY KEY,
-       Name                    VARCHAR2(200) CONSTRAINT Queues_Name_Unique UNIQUE NOT NULL,
-       Description             VARCHAR2(255),
-       CorrespondAddress       VARCHAR2(120),
-       CommentAddress          VARCHAR2(120),
-       InitialPriority         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       FinalPriority           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       DefaultDueIn            NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Creator                 NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created                 DATE,
-       LastUpdatedBy           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated             DATE,
-       Disabled                NUMBER(11,0) DEFAULT 0 NOT NULL
-);
- CREATE  INDEX Queues1 ON Queues (lower(Name));
-CREATE INDEX Queues2 ON Queues (Disabled);
-
-
-CREATE SEQUENCE LINKS_seq;
-CREATE TABLE Links (
-       id              NUMBER(11,0) 
-               CONSTRAINT Links_Key PRIMARY KEY,
-       Base            VARCHAR2(240),
-       Target          VARCHAR2(240),
-       Type            VARCHAR2(20) NOT NULL,
-       LocalTarget     NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LocalBase       NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdatedBy   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated     DATE,
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE
-);
-CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type);
-CREATE INDEX Links2 ON Links (Base, Type);
-CREATE INDEX Links3 ON Links (Target, Type);
-CREATE INDEX Links4 ON Links(Type,LocalBase);
-
-
-CREATE SEQUENCE PRINCIPALS_seq;
-CREATE TABLE Principals (
-       id              NUMBER(11,0) 
-               CONSTRAINT Principals_Key PRIMARY KEY,
-       PrincipalType   VARCHAR2(16),
-       ObjectId        NUMBER(11,0),
-       Disabled        NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE UNIQUE  INDEX Principals2 ON Principals (ObjectId);
-
-
-CREATE SEQUENCE GROUPS_seq;
-CREATE TABLE Groups (
-       id              NUMBER(11,0) 
-               CONSTRAINT Groups_Key PRIMARY KEY,
-       Name            VARCHAR2(200),
-       Description     VARCHAR2(255),
-       Domain          VARCHAR2(64),
-       Type            VARCHAR2(64),
-       Instance        NUMBER(11,0) DEFAULT 0 -- NOT NULL
---     Instance        VARCHAR2(64)
-);
-CREATE INDEX Groups1 ON Groups (lower( Domain), Instance, lower(Type), id);
-CREATE INDEX Groups2 ON Groups (lower(Type), Instance, lower(Domain));
-
-
-CREATE SEQUENCE SCRIPCONDITIONS_seq;
-CREATE TABLE ScripConditions (
-       id                      NUMBER(11, 0) 
-               CONSTRAINT ScripConditions_Key PRIMARY KEY,
-       Name                    VARCHAR2(200),
-       Description             VARCHAR2(255),
-       ExecModule              VARCHAR2(60),
-       Argument                VARCHAR2(255),
-       ApplicableTransTypes    VARCHAR2(60),
-       Creator                 NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created                 DATE,
-       LastUpdatedBy           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated             DATE
-);
-
-
-CREATE SEQUENCE TRANSACTIONS_seq;
-CREATE TABLE Transactions (
-       id                      NUMBER(11,0) 
-               CONSTRAINT Transactions_Key PRIMARY KEY,
-       EffectiveTicket         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Ticket                  NUMBER(11,0) DEFAULT 0 NOT NULL,
-       TimeTaken               NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Type                    VARCHAR2(20),
-       Field                   VARCHAR2(40),
-       OldValue                VARCHAR2(255),
-       NewValue                VARCHAR2(255),
-       Data                    VARCHAR2(255),
-       Creator                 NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created                 DATE
-);
-CREATE INDEX Transactions1 ON Transactions (Ticket);
-CREATE INDEX Transactions2 ON Transactions (EffectiveTicket);
-
-
-CREATE SEQUENCE SCRIPS_seq;
-CREATE TABLE Scrips (
-       id              NUMBER(11,0) 
-               CONSTRAINT Scrips_Key PRIMARY KEY,      
-       Description     VARCHAR2(255),
-       ScripCondition  NUMBER(11,0) DEFAULT 0 NOT NULL,
-       ScripAction     NUMBER(11,0) DEFAULT 0 NOT NULL,
-       ConditionRules  CLOB,
-       ActionRules     CLOB,
-       CustomIsApplicableCode  CLOB,
-       CustomPrepareCode       CLOB,
-       CustomCommitCode        CLOB,
-       Stage           VARCHAR2(32),
-       Queue           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Template        NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE,
-       LastUpdatedBy   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated     DATE  
-);
-
-
-CREATE SEQUENCE ACL_seq;
-CREATE TABLE ACL (
-       id              NUMBER(11,0) 
-               CONSTRAINT ACL_Key PRIMARY KEY,
-       PrincipalType   VARCHAR2(25) NOT NULL,
-       PrincipalId     NUMBER(11,0) NOT NULL,
-       RightName       VARCHAR2(25) NOT NULL,
-       ObjectType      VARCHAR2(25) NOT NULL,
-       ObjectId        NUMBER(11,0) DEFAULT 0 NOT NULL,
-       DelegatedBy     NUMBER(11,0) DEFAULT 0 NOT NULL,
-       DelegatedFrom   NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX ACL1 ON ACL(RightName, ObjectType, ObjectId, PrincipalType, PrincipalId);
-
-
-CREATE SEQUENCE GROUPMEMBERS_seq;
-CREATE TABLE GroupMembers (
-       id              NUMBER(11,0) 
-               CONSTRAINT GroupMembers_Key PRIMARY KEY,
-       GroupId         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       MemberId        NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE UNIQUE INDEX GroupMembers1 ON GroupMembers (GroupId, MemberId);
-
-
-CREATE SEQUENCE CachedGroupMembers_seq;
-CREATE TABLE CachedGroupMembers (
-       id              NUMBER(11,0) 
-               CONSTRAINT CachedGroupMembers_Key PRIMARY KEY,
-       GroupId         NUMBER(11,0),
-       MemberId        NUMBER(11,0),
-       Via             NUMBER(11,0),
-       ImmediateParentId       NUMBER(11,0),
-       Disabled        NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX DisGrouMem ON CachedGroupMembers (GroupId, MemberId, Disabled);
-CREATE INDEX GrouMem ON CachedGroupMembers (GroupId, MemberId);
-
-
-CREATE SEQUENCE USERS_seq;
-CREATE TABLE Users (
-       id                      NUMBER(11,0) 
-               CONSTRAINT Users_Key PRIMARY KEY,
-       Name                    VARCHAR2(200) CONSTRAINT Users_Name_Unique 
-               unique  NOT NULL,
-       Password                VARCHAR2(40),
-       Comments                CLOB,
-       Signature               CLOB,
-       EmailAddress            VARCHAR2(120),
-       FreeFormContactInfo     CLOB,
-       Organization            VARCHAR2(200),
-       RealName                VARCHAR2(120),
-       NickName                VARCHAR2(16),
-       Lang                    VARCHAR2(16),
-       EmailEncoding           VARCHAR2(16),
-       WebEncoding             VARCHAR2(16),
-       ExternalContactInfoId   VARCHAR2(100),
-       ContactInfoSystem       VARCHAR2(30),
-       ExternalAuthId          VARCHAR2(100),
-       AuthSystem              VARCHAR2(30),
-       Gecos                   VARCHAR2(16),
-       HomePhone               VARCHAR2(30),
-       WorkPhone               VARCHAR2(30),
-       MobilePhone             VARCHAR2(30),
-       PagerPhone              VARCHAR2(30),
-       Address1                VARCHAR2(200),
-       Address2                VARCHAR2(200),
-       City                    VARCHAR2(100),
-       State                   VARCHAR2(100),
-       Zip                     VARCHAR2(16),
-       Country                 VARCHAR2(50),
-       Timezone                VARCHAR2(50),
-       PGPKey                  CLOB,
-       Creator                 NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created                 DATE,
-       LastUpdatedBy           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated             DATE
-);
--- CREATE UNIQUE INDEX Users1 ON Users (Name);
-
-CREATE INDEX Users2 ON Users( LOWER(name));
-CREATE INDEX Users4 ON Users (lower(EmailAddress));
-
-
-CREATE SEQUENCE TICKETS_seq;
-CREATE TABLE Tickets (
-       id                      NUMBER(11, 0) 
-               CONSTRAINT Tickets_Key PRIMARY KEY,
-       EffectiveId             NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Queue                   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Type                    VARCHAR2(16),           
-       IssueStatement          NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Resolution              NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Owner                   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Subject                 VARCHAR2(200) DEFAULT '[no subject]', 
-       InitialPriority         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       FinalPriority           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Priority                NUMBER(11,0) DEFAULT 0 NOT NULL,
-       TimeEstimated           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       TimeWorked              NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Status                  VARCHAR2(10),           
-       TimeLeft                NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Told                    DATE,
-       Starts                  DATE,
-       Started                 DATE,
-       Due                     DATE,
-       Resolved                DATE,
-       LastUpdatedBy           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated             DATE,
-       Creator                 NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created                 DATE,
-       Disabled                NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX Tickets1 ON Tickets (Queue, Status);
-CREATE INDEX Tickets2 ON Tickets (Owner);
-CREATE INDEX Tickets4 ON Tickets (id, Status);
-CREATE INDEX Tickets5 ON Tickets (id, EffectiveId);
-CREATE INDEX Tickets6 ON Tickets (EffectiveId, Type);
-
-
-CREATE SEQUENCE SCRIPACTIONS_seq;
-CREATE TABLE ScripActions (
-  id           NUMBER(11,0) 
-               CONSTRAINT ScripActions_Key PRIMARY KEY,
-  Name         VARCHAR2(200),
-  Description  VARCHAR2(255),
-  ExecModule   VARCHAR2(60),
-  Argument     VARCHAR2(255),
-  Creator      NUMBER(11,0) DEFAULT 0 NOT NULL,
-  Created      DATE,
-  LastUpdatedBy        NUMBER(11,0) DEFAULT 0 NOT NULL,
-  LastUpdated  DATE
-);
-
-
-CREATE SEQUENCE TEMPLATES_seq;
-CREATE TABLE Templates (
-       id              NUMBER(11,0) 
-               CONSTRAINT Templates_Key PRIMARY KEY,
-       Queue           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Name            VARCHAR2(200) NOT NULL,
-       Description     VARCHAR2(255),
-       Type            VARCHAR2(16),
-       Language        VARCHAR2(16), 
-       TranslationOf   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Content         CLOB,
-       LastUpdated     DATE,
-       LastUpdatedBy   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE
-);
-
-
-CREATE SEQUENCE TICKETCUSTOMFIELDVALUES_seq;
-CREATE TABLE TicketCustomFieldValues (
-       id              NUMBER(11,0) 
-               CONSTRAINT TicketCustomFieldValues_Key PRIMARY KEY,
-       Ticket          NUMBER(11,0),
-       CustomField     NUMBER(11,0) NOT NULL,
-       Content         VARCHAR2(255),
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE,
-       LastUpdatedBy   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated     DATE
-);
-
-CREATE INDEX TicketCustomFieldValues1 ON TicketCustomFieldValues (CustomField,Ticket,Content); 
-CREATE INDEX TicketCustomFieldValues2 ON TicketCustomFieldValues (CustomField,Ticket); 
-
-CREATE SEQUENCE CUSTOMFIELDS_seq;
-CREATE TABLE CustomFields (
-       id              NUMBER(11,0) 
-               CONSTRAINT CustomFields_Key PRIMARY KEY,
-       Name            VARCHAR2(200),
-       Type            VARCHAR2(200),
-       Queue           NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Description     VARCHAR2(255),
-       SortOrder       NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE,
-       LastUpdatedBy   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated     DATE,
-       Disabled        NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX CustomFields1 ON CustomFields (Disabled, Queue);
-
-
-CREATE SEQUENCE CUSTOMFIELDVALUES_seq;
-CREATE TABLE CustomFieldValues (
-       id              NUMBER(11,0) 
-               CONSTRAINT CustomFieldValues_Key PRIMARY KEY,
-       CustomField     NUMBER(11,0),
-       Name            VARCHAR2(200),
-       Description     VARCHAR2(255),
-       SortOrder       NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Creator         NUMBER(11,0) DEFAULT 0 NOT NULL,
-       Created         DATE,
-       LastUpdatedBy   NUMBER(11,0) DEFAULT 0 NOT NULL,
-       LastUpdated     DATE
-);
-
-CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
-
-CREATE TABLE sessions (
-       id              VARCHAR2(32) 
-               CONSTRAINT Sessions_Key PRIMARY KEY,
-       a_session       CLOB,
-       LastUpdated     DATE
-);
-
index 5c833f8..052bf87 100644 (file)
 <td>
 % }
 <& /Elements/Callback, %ARGS &>
+<!--
 <div class="bpscredits">
 &#187;&#124;&#171; <&|/l, $RT::VERSION &>RT [_1] from <a href="http://bestpractical.com">Best Practical Solutions, LLC</a>.</&>
 </div>
+-->
 % if ($Debug) {
 <HR>
 <b><&|/l&>Time to display</&>: <%Time::HiRes::tv_interval( $m->{'rt_base_time'} )%></b>
index 23ab5f7..38cb4bd 100644 (file)
@@ -42,10 +42,9 @@ ONLOAD="
 >
 <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#FFFFFF">
   <tr> 
-    <td colspan=2><a href="http://bestpractical.com"><img src="<%$RT::WebImagesURL%>/bplogo.gif" alt="" width="230" height="50"></a></td>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-    <td width="50%" align="right">
+    <td rowspan=2><img border=0 alt="freeside" src="<%$RT::WebImagesURL%>/small-logo.png" width="92" height="62"></td>
+    <td align="left" rowspan=2><font size=6><% FS::Conf->new->config('company_name') %> Ticketing</font></td>
+    <td align="right" valign="top">
 % if ($session{'CurrentUser'} && $session{'CurrentUser'}->Id && $LoggedIn) {
 <SPAN STYLE="display: none"><A HREF="#skipnav"><&|/l&>Skip Menu</&></A> |</SPAN>
 <A  HREF="<%$RT::WebPath%><% $Prefs %>" ><&|/l&>Preferences</&></A>
@@ -59,6 +58,31 @@ ONLOAD="
 <&|/l&>Not logged in.</&>
 % }
     </td>
+
+  </tr>
+  <tr>
+
+    <td align=right valign=bottom>
+      <table>
+        <tr>
+          <td align=right>
+            <FONT SIZE="-3">
+              <A HREF="http://www.sisd.com/freeside">Freeside</A>&nbsp;v<% $FS::VERSION %><BR>
+             <A HREF="docs/">Documentation</A><BR>
+            </FONT>
+          </td>
+          <td bgcolor=#000000></td>
+          <td align=left>
+            <FONT SIZE="-3">
+             <A HREF="http://www.bestpractical.com/rt">RT</A>&nbsp;v<% $RT::VERSION %><BR>
+             <A HREF="http://wiki.bestpractical.com/">Documentation</A><BR>
+            </FONT>
+          </td>
+
+        </tr>
+      </table>
+    </td>
+
   </tr>
 </table>
 <%INIT>
index 6853175..6146b80 100644 (file)
 %# 
 %# 
 %# END LICENSE BLOCK
-<table class="darkblue" border=0 cellspacing=0 cellpadding=0 width="100%">
-  <th class="titlebox" align="left"><span class="rtname"><%$AppName%></span>
+<table class="lightgray" border=0 cellspacing=0 cellpadding=0 width="100%">
+  <th class="lightgray" align="left" width=50%><span class="rtname"><%$AppName%></span>
   </th>
       <span class="topactions">
 % foreach my $action (sort keys %{$topactions}) {
-        <td class="darkblueright">
+        <td class="lightgrayright">
         <%$topactions->{"$action"}->{'html'} |n %>
         </td>
 % }
 <table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%">
 %# Vertical menu
 <TR height="100%">
-<TD valign="top" width="140" class="blue">
+<TD valign="top" width="140" class="lightgray">
           <& /Elements/Menu, toptabs => $toptabs, current_toptab => $current_toptab &>
 </TD>
 <td valign="top">
 <table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
 <tr>
-  <td class="blue" valign="top">
+  <td class="mediumgray" valign="top">
     <span class="title"><%$title%></span>
 </td>
 </tr>
 <tr>
-<td class="blueright" valign="top">
+<td class="mediumgrayright" valign="top">
     <span class="nav"> 
 % if ($actions) {
 % my @actions;
@@ -57,7 +57,8 @@
 % push @actions,  "<A class='nav' HREF=\"".$RT::WebPath."/".$actions->{$action}->{'path'}."\">".$actions->{$action}->{'title'}."</A>";
 % }
 %  }
-<% join(" | ", @actions) | n %>
+%#<% join(" | ", @actions) | n %>
+<% '['. join("] [", @actions). ']' | n %>
 % if ($subactions) {
 % my @actions;
 % foreach my $action (sort keys %{$subactions}) {
@@ -95,5 +96,5 @@ $tabs => undef
 $actions => undef
 $subactions => undef
 $title => $m->callers(-1)->path
-$AppName => undef
+$AppName => ''
 </%ARGS>
index 4a0d106..85f3740 100644 (file)
@@ -23,5 +23,5 @@
 %# END LICENSE BLOCK
 <form action="<% $RT::WebPath %>/index.html">
 <input size="12" name="q" autocomplete="off" accesskey="0">
-<input type="submit" value="<&|/l&>Search</&>">&nbsp;
+<input type="submit" value="<&|/l&>Search tickets</&>">&nbsp;
 </form>
index 4db3849..6791e50 100644 (file)
@@ -40,7 +40,12 @@ my $basetopactions = {
        B => { html => $m->scomp('/Elements/SimpleSearch') 
                }
        };
-my $basetabs = {     A => { title => loc('Homepage'),
+my $basetabs = {
+                  ' A'=> { title => 'Billing Main',
+                           path  => '..',
+                         },
+                    A => { #title => loc('Homepage'),
+                           title => 'Ticketing Main',
                            path => '',
                          },
                     B => { title => loc('Tickets'),
diff --git a/rt/html/NoAuth/images/small-logo.png b/rt/html/NoAuth/images/small-logo.png
new file mode 100644 (file)
index 0000000..1e415e6
Binary files /dev/null and b/rt/html/NoAuth/images/small-logo.png differ
index 159e79c..3e8e3f9 100644 (file)
 %# END LICENSE BLOCK
 SPAN.nav {  font-family: Verdana, Arial, Helvetica, sans-serif;
         font-size: 12px;
-        color: #FFFFFF;
+%#      color: #FFFFFF;
+        color: #000000;
         text-decoration: none;
         white-space: nowrap}
 .nav2 {         font-size: 10px;
         white-space: nowrap}
 .nav {  font-family: Verdana, Arial, Helvetica, sans-serif;
         font-size: 13px;
-        font-weight: normal;
-        color: #FFFFFF;
+%#      font-weight: normal;
+        font-weight: bold;
+%#      color: #FFFFFF;
+        color: #000000;
         text-decoration: none;
         white-space: nowrap}
 .currentnav {  font-family: Verdana, Arial, Helvetica, sans-serif;
@@ -43,13 +46,15 @@ SPAN.nav {  font-family: Verdana, Arial, Helvetica, sans-serif;
 .topnav {  font-family: Verdana, Arial, Helvetica, sans-serif;
         font-size: 16px;
         font-weight: normal;
-        color: #FFFFFF;
+%#      color: #FFFFFF;
+        color: #000000;
         text-decoration: none;
         white-space: nowrap}
 .currenttopnav {  font-family: Verdana, Arial, Helvetica, sans-serif;
         font-size: 16px;
          font-weight: bold;
-        color: #FFFF66;
+%#      color: #FFFF66;
+        color: #000000; background-color: #cccccc;
         text-decoration: none;
         white-space: nowrap}
 .topactions {  font-family: Verdana, Arial, Helvetica, sans-serif;
@@ -79,12 +84,16 @@ SPAN.nav {  font-family: Verdana, Arial, Helvetica, sans-serif;
         vertical-align: top;
         text-align: left;
          }
-.blue {  background-color: #4682B4;
+.blue {
+        background-color: #4682B4;
+%#      background-color: #eeeeee;
         background-position: left top;
         vertical-align: top;
         text-align: left;
          }
-.blueright {  background-color: #4682B4;
+.blueright {
+        background-color: #4682B4;
+%#      background-color: #eeeeee;
         background-position: left top;
         vertical-align: top;
         text-align: right;
@@ -94,12 +103,50 @@ SPAN.nav {  font-family: Verdana, Arial, Helvetica, sans-serif;
         vertical-align: top;
         text-align: left;
          }
-.darkblue {  background-color: #000080;
+.darkblue {
+        background-color: #000080;
+        background-position: left top;
+        vertical-align: top;
+        text-align: left;
+         }
+.darkblueright { 
+        background-color: #000080;
+        background-position: left top;
+        vertical-align: top;
+        text-align: right;
+         }
+.lightgray {
+        background-color: #eeeeee;
+        background-position: left top;
+        vertical-align: top;
+        text-align: left;
+         }
+.lightgrayright { 
+        background-color: #eeeeee;
+        background-position: left top;
+        vertical-align: top;
+        text-align: right;
+         }
+.mediumgray {
+        background-color: #cccccc;
+        background-position: left top;
+        vertical-align: top;
+        text-align: left;
+         }
+.mediumgrayright { 
+        background-color: #cccccc;
+        background-position: left top;
+        vertical-align: top;
+        text-align: right;
+         }
+.white {
+        background-color: #ffffff;
         background-position: left top;
         vertical-align: top;
         text-align: left;
          }
-.darkblueright {  background-color: #000080;
+.whiteright { 
+        background-color: #ffffff;
         background-position: left top;
         vertical-align: top;
         text-align: right;
@@ -266,7 +313,9 @@ SPAN.date { font-size: 0.8em }
 
 span.title { font-size: 1.6em;
             vertical-align: middle;
-             color: #ffffff;}
+%#          color: #ffffff;
+             color: #000000;
+           }
 span.productname { font-size: 2em;
              color: #0066cc;}
 SPAN.titleboxtitle {
diff --git a/rt/html/Ticket/Elements/AddCustomers b/rt/html/Ticket/Elements/AddCustomers
new file mode 100644 (file)
index 0000000..66480e2
--- /dev/null
@@ -0,0 +1,53 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# 
+%# 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
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+<BR>
+<%$msg%><br>
+
+% if (@Customers) {
+
+<br><i>(Check box to link)<i>
+<table>
+% foreach my $customer (@Customers) {
+<tr>
+  <td>
+    <input type="checkbox" name="Ticket-AddCustomer-<% $customer->custnum %>" VALUE="1" <% scalar(@Customers) == 1 ? 'CHECKED' : '' %>>
+%#    <% $customer->name %>
+    <A HREF="<% $p %>view/cust_main.cgi?<% $customer->custnum %>"><% small_custview( $customer, scalar(FS::Conf->new->config('countrydefault')), 1 ) |n %>
+  </td>
+</tr>
+% }
+
+% }
+
+<%INIT>
+my ($msg);
+
+my @Customers = ();
+if ( $CustomerString ) {
+    @Customers = smart_search( 'search' => $CustomerString );
+    warn scalar(@Customers);
+}
+
+my @Services = ();
+if ($ServiceString) {
+    @Services = (); #service_search();
+}
+
+eval { use FS::CGI qw( popurl small_custview ); };
+my $p = eval { popurl(3); };
+
+</%INIT>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditCustomers b/rt/html/Ticket/Elements/EditCustomers
new file mode 100644 (file)
index 0000000..10b9c5b
--- /dev/null
@@ -0,0 +1,67 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# 
+%# 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
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+<TABLE width=100%>
+  <TR>
+    <TD VALIGN=TOP WIDTH=50%>
+      <h3><&|/l&>Current Customers</&></h3>
+
+<table>
+  <tr>
+    <td><i><&|/l&>(Check box to disassociate)</&></i></td>
+  </tr>
+  <tr>
+    <td class="value">
+% #while (my $link = $Ticket->MemberOf->Next) {
+% foreach my $link (
+%   grep { $_->TargetURI->Resolver->{'fstable'} eq 'cust_main' }
+%   grep { $_->TargetURI->Scheme eq 'freeside' }
+%        @{ $Ticket->_Links('Base')->ItemsArrayRef }
+% ) {
+
+      <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
+%#        <& ShowLink, URI => $link->TargetURI &><br>
+        <A HREF="<% $link->TargetURI->Resolver->HREF %>"><% $link->TargetURI->Resolver->AsStringLong |n %>
+      <BR>
+% }
+    </td>
+  </tr>
+</table>
+                           
+</TD>
+
+<TD VALIGN=TOP>
+<h3><&|/l&>New Customer Links</&></h3>
+<&|/l&>Find customer</&><BR>
+<input name="CustomerString">
+<input type=submit name="OnlySearchForCustomers" value="<&|/l&>Go!</&>">
+<br><i>cust #, last name, or company</i>
+<BR>
+%#<BR>
+%#<&|/l&>Find service</&><BR>
+%#<input name="ServiceString">
+%#<input type=submit name="OnlySearchForServices" value="<&|/l&>Go!</&>">
+%#<br><i>username, username@domain, domain, or IP address</i>
+%#<BR>
+
+<& AddCustomers, Ticket         => $Ticket,
+                 CustomerString => $CustomerString,
+                 ServiceString  => $ServiceString,  &>
+
+</TD>
+</TR>
+</TABLE>
+      
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowCustomers b/rt/html/Ticket/Elements/ShowCustomers
new file mode 100644 (file)
index 0000000..5519d24
--- /dev/null
@@ -0,0 +1,40 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%#
+%# 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
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+<table>
+% my $cust = 0;
+% foreach my $customerURI (
+%   grep { $_->Resolver->{'fstable'} eq 'cust_main' }
+%   grep { $_->Scheme eq 'freeside' }
+%    map { $_->TargetURI }
+%        @{ $Ticket->_Links('Base')->ItemsArrayRef }
+% ) {
+%   $cust++;
+%   my $cust_main = '';
+  <tr>
+    <td class="value">
+      <A HREF="<% $customerURI->Resolver->HREF %>"><% $customerURI->Resolver->AsStringLong |n %>
+    </td>
+  </tr>
+% }
+% unless ( $cust ) {
+  <tr>
+    <td class="labeltop">
+      <i>(none)<i>
+    </td>
+  </tr>
+
+% }
+</table>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
+
index 5bcc94b..1b9f629 100644 (file)
                color => "#333399" &>
          <& /Ticket/Elements/ShowPeople, Ticket => $Ticket &>
          <& /Elements/TitleBoxEnd &>
+         <br>
+
+         <& /Elements/TitleBoxStart, title => loc('Customers'),
+               title_href =>"$RT::WebPath/Ticket/ModifyCustomers.html?id=".$Ticket->Id,
+               title_class=> 'inverse',
+               color => "#7f007b" &>
+          <& /Ticket/Elements/ShowCustomers, Ticket => $Ticket &>
+          <& /Elements/TitleBoxEnd &>
+
        <BR>
        </TD>
        <TD VALIGN=TOP WIDTH="50%">
index cba45df..26b2527 100644 (file)
@@ -101,6 +101,8 @@ my $ticket_page_tabs = {
       { title => loc('People'), path => "Ticket/ModifyPeople.html?id=" . $id, },
     _E => { title => loc('Links'),
             path  => "Ticket/ModifyLinks.html?id=" . $id, },
+    _Eb=> { title => loc('Customers'),
+            path  => "Ticket/ModifyCustomers.html?id=" . $id, },
     _F => { title => loc('Jumbo'),
             path  => "Ticket/ModifyAll.html?id=" . $id,
             seperator => 1
diff --git a/rt/html/Ticket/ModifyCustomers.html b/rt/html/Ticket/ModifyCustomers.html
new file mode 100644 (file)
index 0000000..72d103b
--- /dev/null
@@ -0,0 +1,49 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%#
+%# 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
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+<& /Elements/Header, Title => loc("Customers for ticket #[_1]", $Ticket->Id) &>
+<& /Ticket/Elements/Tabs, 
+    Ticket => $Ticket, 
+    current_tab => "Ticket/ModifyCustomers.html?id=".$Ticket->Id, 
+    Title => loc("Customers for ticket #[_1]", $Ticket->Id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="ModifyCustomers.html" method="post">
+<input type="hidden" name="id" value="<%$Ticket->id%>">
+
+<& /Elements/TitleBoxStart, title => loc('Edit Customer Links'), color => "#7f007b"&>
+<& Elements/EditCustomers, Ticket => $Ticket, CustomerString => $CustomerString, ServiceString => $ServiceString &>
+<& /Elements/TitleBoxEnd &>
+<& /Elements/Submit, color => "#7f007b", Label => loc('Save Changes') &>
+</form>
+
+
+<%INIT>
+
+my @results = ();
+my $Ticket = LoadTicket($id);
+
+# if we're trying to search for customers/services and nothing else
+unless ( $OnlySearchForCustomers || $OnlySearchForServices) {
+   @results = ProcessTicketCustomers( TicketObj => $Ticket, ARGSRef => \%ARGS);
+}
+    
+</%INIT>
+
+
+<%ARGS>
+$OnlySearchForCustomers => undef
+$OnlySearchForServices => undef
+$CustomerString => undef
+$ServiceString => undef
+$id => undef
+</%ARGS>
diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm
new file mode 100644 (file)
index 0000000..5be20e6
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+#
+# 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
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+# 
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+
+=head1 NAME
+
+RT::Interface::Web_Vendor
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Freeside vendor overlay for RT::Interface::Web.
+
+=begin testing
+
+use_ok(RT::Interface::Web_Vendor);
+
+=end testing
+
+=cut
+
+#package RT::Interface::Web;
+#use strict;
+
+package HTML::Mason::Commands;
+use strict;
+
+=head2 ProcessTicketCustomers 
+
+=cut
+
+sub ProcessTicketCustomers {
+    my %args = (
+        TicketObj => undef,
+        ARGSRef   => undef,
+        @_
+    );
+    my @results = ();
+
+    my $Ticket  = $args{'TicketObj'};
+    my $ARGSRef = $args{'ARGSRef'};
+
+    ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+    # Delete links that are gone gone gone.
+    foreach my $arg ( keys %$ARGSRef ) {
+        if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+            my $base   = $1;
+            my $type   = $2;
+            my $target = $3;
+
+            push @results,
+              "Trying to delete: Base: $base Target: $target  Type $type";
+            my ( $val, $msg ) = $Ticket->DeleteLink( Base   => $base,
+                                                     Type   => $type,
+                                                     Target => $target );
+
+            push @results, $msg;
+
+        }
+
+    }
+    ###
+
+    my @delete_custnums =
+      map  { /^Ticket-AddCustomer-(\d+)$/; $1 }
+      grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+      keys %$ARGSRef;
+
+    my @custnums = map  { /^Ticket-AddCustomer-(\d+)$/; $1 }
+                   grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+                   keys %$ARGSRef;
+
+    foreach my $custnum ( @custnums ) {
+      my( $val, $msg ) =
+        $Ticket->AddLink( 'Type'   => 'MemberOf',
+                          'Target' => "freeside://freeside/cust_main/$custnum",
+                        );
+      push @results, $msg;
+    }
+
+    return @results;
+
+}
+
+1;
+
diff --git a/rt/lib/RT/URI/freeside.pm b/rt/lib/RT/URI/freeside.pm
new file mode 100644 (file)
index 0000000..ebd24ad
--- /dev/null
@@ -0,0 +1,260 @@
+# BEGIN LICENSE BLOCK
+# 
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+# 
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+# 
+# (Except where explictly superceded by other copyright notices)
+# 
+# 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
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+# 
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# 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.
+# 
+# 
+# END LICENSE BLOCK
+package RT::URI::freeside;
+
+use RT::URI::base;
+use strict;
+use vars qw(@ISA);
+
+@ISA = qw/RT::URI::base/;
+
+
+=head1 NAME
+
+RT::URI::base
+
+=head1 DESCRIPTION
+
+URI handler for freeside URIs.  See http://www.sisd.com/freeside/ for
+more information on freeside.
+
+=cut
+
+
+sub FreesideURIPrefix {
+
+  my $self = shift;
+  return($self->Scheme . '://freeside');
+
+}
+
+sub FreesideURILabel {
+
+  my $self = shift;
+
+  return(undef) unless (exists($self->{'fstable'}) and
+                        exists($self->{'fspkey'}));
+
+  my $label;
+  my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+  eval {
+    use FS::UID qw(dbh);
+    use FS::Record qw(qsearchs qsearch dbdef);
+    eval "use FS::$table;";
+    use FS::cust_svc;
+
+    my $dbdef = dbdef or die "No dbdef";
+    my $pkeyfield = $dbdef->table($table)->primary_key
+      or die "No primary key for table $table";
+
+    my $rec = qsearchs($table, { $pkeyfield => $pkey })
+      or die "Record with $pkeyfield == $pkey does not exist in table $table";
+
+    if ($table =~ /^svc_/) {
+      if ($rec->can('cust_svc')) {
+        my $cust_svc = $rec->cust_svc or die '$rec->cust_svc failed';
+        my ($svc, $tag, $svcdb) = $cust_svc->label;
+        $label = "Freeside service ${svc}: ${tag}";
+      }
+    } elsif ($table eq 'cust_main') {
+      #my ($last, $first, $company) = map { $rec->getfield($_) }
+      #                                   qw(last first company);
+      #$label = "Freeside customer ${last}, ${first}";
+      #$label .= ($company ne '') ? " with ${company}" : '';
+      $label = "$pkey: ". $rec->name;
+    } else {
+      $label = "Freeside ${table}, ${pkeyfield} == ${pkey}";
+    }
+
+    #... other cases
+
+  };
+
+  if ($label and !$@) {
+    return($label);
+  } else {
+    return(undef);
+  }
+      
+
+}
+
+sub FreesideURILabelLong {
+
+  my $self = shift;
+
+  return(undef) unless (exists($self->{'fstable'}) and
+                        exists($self->{'fspkey'}));
+
+  my $label;
+  my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+  eval {
+    use FS::UID qw(dbh);
+    use FS::Record qw(qsearchs qsearch dbdef);
+    eval "use FS::$table;";
+    use FS::cust_svc;
+
+    my $dbdef = dbdef or die "No dbdef";
+    my $pkeyfield = $dbdef->table($table)->primary_key
+      or die "No primary key for table $table";
+
+    my $rec = qsearchs($table, { $pkeyfield => $pkey })
+      or die "Record with $pkeyfield == $pkey does not exist in table $table";
+
+    if ($table =~ /^svc_/) {
+      #if ($rec->can('cust_svc')) {
+      #  my $cust_svc = $rec->cust_svc or die '$rec->cust_svc failed';
+      #  my ($svc, $tag, $svcdb) = $cust_svc->label;
+      #  $label = "Freeside service ${svc}: ${tag}";
+      #}
+      $label = '';
+    } elsif ($table eq 'cust_main') {
+      use FS::CGI qw(small_custview);
+      $label = small_custview( $rec,
+                               scalar(FS::Conf->new->config('countrydefault')),
+                               1 #nobalance
+                             );
+    } else {
+      #$label = "Freeside ${table}, ${pkeyfield} == ${pkey}";
+      $label = '';
+    }
+
+    #... other cases
+
+  };
+
+  if ($label and !$@) {
+    return($label);
+  } else {
+    warn $@;
+    return(undef);
+  }
+      
+
+}
+
+sub ParseURI { 
+    my $self = shift;
+    my $uri = shift;
+    my ($table, $pkey);
+
+    my $uriprefix = $self->FreesideURIPrefix;
+    if ($uri =~ /^$uriprefix\/(\w+)\/(\d+)$/) {
+      $table = $1;
+      $pkey = $2;
+      $self->{'scheme'} = $self->Scheme;
+    } else {
+      return(undef);
+    }
+
+    $self->{'uri'} = "${uriprefix}/${table}/${pkey}";
+    $self->{'fstable'} = $table;
+    $self->{'fspkey'} = $pkey;
+
+    my $p;
+
+    eval {
+      use FS::UID qw(dbh);
+      use FS::CGI qw(popurl);
+
+      if (dbh) {
+       $p = popurl(3);
+      }
+
+    };
+
+    if ($@ or (!$p)) {
+      $self->{'href'} = $self->{'uri'};
+    } else {
+      $self->{'href'} = "${p}view/${table}.cgi?${pkey}";
+    }
+
+    $self->{'uri'};
+
+}
+
+sub Scheme { 
+    my $self = shift;
+    return('freeside');
+
+}
+
+sub HREF {
+    my $self = shift;
+    return($self->{'href'} || $self->{'uri'});
+}
+
+sub IsLocal {
+    my $self = shift;
+    return undef;
+}
+
+=head2 AsString
+
+Return a "pretty" string representing the URI object.
+
+This is meant to be used like this:
+
+ % $re = $uri->Resolver;
+ <A HREF="<% $re->HREF %>"><% $re->AsString %></A>
+
+=cut
+
+sub AsString {
+    my $self = shift;
+    my $prettystring;
+    if ($prettystring = $self->FreesideURILabel) {
+      return $prettystring;
+    } else {
+      return $self->URI;
+    }
+}
+
+=head2 AsStringLong
+
+Return a longer (HTML) string representing the URI object.
+
+=cut
+
+sub AsStringLong {
+    my $self = shift;
+    my $prettystring;
+    if ($prettystring = $self->FreesideURILabelLong || $self->FreesideURILabel){
+      return $prettystring;
+    } else {
+      return $self->URI;
+    }
+}
+
+eval "require RT::URI::base_Vendor";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI/base_Vendor.pm});
+eval "require RT::URI::base_Local";
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/URI/base_Local.pm});
+
+1;
diff --git a/rt/sbin/rt-setup-database b/rt/sbin/rt-setup-database
deleted file mode 100644 (file)
index 58f882f..0000000
+++ /dev/null
@@ -1,619 +0,0 @@
-#!/usr/bin/perl -w
-# BEGIN LICENSE BLOCK
-# 
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-# 
-# (Except where explictly superceded by other copyright notices)
-# 
-# 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
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-# 
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# 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.
-# 
-# 
-# END LICENSE BLOCK
-
-use strict;
-use vars qw($PROMPT $VERSION $Handle $Nobody $SystemUser $item);
-use vars
-  qw(@Groups @Users @ACL @Queues @ScripActions @ScripConditions @Templates @CustomFields @Scrips);
-
-use lib "/opt/rt3/lib";
-
-#This drags in  RT's config.pm
-# We do it in a begin block because RT::Handle needs to know the type to do its
-# inheritance
-use RT;
-use Carp;
-use RT::User;
-use RT::CurrentUser;
-use RT::Template;
-use RT::ScripAction;
-use RT::ACE;
-use RT::Group;
-use RT::User;
-use RT::Queue;
-use RT::ScripCondition;
-use RT::CustomField;
-use RT::Scrip;
-
-RT::LoadConfig();
-use Term::ReadKey;
-use Getopt::Long;
-
-my %args;
-
-GetOptions(
-    \%args,
-    'prompt-for-dba-password', 'force', 'debug',
-    'action=s',                'dba=s', 'dba-password=s', 'datafile=s',
-    'datadir=s'
-);
-
-$| = 1;    #unbuffer that output.
-
-require RT::Handle;
-my $Handle = RT::Handle->new($RT::DatabaseType);
-$Handle->BuildDSN;
-my $dbh;
-
-if ( $args{'prompt-for-dba-password'} ) {
-    $args{'dba-password'} = get_dba_password();
-    chomp( $args{'dba-password'} );
-}
-
-unless ( $args{'action'} ) {
-    help();
-    die;
-}
-if ( $args{'action'} eq 'init' ) {
-    $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} )
-      || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
-    print "Now creating a database for RT.\n";
-    if ($RT::DatabaseType ne 'Oracle' ||
-        $args{'dba'} ne $RT::DatabaseUser) {
-    create_db();
-    } else {
-        print "...skipped as ".$args{'dba'} ." is not " . $RT::DatabaseUser . " or we're working with Oracle.\n";
-    }
-
-    $dbh->disconnect;
-    $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
-      || die $DBI::errstr;
-
-    print "Now populating database schema.\n";
-    insert_schema();
-    print "Now inserting database ACLs\n";
-    insert_acl() unless ($RT::DatabaseType eq 'Oracle');
-    print "Now inserting RT core system objects\n";
-    insert_initial_data();
-    print "Now inserting RT data\n";
-    insert_data( $RT::EtcPath . "/initialdata" );
-}
-elsif ( $args{'action'} eq 'drop' ) {
-    unless ( $dbh =
-         DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) )
-    {
-        warn $DBI::errstr;
-        warn "Database doesn't appear to exist. Aborting database drop.";
-        exit(0);
-    }
-    drop_db();
-}
-elsif ( $args{'action'} eq 'insert' ) {
-    insert_data( $args{'datafile'} );
-}
-elsif ($args{'action'} eq 'acl') {
-    $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
-      || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
-     insert_acl($args{'datadir'});
-}
-elsif ($args{'action'} eq 'schema') {
-    $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
-      || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
-        insert_schema($args{'datadir'});
-}
-
-else {
-    print STDERR '$0 called with an invalid --action parameter';
-    exit(-1);
-}
-
-# {{{ sub insert_schema
-sub insert_schema {
-        my $base_path = (shift || $RT::EtcPath);
-    my (@schema);
-    print "Creating database schema.\n";
-
-    if ( -f $base_path . "/schema." . $RT::DatabaseType ) {
-       no warnings 'unopened';
-
-        open( SCHEMA, "<" . $base_path . "/schema." . $RT::DatabaseType );
-        open( SCHEMA_LOCAL, "<" . $RT::LocalEtcPath . "/schema." . $RT::DatabaseType );
-
-        my $statement = "";
-        foreach my $line (<SCHEMA>, ($_ = ';;'), <SCHEMA_LOCAL>) {
-            $line =~ s/\#.*//g;
-            $line =~ s/--.*//g;
-            $statement .= $line;
-            if ( $line =~ /;(\s*)$/ ) {
-                $statement =~ s/;(\s*)$//g;
-                push @schema, $statement;
-                $statement = "";
-            }
-        }
-
-       local $SIG{__WARN__} = sub {};
-       my $is_local = 0; # local/etc/schema needs to be nonfatal. 
-        foreach my $statement (@schema) {
-           if ($statement =~ /^\s*;$/) { $is_local = 1; next; }
-            print STDERR "SQL: $statement\n" if defined $args{'debug'};
-            my $sth = $dbh->prepare($statement) or die $dbh->errstr;
-            unless ( $sth->execute or $is_local ) {
-                die "Problem with statement:\n $statement\n" . $sth->errstr;
-            }
-        }
-
-    }
-    else {
-        die "Couldn't find schema file for " . $RT::DatabaseType . "\n";
-    }
-    print "schema sucessfully inserted\n";
-
-}
-
-# }}}
-
-# {{{ sub drop_db
-sub drop_db {
-    return if ( $RT::DatabaseType eq 'SQLite' );
-    if ( $RT::DatabaseType eq 'Oracle' ) {
-        print <<END;
-
-To delete the tables and sequences of the RT Oracle database by running 
-    \@etc/drop.Oracle 
-through SQLPlus.
-
-END
-        return;
-    }  
-    unless ( $args{'force'} ) {
-        print <<END;
-
-About to drop $RT::DatabaseType database $RT::DatabaseName on $RT::DatabaseHost.
-WARNING: This will erase all data in $RT::DatabaseName.
-
-END
-        exit unless _yesno();
-
-    }
-
-    print "Dropping $RT::DatabaseType database $RT::DatabaseName.\n";
-
-    $dbh->do("Drop DATABASE $RT::DatabaseName") or warn $DBI::errstr;
-}
-
-# }}}
-
-# {{{ sub create_db
-sub create_db {
-    print "Creating $RT::DatabaseType database $RT::DatabaseName.\n";
-    if ( $RT::DatabaseType eq 'SQLite' ) {
-        return;
-    }
-    elsif ( $RT::DatabaseType eq 'Pg' ) {
-        $dbh->do("CREATE DATABASE $RT::DatabaseName WITH ENCODING='UNICODE'");
-        if ($DBI::errstr) {
-            $dbh->do("CREATE DATABASE $RT::DatabaseName") || die $DBI::errstr;
-        }
-    }
-    elsif ($RT::DatabaseType eq 'Oracle') {
-        insert_acl();
-    }
-    elsif ( $RT::DatabaseType eq 'Informix' ) {
-       $ENV{DB_LOCALE} = 'en_us.utf8';
-        $dbh->do("CREATE DATABASE $RT::DatabaseName WITH BUFFERED LOG");
-    }
-    else {
-        $dbh->do("CREATE DATABASE $RT::DatabaseName") or die $DBI::errstr;
-    }
-}
-
-# }}}
-
-sub get_dba_password {
-    print
-"In order to create a new database and grant RT access to that database,\n";
-    print "this script needs to connect to your "
-      . $RT::DatabaseType
-      . " instance on "
-      . $RT::DatabaseHost . " as "
-      . $args{'dba'} . ".\n";
-    print
-"Please specify that user's database password below. If the user has no database\n";
-    print "password, just press return.\n\n";
-    print "Password: ";
-    ReadMode('noecho');
-    my $password = ReadLine(0);
-    ReadMode('normal');
-    return ($password);
-}
-
-# {{{ sub _yesno
-sub _yesno {
-    print "Proceed [y/N]:";
-    my $x = scalar(<STDIN>);
-    $x =~ /^y/i;
-}
-
-# }}}
-
-# {{{ insert_acls
-sub insert_acl {
-
-        my $base_path = (shift || $RT::EtcPath);
-
-    if ( $RT::DatabaseType =~ /^oracle$/i ) {
-        do $base_path . "/acl.Oracle"
-          || die "Couldn't find ACLS for Oracle\n" . $@;
-    }
-    elsif ( $RT::DatabaseType =~ /^pg$/i ) {
-        do $base_path . "/acl.Pg" || die "Couldn't find ACLS for Pg\n" . $@;
-    }
-    elsif ( $RT::DatabaseType =~ /^mysql$/i ) {
-        do $base_path . "/acl.mysql"
-          || die "Couldn't find ACLS for mysql in " . $RT::EtcPath . "\n" . $@;
-    }
-    elsif ( $RT::DatabaseType =~ /^informix$/i ) {
-        do $base_path . "/acl.Informix"
-          || die "Couldn't find ACLS for Informix in " . $RT::EtcPath . "\n" . $@;
-    }
-    elsif ( $RT::DatabaseType =~ /^SQLite$/i ) {
-        return;
-    }
-    else {
-        die "Unknown RT database type";
-    }
-
-    my @acl = acl($dbh);
-    foreach my $statement (@acl) {
-        print STDERR $statement if $args{'debug'};
-        my $sth = $dbh->prepare($statement) or die $dbh->errstr;
-        unless ( $sth->execute ) {
-            die "Problem with statement:\n $statement\n" . $sth->errstr;
-        }
-    }
-}
-
-# }}}
-
-=head2 get_system_dsn
-
-Returns a dsn suitable for database creates and drops
-and user creates and drops
-
-=cut
-
-sub get_system_dsn {
-
-    my $dsn = $Handle->DSN;
-
-    #with mysql, you want to connect sans database to funge things
-    if ( $RT::DatabaseType eq 'mysql' ) {
-        $dsn =~ s/dbname=$RT::DatabaseName//;
-
-        # with postgres, you want to connect to database1
-    }
-    elsif ( $RT::DatabaseType eq 'Pg' ) {
-        $dsn =~ s/dbname=$RT::DatabaseName/dbname=template1/;
-    }
-    elsif ( $RT::DatabaseType eq 'Informix' ) {
-       # with Informix, you want to connect sans database:
-       $dsn =~ s/Informix:$RT::DatabaseName/Informix:/;
-    }
-    return $dsn;
-}
-
-sub insert_initial_data {
-
-    RT::InitLogging();
-
-    #connect to the db, for actual RT work
-    require RT::Handle;
-    $RT::Handle = RT::Handle->new();
-    $RT::Handle->Connect();
-
-    #Put together a current user object so we can create a User object
-    my $CurrentUser = new RT::CurrentUser();
-
-    print "Checking for existing system user ($CurrentUser)...";
-    my $test_user = RT::User->new($CurrentUser);
-    $test_user->Load('RT_System');
-    if ( $test_user->id ) {
-        print "found!\n\nYou appear to have a functional RT database.\n"
-          . "Exiting, so as not to clobber your existing data.\n";
-        exit(-1);
-
-    }
-    else {
-        print "not found.  This appears to be a new installation.\n";
-    }
-
-    print "Creating system user...";
-    my $RT_System = new RT::User($CurrentUser);
-
-    my ( $val, $msg ) = $RT_System->_BootstrapCreate(
-        Name     => 'RT_System',
-        RealName => 'The RT System itself',
-        Comments =>
-'Do not delete or modify this user. It is integral to RT\'s internal database structures',
-        Creator => '1' );
-
-    unless ($val) {
-        print "$msg\n";
-        exit(1);
-    }
-    print "done.\n";
-    $RT::Handle->Disconnect();
-
-}
-
-# load some sort of data into the database
-
-sub insert_data {
-    my $datafile = shift;
-
-    #Connect to the database and get RT::SystemUser and RT::Nobody loaded
-    RT::Init;
-
-    my $CurrentUser = RT::CurrentUser->new();
-    $CurrentUser->LoadByName('RT_System');
-
-    if ( $datafile eq $RT::EtcPath . "/initialdata" ) {
-
-        print "Creating Superuser  ACL...";
-
-        my $superuser_ace = RT::ACE->new($CurrentUser);
-        $superuser_ace->_BootstrapCreate(
-                             PrincipalId => ACLEquivGroupId( $CurrentUser->Id ),
-                             PrincipalType => 'Group',
-                             RightName     => 'SuperUser',
-                             ObjectType    => 'RT::System',
-                             ObjectId      => '1' );
-
-    }
-
-    # Slurp in stuff to insert from the datafile. Possible things to go in here:-
-    # @groups, @users, @acl, @queues, @ScripActions, @ScripConditions, @templates
-
-    require $datafile
-      || die "Couldn't find initial data for import\n" . $@;
-
-    if (@Groups) {
-        print "Creating groups...";
-        foreach $item (@Groups) {
-            my $new_entry = RT::Group->new($CurrentUser);
-            my ( $return, $msg ) = $new_entry->_Create(%$item);
-            print "(Error: $msg)" unless ($return);
-            print $return. ".";
-        }
-        print "done.\n";
-    }
-    if (@Users) {
-        print "Creating users...";
-        foreach $item (@Users) {
-            my $new_entry = new RT::User($CurrentUser);
-            my ( $return, $msg ) = $new_entry->Create(%$item);
-            print "(Error: $msg)" unless ($return);
-            print $return. ".";
-        }
-        print "done.\n";
-    }
-    if (@Queues) {
-        print "Creating queues...";
-        for $item (@Queues) {
-            my $new_entry = new RT::Queue($CurrentUser);
-            my ( $return, $msg ) = $new_entry->Create(%$item);
-            print "(Error: $msg)" unless ($return);
-            print $return. ".";
-        }
-        print "done.\n";
-    }
-    if (@ACL) {
-        print "Creating ACL...";
-        for my $item (@ACL) {
-
-           my ($princ, $object);
-
-           # Global rights or Queue rights?
-           if ($item->{'Queue'}) {
-                $object = RT::Queue->new($CurrentUser);
-                $object->Load( $item->{'Queue'} );
-           } else {
-               $object = $RT::System;
-           }
-
-           # Group rights or user rights?
-           if ($item->{'GroupDomain'}) {
-                $princ = RT::Group->new($CurrentUser);
-               if ($item->{'GroupDomain'} eq 'UserDefined') {
-                  $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
-               } elsif ($item->{'GroupDomain'} eq 'SystemInternal') {
-                  $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
-               } elsif ($item->{'GroupDomain'} eq 'RT::Queue-Role' &&
-                        $item->{'Queue'}) {
-                  $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
-                                             Queue => $object->id);
-               } else {
-                  $princ->Load( $item->{'GroupId'} );
-               }
-           } else {
-               $princ = RT::User->new($CurrentUser);
-               $princ->Load( $item->{'UserId'} );
-           }
-
-           # Grant it
-           my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
-                                                     Right => $item->{'Right'},
-                                                     Object => $object );
-
-            if ($return) {
-                print $return. ".";
-            }
-            else {
-                print $msg . ".";
-
-            }
-
-        }
-        print "done.\n";
-    }
-    if (@CustomFields) {
-        print "Creating custom fields...";
-        for $item (@CustomFields) {
-            my $new_entry = new RT::CustomField($CurrentUser);
-            my $values    = $item->{'Values'};
-            delete $item->{'Values'};
-            my $q     = $item->{'Queue'};
-            my $q_obj = RT::Queue->new($CurrentUser);
-            $q_obj->Load($q);
-            if ( $q_obj->Id ) {
-                $item->{'Queue'} = $q_obj->Id;
-            }
-            elsif ( $q == 0 ) {
-                $item->{'Queue'} = 0;
-            }
-            else {
-                print "(Error: Could not find queue " . $q . ")\n"
-                  unless ( $q_obj->Id );
-                next;
-            }
-            my ( $return, $msg ) = $new_entry->Create(%$item);
-
-            foreach my $value ( @{$values} ) {
-                my ( $eval, $emsg ) = $new_entry->AddValue(%$value);
-                print "(Error: $emsg)\n" unless ($eval);
-            }
-
-            print "(Error: $msg)\n" unless ($return);
-            print $return. ".";
-        }
-
-        print "done.\n";
-    }
-
-    if (@ScripActions) {
-        print "Creating ScripActions...";
-
-        for $item (@ScripActions) {
-            my $new_entry = RT::ScripAction->new($CurrentUser);
-            my $return    = $new_entry->Create(%$item);
-            print $return. ".";
-        }
-
-        print "done.\n";
-    }
-
-    if (@ScripConditions) {
-        print "Creating ScripConditions...";
-
-        for $item (@ScripConditions) {
-            my $new_entry = RT::ScripCondition->new($CurrentUser);
-            my $return    = $new_entry->Create(%$item);
-            print $return. ".";
-        }
-
-        print "done.\n";
-    }
-
-    if (@Templates) {
-        print "Creating templates...";
-
-        for $item (@Templates) {
-            my $new_entry = new RT::Template($CurrentUser);
-            my $return    = $new_entry->Create(%$item);
-            print $return. ".";
-        }
-        print "done.\n";
-    }
-    if (@Scrips) {
-        print "Creating scrips...";
-
-        for $item (@Scrips) {
-            my $new_entry = new RT::Scrip($CurrentUser);
-            my ( $return, $msg ) = $new_entry->Create(%$item);
-            if ($return) {
-                print $return. ".";
-            }
-            else {
-                print "(Error: $msg)\n";
-            }
-        }
-        print "done.\n";
-    }
-    $RT::Handle->Disconnect();
-
-}
-
-=head2 ACLEquivGroupId
-
-Given a userid, return that user's acl equivalence group
-
-=cut
-
-sub ACLEquivGroupId {
-    my $username = shift;
-    my $user     = RT::User->new($RT::SystemUser);
-    $user->Load($username);
-    my $equiv_group = RT::Group->new($RT::SystemUser);
-    $equiv_group->LoadACLEquivalenceGroup($user);
-    return ( $equiv_group->Id );
-}
-
-sub help {
-
-    print <<EOF;
-
-$0: Set up RT's database
-
---action        init    Initialize the database
-                drop    Drop the database. 
-                        This will ERASE ALL YOUR DATA
-                insert  Insert data into RT's database. 
-                        By default, will use RT's installation data.
-                        To use a local or supplementary datafile, specify it
-                        using the '--datafile' option below.
-                        
-                acl     Initialize only the database ACLs
-                        To use a local or supplementary datafile, specify it
-                        using the '--datadir' option below.
-                        
-                schema  Initialize only the database schema
-                        To use a local or supplementary datafile, specify it
-                        using the '--datadir' option below.
-
---datafile /path/to/datafile
---datadir /path/to/              Used to specify a path to find the local
-                                database schema and acls to be installed.
-
-
---dba                           dba's username
---dba-password                  dba's password
---prompt-for-dba-password       Ask for the database administrator's password interactively
-
-
-EOF
-
-}
-
-1;
index a7ee86d..0b9a107 100644 (file)
@@ -110,6 +110,9 @@ elsif ( $args{'action'} eq 'drop' ) {
     }
     drop_db();
 }
+elsif ( $args{'action'} eq 'insert_initial' ) {
+    insert_initial_data();
+}
 elsif ( $args{'action'} eq 'insert' ) {
     insert_data( $args{'datafile'} );
 }
@@ -591,6 +594,8 @@ $0: Set up RT's database
 --action        init    Initialize the database
                 drop    Drop the database. 
                         This will ERASE ALL YOUR DATA
+                insert_initial 
+                        Insert RT's core system objects
                 insert  Insert data into RT's database. 
                         By default, will use RT's installation data.
                         To use a local or supplementary datafile, specify it
diff --git a/rt/sbin/rt-test-dependencies b/rt/sbin/rt-test-dependencies
deleted file mode 100644 (file)
index c1591b1..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/usr/bin/perl
-# BEGIN LICENSE BLOCK
-# 
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-# 
-# (Except where explictly superceded by other copyright notices)
-# 
-# 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
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-# 
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# 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.
-# 
-# 
-# END LICENSE BLOCK
-
-#
-# This is just a basic script that checks to make sure that all
-# the modules needed by RT before you can install it.
-#
-
-use strict;
-no warnings qw(numeric redefine);
-use Getopt::Long;
-use CPAN;
-my %args;
-my %deps;
-GetOptions(\%args,'install', 'with-MYSQL', 'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE', 'with-ORACLE', 'with-FASTCGI', 'with-SPEEDYCGI', 'with-MODPERL1', 'with-MODPERL2' ,'with-DEV');
-
-if (!keys %args) {
-    help();
-    exit(0);
-}
-if ($args{'with-MODPERL2'}) {
-    warn_modperl2();
-}
-
-$args{'with-MASON'} = 1;
-$args{'with-CORE'} = 1;
-$args{'with-DEV'} =1; 
-$args{'with-CLI'} =1; 
-$args{'with-MAILGATE'} =1; 
-if ($] < 5.007) {
-$args{'with-I18N-COMPAT'} = 1;
-}
-
-sub warn_modperl2 {
-    print <<'.';
-        NOTE: mod_perl 2.0 isn't quite ready for prime_time just yet;
-        Best Practical Solutions strongly recommends that sites use
-        Apache 1.3 or FastCGI. If you MUST use mod_perl 2.0 (or 1.99),
-        please read the mailing list archives before asking for help.
-.
-    sleep 5;
-}
-
-
-sub help {
-
-    print <<'.';
-
-By default, testdeps determine whether you have 
-installed all the perl modules RT needs to run.
-
-       --install               Install missing modules
-
-The following switches will tell the tool to check for specific dependencies
-
-       --with-mysql            Database interface for MySQL
-       --with-postgresql       Database interface for PostgreSQL 
-       --with-sqlite           Database interface and driver for SQLite (unsupported)
-       --with-oracle           Database interface for oracle (unsupported)
-
-       --with-fastcgi          Libraries needed to support the fastcgi handler
-       --with-speedycgi        Libraries needed to support the speedycgi handler
-       --with-modperl1         Libraries needed to support the modperl 1 handler
-       --with-modperl2         Libraries needed to support the modperl 2 handler
-
-       --with-dev              Tools needed for RT development
-.
-}
-
-
-sub _ {
-    map { /(\S+)\s*(\S*)/; $1 => ($2 ? $2 :'') } split ( /\n/, $_[0] );
-}
-
-$deps{'CORE'} = [ _( << '.') ];
-Digest::MD5 2.27
-DBI 1.37
-Test::Inline
-Class::ReturnValue 0.40
-DBIx::SearchBuilder 0.97
-Text::Template
-File::Spec 0.8
-HTML::Entities 
-Net::Domain
-Log::Dispatch 2.0
-Locale::Maketext 1.06
-Locale::Maketext::Lexicon 0.32
-Locale::Maketext::Fuzzy
-MIME::Entity 5.108
-Mail::Mailer 1.57
-Net::SMTP
-Text::Wrapper 
-Time::ParseDate
-File::Temp
-Term::ReadKey
-Text::Autoformat
-Text::Quoted 1.3
-Scalar::Util
-.
-
-$deps{'MASON'} = [ _( << '.') ];
-Params::Validate 0.02
-Cache::Cache
-Exception::Class
-HTML::Mason 1.16
-MLDBM
-Errno
-FreezeThaw
-Digest::MD5 2.27
-CGI::Cookie 1.20
-Storable 2.08
-Apache::Session 1.53
-.
-
-$deps{'MAILGATE'} = [ _( << '.') ];
-HTML::TreeBuilder
-HTML::FormatText
-Getopt::Long
-LWP::UserAgent
-.
-
-$deps{'CLI'} = [ _( << '.') ];
-Getopt::Long 2.24
-.
-
-$deps{'DEV'} = [ _( << '.') ];
-Regexp::Common
-Time::HiRes 
-Test::Inline 
-WWW::Mechanize
-.
-
-$deps{'FASTCGI'} = [ _( << '.') ];
-CGI 2.92
-FCGI
-CGI::Fast 
-.
-
-$deps{'SPEEDYCGI'} = [ _( << '.') ];
-CGI 2.92
-CGI::SpeedyCGI
-.
-
-
-$deps{'MODPERL1'} = [ _( << '.') ];
-CGI 2.92
-Apache::Request
-Apache::DBI 0.92
-.
-
-$deps{'MODPERL2'} = [ _( << '.') ];
-CGI 2.92
-Apache::DBI
-.
-
-$deps{'I18N-COMPAT'} = [ _( << '.') ];
-Text::Iconv
-Encode::compat 0.04
-.
-
-$deps{'MYSQL'} = [ _( << '.') ];
-DBD::mysql 2.1018
-.
-$deps{'ORACLE'} = [ _( << '.') ];
-DBD::Oracle
-.
-$deps{'POSTGRESQL'} = [ _( << '.') ];
-DBD::Pg
-.
-
-print "perl:\n";
-print "\t5.8.0";
-eval {require 5.008};
-if ($@) {
-print "...missing.\n";
-        eval {require 5.006001};
-        if ($@) {
-            print " RT is known to be non-functional on versions of perl older than 5.6.1. Please upgrade to 5.8.0 or newer";
-            die;
-        } else {
-            print " RT is not supported on perl 5.6.1\n";
-        }
-} else {
-        print "...found\n";
-
-}
-
-
-foreach my $type (keys %args)  {
-next unless ($type =~ /^with-(.*?)$/);
-my $type = $1;
-print "$type dependencies:\n";
-       my @deps = (@{$deps{$type}});
-       while (@deps) {
-               my $module = shift @deps;
-               my $version = shift @deps;
-my $ret;
-       $ret =test_dep($module, $version);      
-
-if ($args{'install'} && !$ret) {
-       resolve_dep($module);           
-}
-}
-}
-sub test_dep {
-       my $module = shift;
-       my $version = shift;
-
-       print "\t$module $version";
-       eval "use $module $version" ;
-       if ($@) {
-               my $error = $@;
-               $error =~ s/\n(.*)$//s;
-               print "...MISSING\n";
-                       print "\t\t$error\n" if $error =~ /this is only/;
-
-               return undef;
-       } else {
-               print "...found\n";
-return 1;
-       }
-}
-
-sub resolve_dep {
-       my $module = shift;
-       use CPAN;
-       CPAN::Shell->install($module);          
-       
-}
-
-
-sub print_help {
-    print << "EOF";
-
-$0 FLAG DBTYPE
-
-
-$0 is a tool for RT that will tell you if you've got all
-the modules RT depends on properly installed.
-
-Flags: (only one flag is valid for a given run)
-
--quiet will check to see if we've got everything we need
-       and will exit with a return code of (1) if we don't.
-
--warn will tell you what isn't properly installed
-
--fix will use CPANPLUS.pm or CPAN.pm to magically make everything better
-
-DBTYPE is one of:
-       oracle, pg, mysql
-
-EOF
-
-    exit(0);
-}