[Bps-public-commit] rt1-to-rt3 branch, master, updated. 097af7a38b447cbb524fbc154f06af66d1ba646d

jesse jesse at bestpractical.com
Tue Dec 8 10:17:45 EST 2009


The branch, master has been updated
  discards  8058e7082b14caf0924f99c10ed5d451d72341e7 (commit)
       via  097af7a38b447cbb524fbc154f06af66d1ba646d (commit)
       via  c57501bcd572cb1f4dea1bd1820dbfe3721e2581 (commit)
       via  24a61536da018f03dcd4533416e1f0fdf99cdc22 (commit)
       via  16c8cc8a0b664ec8b576092b292e47729111e742 (commit)
       via  be24968f6199a32eb997f58fd9df680418e05502 (commit)
       via  528ff39d92f29e452e32104a81fd56296e13282a (commit)
       via  38a8747f6e737ec7c4d90ffa339f2a7fef798054 (commit)
       via  c6b997ef322b661b9a698f7171336c77d4fd9f24 (commit)
       via  d1686f845316f9d73c9e1927929477c9f5209962 (commit)
       via  f3d764063300b98cbead123d2992f526e0308524 (commit)
       via  e611ffde4a3e835c4476edab5908b57b71d1ec7a (commit)
       via  aa4707da554bc306f92e6ffacdf11fcc146f0f9e (commit)
       via  9ee5c356c6ddd8e33c2ac317baff26ffcead0bf1 (commit)
       via  be525753d87565c009c7c30e3df73d1c36d162c8 (commit)
       via  2635abc43dbbdd404a3e781281237c7f6cdf0144 (commit)
       via  4ecd1c2b64fb073d8b55eef53dd8a3cdcde11984 (commit)
       via  f5b23e044a0d805661cf8a02e7d77c5b9a6e5875 (commit)
       via  056740f215fdce65599fe8a3b85ca10842c96d54 (commit)
       via  3343c6332b066a6946688f3183aae7f3d95bb563 (commit)
       via  825479e62f4a838323c59faa6c4acaa196d11eed (commit)
       via  0c5583166664913851ad417e88e25fd1e3c12658 (commit)
       via  93dfd7d541addafc907a1f2a9f9cc156894a82a5 (commit)
       via  04eb2149b3a7c9e95fe314d13ff82e41abd20bc7 (commit)
       via  97cedb3978b944eedc45dbbd925dd522f5312026 (commit)
       via  68d29586801d0cca998d332e9dbe03d751714752 (commit)
       via  322d88e62b7cdc03bf70e8cbc63daacae4850154 (commit)
       via  af3028b3791f4e080f1dc36fc610258933adeba8 (commit)
       via  e6ec2a681327ae829b2770da045f55bb6bdcc617 (commit)
       via  45eb2e1e965bb6e551d299cc40faf91d60090879 (commit)
       via  8a6819581ed59a4422ed4471a6cf5c235666dd11 (commit)
       via  bca6dff757b2860f69b0400fb18df54e663656ee (commit)
       via  832d84cc32d81898dd24bfcb93bc3c2c0e28551e (commit)
       via  7f3f49313a6e412a1c4a654045328f9a862e079c (commit)
       via  b9122a636cf3734ba9795f5cf3093007ef1c4d32 (commit)
       via  ed3bfa2567cc0ca711588137365764e35e97470b (commit)
       via  41530a62ae28bb8fb2b3174f0cf6474f4ee34d99 (commit)
       via  d4aa8310e04169688383e207c4487160007ce158 (commit)
       via  3604a8a45effbb57c645691a346aced40b0ae686 (commit)
       via  99f9222a2105300852fb7de9196f8635846e61c0 (commit)
       via  8225a8e448ccb93784a47f18f4bafe34d5b1a358 (commit)
       via  05ec3d799a80b73c778ec4c6b1eee104993b2f96 (commit)
       via  ce322ee23b9ba8b885de075cb0a65e50a3b2f73d (commit)
       via  96bd5331ea7b583d380b72c3f02009d4f3c96d68 (commit)
       via  df5d1fc5731c6464f80d9d1f205c41fea2a678d3 (commit)
       via  7bed09f1c202241a8fd1b2d356986058af980a40 (commit)
       via  63f754b1fe4bd84fd1e594a7260fedd94568029f (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (8058e7082b14caf0924f99c10ed5d451d72341e7)
            \
             N -- N -- N (097af7a38b447cbb524fbc154f06af66d1ba646d)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

Summary of changes:
 Changes                                  |    7 +
 MANIFEST                                 |   23 +
 META.yml                                 |   17 +
 Makefile.PL                              |   12 +
 README                                   |   23 +
 bin/rt1-to-rt3                           |   85 +++
 inc/Module/Install.pm                    |  281 ++++++++++
 inc/Module/Install/Base.pm               |   70 +++
 inc/Module/Install/Can.pm                |   82 +++
 inc/Module/Install/Fetch.pm              |   93 ++++
 inc/Module/Install/Makefile.pm           |  208 +++++++
 inc/Module/Install/Metadata.pm           |  315 +++++++++++
 inc/Module/Install/Win32.pm              |   65 +++
 inc/Module/Install/WriteAll.pm           |   43 ++
 lib/RT/Extension/Converter.pm            |  103 ++++
 lib/RT/Extension/Converter/Config.pm     |   78 +++
 lib/RT/Extension/Converter/RT1.pm        |  378 +++++++++++++
 lib/RT/Extension/Converter/RT1/Config.pm |  116 ++++
 lib/RT/Extension/Converter/RT3.pm        |  870 ++++++++++++++++++++++++++++++
 lib/RT/Extension/Converter/RT3/Config.pm |   90 +++
 t/00.load.t                              |   12 +
 t/99.pod-coverage.t                      |    6 +
 t/99.pod.t                               |    6 +
 t/basic.t                                |   20 +
 24 files changed, 3003 insertions(+), 0 deletions(-)
 delete mode 100644 .gitignore
 create mode 100644 Changes
 create mode 100644 MANIFEST
 create mode 100644 META.yml
 create mode 100644 Makefile.PL
 create mode 100644 README
 create mode 100644 bin/rt1-to-rt3
 create mode 100644 inc/Module/Install.pm
 create mode 100644 inc/Module/Install/Base.pm
 create mode 100644 inc/Module/Install/Can.pm
 create mode 100644 inc/Module/Install/Fetch.pm
 create mode 100644 inc/Module/Install/Makefile.pm
 create mode 100644 inc/Module/Install/Metadata.pm
 create mode 100644 inc/Module/Install/Win32.pm
 create mode 100644 inc/Module/Install/WriteAll.pm
 create mode 100644 lib/RT/Extension/Converter.pm
 create mode 100644 lib/RT/Extension/Converter/Config.pm
 create mode 100644 lib/RT/Extension/Converter/RT1.pm
 create mode 100644 lib/RT/Extension/Converter/RT1/Config.pm
 create mode 100644 lib/RT/Extension/Converter/RT3.pm
 create mode 100644 lib/RT/Extension/Converter/RT3/Config.pm
 create mode 100644 t/00.load.t
 create mode 100644 t/99.pod-coverage.t
 create mode 100644 t/99.pod.t
 create mode 100644 t/basic.t

- Log -----------------------------------------------------------------
commit 63f754b1fe4bd84fd1e594a7260fedd94568029f
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Fri Jan 12 21:17:09 2007 +0000

    new converter from rt1 to rt3.6

commit 7bed09f1c202241a8fd1b2d356986058af980a40
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:24:37 2007 +0000

    framework + user migration

diff --git a/Changes b/Changes
new file mode 100644
index 0000000..494c720
--- /dev/null
+++ b/Changes
@@ -0,0 +1,5 @@
+Revision history for RTx-Converter
+
+0.01  Thu Jan 18 13:09:25 2007
+       Initial release.
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..08b6c5a
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,10 @@
+Changes
+MANIFEST
+META.yml # Will be created by "make dist"
+Makefile.PL
+README
+lib/RTx/Converter.pm
+lib/RTx/Converter/RT1.pm
+t/00.load.t
+t/99.pod-coverage.t
+t/99.pod.t
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..2d4a6f4
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,15 @@
+abstract: Convert RT1 installations to RT3
+author: Kevin Falcone  C<< <falcone at bestpractical.com> >>
+distribution_type: module
+generated_by: Module::Install version 0.64
+license: perl
+name: RTx-Converter
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Class::Accessor::Fast: 0
+  RT: 3.6
+  Test::More: 0
+version: 0.01
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..f691fa9
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,10 @@
+use inc::Module::Install;
+
+name ('RTx-Converter');
+abstract('Convert RT1 installations to RT3');
+all_from('lib/RTx/Converter.pm');
+requires('Test::More');
+requires('Class::Accessor::Fast');
+requires('RT' => '3.6');
+
+&WriteAll;
diff --git a/README b/README
new file mode 100644
index 0000000..d37f311
--- /dev/null
+++ b/README
@@ -0,0 +1,40 @@
+RTx-Converter version 0.0.1
+
+[ REPLACE THIS...
+
+  The README is used to introduce the module and provide instructions on
+  how to install the module, any machine dependencies it may have (for
+  example C compilers and installed libraries) and any other information
+  that should be understood before the module is installed.
+
+  A README file is required for CPAN modules since CPAN extracts the
+  README file from a module distribution so that people browsing the
+  archive can use it get an idea of the modules uses. It is usually a
+  good idea to provide version information here so that people can
+  decide whether fixes for the module are worth downloading.
+]
+
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+    perl Makefile.PL
+    make
+    make test
+    make install
+
+
+
+DEPENDENCIES
+
+None.
+
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2007, Best Practical Solutions LLC.
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
new file mode 100644
index 0000000..8458498
--- /dev/null
+++ b/bin/rt1-to-rt3
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+# this may need to change depending on your installation
+#use lib qw(/opt/rt3/lib);
+use lib qw(lib); # assumes you run this uninstalled
+use lib qw(/Users/falcone/svk/rt/harvard/rt-3.6.3/lib);
+
+use Getopt::Long;
+
+use RTx::Converter;
+
+my $rt1 = RTx::Converter->new( type => 'RT1' );
+my $rt3 = RTx::Converter->new( type => 'RT3' );
+
+# should probably read a config file here
+$rt1->config->dbpassword('');
+$rt1->config->database('harvardrt1');
+use Data::Dumper;
+$rt1->config->debug(1);
+$rt3->config->debug(1);
+
+while (my $user = $rt1->get_user) {
+    my $user_obj = $rt3->create_user(%$user);
+}
diff --git a/inc/Module/Install.pm b/inc/Module/Install.pm
new file mode 100644
index 0000000..0330b0e
--- /dev/null
+++ b/inc/Module/Install.pm
@@ -0,0 +1,281 @@
+#line 1
+package Module::Install;
+
+# For any maintainers:
+# The load order for Module::Install is a bit magic.
+# It goes something like this...
+#
+# IF ( host has Module::Install installed, creating author mode ) {
+#     1. Makefile.PL calls "use inc::Module::Install"
+#     2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
+#     3. The installed version of inc::Module::Install loads
+#     4. inc::Module::Install calls "require Module::Install"
+#     5. The ./inc/ version of Module::Install loads
+# } ELSE {
+#     1. Makefile.PL calls "use inc::Module::Install"
+#     2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
+#     3. The ./inc/ version of Module::Install loads
+# }
+
+use 5.004;
+use strict 'vars';
+
+use vars qw{$VERSION};
+BEGIN {
+    # All Module::Install core packages now require synchronised versions.
+    # This will be used to ensure we don't accidentally load old or
+    # different versions of modules.
+    # This is not enforced yet, but will be some time in the next few
+    # releases once we can make sure it won't clash with custom
+    # Module::Install extensions.
+    $VERSION = '0.64';
+}
+
+# Whether or not inc::Module::Install is actually loaded, the
+# $INC{inc/Module/Install.pm} is what will still get set as long as
+# the caller loaded module this in the documented manner.
+# If not set, the caller may NOT have loaded the bundled version, and thus
+# they may not have a MI version that works with the Makefile.PL. This would
+# result in false errors or unexpected behaviour. And we don't want that.
+my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
+unless ( $INC{$file} ) {
+    die <<"END_DIE";
+Please invoke ${\__PACKAGE__} with:
+
+    use inc::${\__PACKAGE__};
+
+not:
+
+    use ${\__PACKAGE__};
+
+END_DIE
+}
+
+# If the script that is loading Module::Install is from the future,
+# then make will detect this and cause it to re-run over and over
+# again. This is bad. Rather than taking action to touch it (which
+# is unreliable on some platforms and requires write permissions)
+# for now we should catch this and refuse to run.
+if ( -f $0 and (stat($0))[9] > time ) {
+	die << "END_DIE";
+Your installer $0 has a modification time in the future.
+
+This is known to create infinite loops in make.
+
+Please correct this, then run $0 again.
+
+END_DIE
+}
+
+use Cwd        ();
+use File::Find ();
+use File::Path ();
+use FindBin;
+
+*inc::Module::Install::VERSION = *VERSION;
+ at inc::Module::Install::ISA     = __PACKAGE__;
+
+sub autoload {
+    my $self = shift;
+    my $who  = $self->_caller;
+    my $cwd  = Cwd::cwd();
+    my $sym  = "${who}::AUTOLOAD";
+    $sym->{$cwd} = sub {
+        my $pwd = Cwd::cwd();
+        if ( my $code = $sym->{$pwd} ) {
+            # delegate back to parent dirs
+            goto &$code unless $cwd eq $pwd;
+        }
+        $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+        unshift @_, ($self, $1);
+        goto &{$self->can('call')} unless uc($1) eq $1;
+    };
+}
+
+sub import {
+    my $class = shift;
+    my $self  = $class->new(@_);
+    my $who   = $self->_caller;
+
+    unless ( -f $self->{file} ) {
+        require "$self->{path}/$self->{dispatch}.pm";
+        File::Path::mkpath("$self->{prefix}/$self->{author}");
+        $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
+        $self->{admin}->init;
+        @_ = ($class, _self => $self);
+        goto &{"$self->{name}::import"};
+    }
+
+    *{"${who}::AUTOLOAD"} = $self->autoload;
+    $self->preload;
+
+    # Unregister loader and worker packages so subdirs can use them again
+    delete $INC{"$self->{file}"};
+    delete $INC{"$self->{path}.pm"};
+}
+
+sub preload {
+    my ($self) = @_;
+
+    unless ( $self->{extensions} ) {
+        $self->load_extensions(
+            "$self->{prefix}/$self->{path}", $self
+        );
+    }
+
+    my @exts = @{$self->{extensions}};
+    unless ( @exts ) {
+        my $admin = $self->{admin};
+        @exts = $admin->load_all_extensions;
+    }
+
+    my %seen;
+    foreach my $obj ( @exts ) {
+        while (my ($method, $glob) = each %{ref($obj) . '::'}) {
+            next unless $obj->can($method);
+            next if $method =~ /^_/;
+            next if $method eq uc($method);
+            $seen{$method}++;
+        }
+    }
+
+    my $who = $self->_caller;
+    foreach my $name ( sort keys %seen ) {
+        *{"${who}::$name"} = sub {
+            ${"${who}::AUTOLOAD"} = "${who}::$name";
+            goto &{"${who}::AUTOLOAD"};
+        };
+    }
+}
+
+sub new {
+    my ($class, %args) = @_;
+
+    # ignore the prefix on extension modules built from top level.
+    my $base_path = Cwd::abs_path($FindBin::Bin);
+    unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
+        delete $args{prefix};
+    }
+
+    return $args{_self} if $args{_self};
+
+    $args{dispatch} ||= 'Admin';
+    $args{prefix}   ||= 'inc';
+    $args{author}   ||= ($^O eq 'VMS' ? '_author' : '.author');
+    $args{bundle}   ||= 'inc/BUNDLES';
+    $args{base}     ||= $base_path;
+    $class =~ s/^\Q$args{prefix}\E:://;
+    $args{name}     ||= $class;
+    $args{version}  ||= $class->VERSION;
+    unless ( $args{path} ) {
+        $args{path}  = $args{name};
+        $args{path}  =~ s!::!/!g;
+    }
+    $args{file}     ||= "$args{base}/$args{prefix}/$args{path}.pm";
+
+    bless( \%args, $class );
+}
+
+sub call {
+	my ($self, $method) = @_;
+	my $obj = $self->load($method) or return;
+        splice(@_, 0, 2, $obj);
+	goto &{$obj->can($method)};
+}
+
+sub load {
+    my ($self, $method) = @_;
+
+    $self->load_extensions(
+        "$self->{prefix}/$self->{path}", $self
+    ) unless $self->{extensions};
+
+    foreach my $obj (@{$self->{extensions}}) {
+        return $obj if $obj->can($method);
+    }
+
+    my $admin = $self->{admin} or die <<"END_DIE";
+The '$method' method does not exist in the '$self->{prefix}' path!
+Please remove the '$self->{prefix}' directory and run $0 again to load it.
+END_DIE
+
+    my $obj = $admin->load($method, 1);
+    push @{$self->{extensions}}, $obj;
+
+    $obj;
+}
+
+sub load_extensions {
+    my ($self, $path, $top) = @_;
+
+    unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
+        unshift @INC, $self->{prefix};
+    }
+
+    foreach my $rv ( $self->find_extensions($path) ) {
+        my ($file, $pkg) = @{$rv};
+        next if $self->{pathnames}{$pkg};
+
+        local $@;
+        my $new = eval { require $file; $pkg->can('new') };
+        unless ( $new ) {
+            warn $@ if $@;
+            next;
+        }
+        $self->{pathnames}{$pkg} = delete $INC{$file};
+        push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
+    }
+
+    $self->{extensions} ||= [];
+}
+
+sub find_extensions {
+    my ($self, $path) = @_;
+
+    my @found;
+    File::Find::find( sub {
+        my $file = $File::Find::name;
+        return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
+        my $subpath = $1;
+        return if lc($subpath) eq lc($self->{dispatch});
+
+        $file = "$self->{path}/$subpath.pm";
+        my $pkg = "$self->{name}::$subpath";
+        $pkg =~ s!/!::!g;
+
+        # If we have a mixed-case package name, assume case has been preserved
+        # correctly.  Otherwise, root through the file to locate the case-preserved
+        # version of the package name.
+        if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
+            open PKGFILE, "<$subpath.pm" or die "find_extensions: Can't open $subpath.pm: $!";
+            my $in_pod = 0;
+            while ( <PKGFILE> ) {
+                $in_pod = 1 if /^=\w/;
+                $in_pod = 0 if /^=cut/;
+                next if ($in_pod || /^=cut/);  # skip pod text
+                next if /^\s*#/;               # and comments
+                if ( m/^\s*package\s+($pkg)\s*;/i ) {
+                    $pkg = $1;
+                    last;
+                }
+            }
+            close PKGFILE;
+        }
+
+        push @found, [ $file, $pkg ];
+    }, $path ) if -d $path;
+
+    @found;
+}
+
+sub _caller {
+    my $depth = 0;
+    my $call  = caller($depth);
+    while ( $call eq __PACKAGE__ ) {
+        $depth++;
+        $call = caller($depth);
+    }
+    return $call;
+}
+
+1;
diff --git a/inc/Module/Install/Base.pm b/inc/Module/Install/Base.pm
new file mode 100644
index 0000000..30a24ca
--- /dev/null
+++ b/inc/Module/Install/Base.pm
@@ -0,0 +1,70 @@
+#line 1
+package Module::Install::Base;
+
+$VERSION = '0.64';
+
+# Suspend handler for "redefined" warnings
+BEGIN {
+	my $w = $SIG{__WARN__};
+	$SIG{__WARN__} = sub { $w };
+}
+
+### This is the ONLY module that shouldn't have strict on
+# use strict;
+
+#line 41
+
+sub new {
+    my ($class, %args) = @_;
+
+    foreach my $method ( qw(call load) ) {
+        *{"$class\::$method"} = sub {
+            shift()->_top->$method(@_);
+        } unless defined &{"$class\::$method"};
+    }
+
+    bless( \%args, $class );
+}
+
+#line 61
+
+sub AUTOLOAD {
+    my $self = shift;
+    local $@;
+    my $autoload = eval { $self->_top->autoload } or return;
+    goto &$autoload;
+}
+
+#line 76
+
+sub _top { $_[0]->{_top} }
+
+#line 89
+
+sub admin {
+    $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new;
+}
+
+sub is_admin {
+    $_[0]->admin->VERSION;
+}
+
+sub DESTROY {}
+
+package Module::Install::Base::FakeAdmin;
+
+my $Fake;
+sub new { $Fake ||= bless(\@_, $_[0]) }
+
+sub AUTOLOAD {}
+
+sub DESTROY {}
+
+# Restore warning handler
+BEGIN {
+	$SIG{__WARN__} = $SIG{__WARN__}->();
+}
+
+1;
+
+#line 138
diff --git a/inc/Module/Install/Can.pm b/inc/Module/Install/Can.pm
new file mode 100644
index 0000000..1c01e22
--- /dev/null
+++ b/inc/Module/Install/Can.pm
@@ -0,0 +1,82 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Module::Install::Base;
+use Config ();
+### This adds a 5.005 Perl version dependency.
+### This is a bug and will be fixed.
+use File::Spec ();
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.64';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+	my ($self, $mod, $ver) = @_;
+	$mod =~ s{::|\\}{/}g;
+	$mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+	my $pkg = $mod;
+	$pkg =~ s{/}{::}g;
+	$pkg =~ s{\.pm$}{}i;
+
+	local $@;
+	eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# check if we can run some command
+sub can_run {
+	my ($self, $cmd) = @_;
+
+	my $_cmd = $cmd;
+	return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+	for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+		my $abs = File::Spec->catfile($dir, $_[1]);
+		return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+	}
+
+	return;
+}
+
+# can we locate a (the) C compiler
+sub can_cc {
+	my $self   = shift;
+	my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+	# $Config{cc} may contain args; try to find out the program part
+	while (@chunks) {
+		return $self->can_run("@chunks") || (pop(@chunks), next);
+	}
+
+	return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+	require ExtUtils::MM_Cygwin;
+	require ExtUtils::MM_Win32;
+	if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+		*ExtUtils::MM_Cygwin::maybe_command = sub {
+			my ($self, $file) = @_;
+			if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+				ExtUtils::MM_Win32->maybe_command($file);
+			} else {
+				ExtUtils::MM_Unix->maybe_command($file);
+			}
+		}
+	}
+}
+
+1;
+
+__END__
+
+#line 157
diff --git a/inc/Module/Install/Fetch.pm b/inc/Module/Install/Fetch.pm
new file mode 100644
index 0000000..24c0c02
--- /dev/null
+++ b/inc/Module/Install/Fetch.pm
@@ -0,0 +1,93 @@
+#line 1
+package Module::Install::Fetch;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.64';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub get_file {
+    my ($self, %args) = @_;
+    my ($scheme, $host, $path, $file) = 
+        $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+
+    if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
+        $args{url} = $args{ftp_url}
+            or (warn("LWP support unavailable!\n"), return);
+        ($scheme, $host, $path, $file) = 
+            $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+    }
+
+    $|++;
+    print "Fetching '$file' from $host... ";
+
+    unless (eval { require Socket; Socket::inet_aton($host) }) {
+        warn "'$host' resolve failed!\n";
+        return;
+    }
+
+    return unless $scheme eq 'ftp' or $scheme eq 'http';
+
+    require Cwd;
+    my $dir = Cwd::getcwd();
+    chdir $args{local_dir} or return if exists $args{local_dir};
+
+    if (eval { require LWP::Simple; 1 }) {
+        LWP::Simple::mirror($args{url}, $file);
+    }
+    elsif (eval { require Net::FTP; 1 }) { eval {
+        # use Net::FTP to get past firewall
+        my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
+        $ftp->login("anonymous", 'anonymous at example.com');
+        $ftp->cwd($path);
+        $ftp->binary;
+        $ftp->get($file) or (warn("$!\n"), return);
+        $ftp->quit;
+    } }
+    elsif (my $ftp = $self->can_run('ftp')) { eval {
+        # no Net::FTP, fallback to ftp.exe
+        require FileHandle;
+        my $fh = FileHandle->new;
+
+        local $SIG{CHLD} = 'IGNORE';
+        unless ($fh->open("|$ftp -n")) {
+            warn "Couldn't open ftp: $!\n";
+            chdir $dir; return;
+        }
+
+        my @dialog = split(/\n/, <<"END_FTP");
+open $host
+user anonymous anonymous\@example.com
+cd $path
+binary
+get $file $file
+quit
+END_FTP
+        foreach (@dialog) { $fh->print("$_\n") }
+        $fh->close;
+    } }
+    else {
+        warn "No working 'ftp' program available!\n";
+        chdir $dir; return;
+    }
+
+    unless (-f $file) {
+        warn "Fetching failed: $@\n";
+        chdir $dir; return;
+    }
+
+    return if exists $args{size} and -s $file != $args{size};
+    system($args{run}) if exists $args{run};
+    unlink($file) if $args{remove};
+
+    print(((!exists $args{check_for} or -e $args{check_for})
+        ? "done!" : "failed! ($!)"), "\n");
+    chdir $dir; return !$?;
+}
+
+1;
diff --git a/inc/Module/Install/Makefile.pm b/inc/Module/Install/Makefile.pm
new file mode 100644
index 0000000..96c7e17
--- /dev/null
+++ b/inc/Module/Install/Makefile.pm
@@ -0,0 +1,208 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.64';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+    shift;
+
+    # Infinite loop protection
+    my @c = caller();
+    if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+        die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+    }
+
+    # In automated testing, always use defaults
+    if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+        local $ENV{PERL_MM_USE_DEFAULT} = 1;
+        goto &ExtUtils::MakeMaker::prompt;
+    } else {
+        goto &ExtUtils::MakeMaker::prompt;
+    }
+}
+
+sub makemaker_args {
+    my $self = shift;
+    my $args = ($self->{makemaker_args} ||= {});
+    %$args = ( %$args, @_ ) if @_;
+    $args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+    my $self = shift;
+    my $name = shift;
+    my $args = $self->makemaker_args;
+    $args->{name} = defined $args->{$name}
+    	? join( ' ', $args->{name}, @_ )
+    	: join( ' ', @_ );
+}
+
+sub build_subdirs {
+    my $self    = shift;
+    my $subdirs = $self->makemaker_args->{DIR} ||= [];
+    for my $subdir (@_) {
+        push @$subdirs, $subdir;
+    }
+}
+
+sub clean_files {
+    my $self  = shift;
+    my $clean = $self->makemaker_args->{clean} ||= {};
+    %$clean = (
+        %$clean, 
+        FILES => join(' ', grep length, $clean->{FILES}, @_),
+    );
+}
+
+sub realclean_files {
+    my $self  = shift;
+    my $realclean = $self->makemaker_args->{realclean} ||= {};
+    %$realclean = (
+        %$realclean, 
+        FILES => join(' ', grep length, $realclean->{FILES}, @_),
+    );
+}
+
+sub libs {
+    my $self = shift;
+    my $libs = ref $_[0] ? shift : [ shift ];
+    $self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+    my $self = shift;
+    $self->makemaker_args( INC => shift );
+}
+
+sub write {
+    my $self = shift;
+    die "&Makefile->write() takes no arguments\n" if @_;
+
+    my $args = $self->makemaker_args;
+    $args->{DISTNAME} = $self->name;
+    $args->{NAME}     = $self->module_name || $self->name || $self->determine_NAME($args);
+    $args->{VERSION}  = $self->version || $self->determine_VERSION($args);
+    $args->{NAME}     =~ s/-/::/g;
+    if ( $self->tests ) {
+        $args->{test} = { TESTS => $self->tests };
+    }
+    if ($] >= 5.005) {
+        $args->{ABSTRACT} = $self->abstract;
+        $args->{AUTHOR}   = $self->author;
+    }
+    if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+        $args->{NO_META} = 1;
+    }
+    if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+        $args->{SIGN} = 1;
+    }
+    unless ( $self->is_admin ) {
+        delete $args->{SIGN};
+    }
+
+    # merge both kinds of requires into prereq_pm
+    my $prereq = ($args->{PREREQ_PM} ||= {});
+    %$prereq = ( %$prereq, map { @$_ } map { @$_ } grep $_,
+                 ($self->build_requires, $self->requires) );
+
+    # merge both kinds of requires into prereq_pm
+    my $subdirs = ($args->{DIR} ||= []);
+    if ($self->bundles) {
+        foreach my $bundle (@{ $self->bundles }) {
+            my ($file, $dir) = @$bundle;
+            push @$subdirs, $dir if -d $dir;
+            delete $prereq->{$file};
+        }
+    }
+
+    if ( my $perl_version = $self->perl_version ) {
+        eval "use $perl_version; 1"
+            or die "ERROR: perl: Version $] is installed, "
+                . "but we need version >= $perl_version";
+    }
+
+    my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+    if ($self->admin->preop) {
+        $args{dist} = $self->admin->preop;
+    }
+
+    my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+    $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+    my $self          = shift;
+    my $makefile_name = shift;
+    my $top_class     = ref($self->_top) || '';
+    my $top_version   = $self->_top->VERSION || '';
+
+    my $preamble = $self->preamble 
+        ? "# Preamble by $top_class $top_version\n"
+            . $self->preamble
+        : '';
+    my $postamble = "# Postamble by $top_class $top_version\n"
+        . ($self->postamble || '');
+
+    local *MAKEFILE;
+    open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+    my $makefile = do { local $/; <MAKEFILE> };
+    close MAKEFILE or die $!;
+
+    $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+    $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+    $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+    $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+    $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+    # Module::Install will never be used to build the Core Perl
+    # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+    # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+    $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+    #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+    # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+    $makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
+
+    # XXX - This is currently unused; not sure if it breaks other MM-users
+    # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+    open  MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+    print MAKEFILE  "$preamble$makefile$postamble" or die $!;
+    close MAKEFILE  or die $!;
+
+    1;
+}
+
+sub preamble {
+    my ($self, $text) = @_;
+    $self->{preamble} = $text . $self->{preamble} if defined $text;
+    $self->{preamble};
+}
+
+sub postamble {
+    my ($self, $text) = @_;
+    $self->{postamble} ||= $self->admin->postamble;
+    $self->{postamble} .= $text if defined $text;
+    $self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 334
diff --git a/inc/Module/Install/Metadata.pm b/inc/Module/Install/Metadata.pm
new file mode 100644
index 0000000..6c80832
--- /dev/null
+++ b/inc/Module/Install/Metadata.pm
@@ -0,0 +1,315 @@
+#line 1
+package Module::Install::Metadata;
+
+use strict 'vars';
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.64';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+my @scalar_keys = qw{
+    name module_name abstract author version license
+    distribution_type perl_version tests
+};
+
+my @tuple_keys = qw{
+    build_requires requires recommends bundles
+};
+
+sub Meta            { shift        }
+sub Meta_ScalarKeys { @scalar_keys }
+sub Meta_TupleKeys  { @tuple_keys  }
+
+foreach my $key (@scalar_keys) {
+    *$key = sub {
+        my $self = shift;
+        return $self->{values}{$key} if defined wantarray and !@_;
+        $self->{values}{$key} = shift;
+        return $self;
+    };
+}
+
+foreach my $key (@tuple_keys) {
+    *$key = sub {
+        my $self = shift;
+        return $self->{values}{$key} unless @_;
+
+        my @rv;
+        while (@_) {
+            my $module = shift or last;
+            my $version = shift || 0;
+            if ( $module eq 'perl' ) {
+                $version =~ s{^(\d+)\.(\d+)\.(\d+)}
+                             {$1 + $2/1_000 + $3/1_000_000}e;
+                $self->perl_version($version);
+                next;
+            }
+            my $rv = [ $module, $version ];
+            push @rv, $rv;
+        }
+        push @{ $self->{values}{$key} }, @rv;
+        @rv;
+    };
+}
+
+sub sign {
+    my $self = shift;
+    return $self->{'values'}{'sign'} if defined wantarray and !@_;
+    $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
+    return $self;
+}
+
+sub dynamic_config {
+	my $self = shift;
+	unless ( @_ ) {
+		warn "You MUST provide an explicit true/false value to dynamic_config, skipping\n";
+		return $self;
+	}
+	$self->{'values'}{'dynamic_config'} = $_[0] ? 1 : 0;
+	return $self;
+}
+
+sub all_from {
+    my ( $self, $file ) = @_;
+
+    unless ( defined($file) ) {
+        my $name = $self->name
+            or die "all_from called with no args without setting name() first";
+        $file = join('/', 'lib', split(/-/, $name)) . '.pm';
+        $file =~ s{.*/}{} unless -e $file;
+        die "all_from: cannot find $file from $name" unless -e $file;
+    }
+
+    $self->version_from($file)      unless $self->version;
+    $self->perl_version_from($file) unless $self->perl_version;
+
+    # The remaining probes read from POD sections; if the file
+    # has an accompanying .pod, use that instead
+    my $pod = $file;
+    if ( $pod =~ s/\.pm$/.pod/i and -e $pod ) {
+        $file = $pod;
+    }
+
+    $self->author_from($file)   unless $self->author;
+    $self->license_from($file)  unless $self->license;
+    $self->abstract_from($file) unless $self->abstract;
+}
+
+sub provides {
+    my $self     = shift;
+    my $provides = ( $self->{values}{provides} ||= {} );
+    %$provides = (%$provides, @_) if @_;
+    return $provides;
+}
+
+sub auto_provides {
+    my $self = shift;
+    return $self unless $self->is_admin;
+
+    unless (-e 'MANIFEST') {
+        warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
+        return $self;
+    }
+
+    # Avoid spurious warnings as we are not checking manifest here.
+
+    local $SIG{__WARN__} = sub {1};
+    require ExtUtils::Manifest;
+    local *ExtUtils::Manifest::manicheck = sub { return };
+
+    require Module::Build;
+    my $build = Module::Build->new(
+        dist_name    => $self->name,
+        dist_version => $self->version,
+        license      => $self->license,
+    );
+    $self->provides(%{ $build->find_dist_packages || {} });
+}
+
+sub feature {
+    my $self     = shift;
+    my $name     = shift;
+    my $features = ( $self->{values}{features} ||= [] );
+
+    my $mods;
+
+    if ( @_ == 1 and ref( $_[0] ) ) {
+        # The user used ->feature like ->features by passing in the second
+        # argument as a reference.  Accomodate for that.
+        $mods = $_[0];
+    } else {
+        $mods = \@_;
+    }
+
+    my $count = 0;
+    push @$features, (
+        $name => [
+            map {
+                ref($_) ? ( ref($_) eq 'HASH' ) ? %$_
+                                                : @$_
+                        : $_
+            } @$mods
+        ]
+    );
+
+    return @$features;
+}
+
+sub features {
+    my $self = shift;
+    while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
+        $self->feature( $name, @$mods );
+    }
+    return $self->{values}->{features}
+    	? @{ $self->{values}->{features} }
+    	: ();
+}
+
+sub no_index {
+    my $self = shift;
+    my $type = shift;
+    push @{ $self->{values}{no_index}{$type} }, @_ if $type;
+    return $self->{values}{no_index};
+}
+
+sub read {
+    my $self = shift;
+    $self->include_deps( 'YAML', 0 );
+
+    require YAML;
+    my $data = YAML::LoadFile('META.yml');
+
+    # Call methods explicitly in case user has already set some values.
+    while ( my ( $key, $value ) = each %$data ) {
+        next unless $self->can($key);
+        if ( ref $value eq 'HASH' ) {
+            while ( my ( $module, $version ) = each %$value ) {
+                $self->can($key)->($self, $module => $version );
+            }
+        }
+        else {
+            $self->can($key)->($self, $value);
+        }
+    }
+    return $self;
+}
+
+sub write {
+    my $self = shift;
+    return $self unless $self->is_admin;
+    $self->admin->write_meta;
+    return $self;
+}
+
+sub version_from {
+    my ( $self, $file ) = @_;
+    require ExtUtils::MM_Unix;
+    $self->version( ExtUtils::MM_Unix->parse_version($file) );
+}
+
+sub abstract_from {
+    my ( $self, $file ) = @_;
+    require ExtUtils::MM_Unix;
+    $self->abstract(
+        bless(
+            { DISTNAME => $self->name },
+            'ExtUtils::MM_Unix'
+        )->parse_abstract($file)
+     );
+}
+
+sub _slurp {
+    my ( $self, $file ) = @_;
+
+    local *FH;
+    open FH, "< $file" or die "Cannot open $file.pod: $!";
+    do { local $/; <FH> };
+}
+
+sub perl_version_from {
+    my ( $self, $file ) = @_;
+
+    if (
+        $self->_slurp($file) =~ m/
+        ^
+        use \s*
+        v?
+        ([\d_\.]+)
+        \s* ;
+    /ixms
+      )
+    {
+        my $v = $1;
+        $v =~ s{_}{}g;
+        $self->perl_version($1);
+    }
+    else {
+        warn "Cannot determine perl version info from $file\n";
+        return;
+    }
+}
+
+sub author_from {
+    my ( $self, $file ) = @_;
+    my $content = $self->_slurp($file);
+    if ($content =~ m/
+        =head \d \s+ (?:authors?)\b \s*
+        ([^\n]*)
+        |
+        =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
+        .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
+        ([^\n]*)
+    /ixms) {
+        my $author = $1 || $2;
+        $author =~ s{E<lt>}{<}g;
+        $author =~ s{E<gt>}{>}g;
+        $self->author($author); 
+    }
+    else {
+        warn "Cannot determine author info from $file\n";
+    }
+}
+
+sub license_from {
+    my ( $self, $file ) = @_;
+
+    if (
+        $self->_slurp($file) =~ m/
+        =head \d \s+
+        (?:licen[cs]e|licensing|copyright|legal)\b
+        (.*?)
+        (=head\\d.*|=cut.*|)
+        \z
+    /ixms
+      )
+    {
+        my $license_text = $1;
+        my @phrases      = (
+            'under the same (?:terms|license) as perl itself' => 'perl',
+            'GNU public license'                              => 'gpl',
+            'GNU lesser public license'                       => 'gpl',
+            'BSD license'                                     => 'bsd',
+            'Artistic license'                                => 'artistic',
+            'GPL'                                             => 'gpl',
+            'LGPL'                                            => 'lgpl',
+            'BSD'                                             => 'bsd',
+            'Artistic'                                        => 'artistic',
+        );
+        while ( my ( $pattern, $license ) = splice( @phrases, 0, 2 ) ) {
+            $pattern =~ s{\s+}{\\s+}g;
+            if ( $license_text =~ /\b$pattern\b/i ) {
+                $self->license($license);
+                return 1;
+            }
+        }
+    }
+
+    warn "Cannot determine license info from $file\n";
+    return 'unknown';
+}
+
+1;
diff --git a/inc/Module/Install/Win32.pm b/inc/Module/Install/Win32.pm
new file mode 100644
index 0000000..2ec7d66
--- /dev/null
+++ b/inc/Module/Install/Win32.pm
@@ -0,0 +1,65 @@
+#line 1
+package Module::Install::Win32;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.64';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# determine if the user needs nmake, and download it if needed
+sub check_nmake {
+	my $self = shift;
+	$self->load('can_run');
+	$self->load('get_file');
+	
+	require Config;
+	return unless (
+		$^O eq 'MSWin32'                     and
+		$Config::Config{make}                and
+		$Config::Config{make} =~ /^nmake\b/i and
+		! $self->can_run('nmake')
+	);
+
+	print "The required 'nmake' executable not found, fetching it...\n";
+
+	require File::Basename;
+	my $rv = $self->get_file(
+		url       => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
+		ftp_url   => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
+		local_dir => File::Basename::dirname($^X),
+		size      => 51928,
+		run       => 'Nmake15.exe /o > nul',
+		check_for => 'Nmake.exe',
+		remove    => 1,
+	);
+
+	if (!$rv) {
+        die <<'END_MESSAGE';
+
+-------------------------------------------------------------------------------
+
+Since you are using Microsoft Windows, you will need the 'nmake' utility
+before installation. It's available at:
+
+  http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+      or
+  ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
+
+Please download the file manually, save it to a directory in %PATH% (e.g.
+C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
+that directory, and run "Nmake15.exe" from there; that will create the
+'nmake.exe' file needed by this module.
+
+You may then resume the installation process described in README.
+
+-------------------------------------------------------------------------------
+END_MESSAGE
+	}
+}
+
+1;
diff --git a/inc/Module/Install/WriteAll.pm b/inc/Module/Install/WriteAll.pm
new file mode 100644
index 0000000..3546e61
--- /dev/null
+++ b/inc/Module/Install/WriteAll.pm
@@ -0,0 +1,43 @@
+#line 1
+package Module::Install::WriteAll;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.64';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub WriteAll {
+    my $self = shift;
+    my %args = (
+        meta        => 1,
+        sign        => 0,
+        inline      => 0,
+        check_nmake => 1,
+        @_
+    );
+
+    $self->sign(1)                if $args{sign};
+    $self->Meta->write            if $args{meta};
+    $self->admin->WriteAll(%args) if $self->is_admin;
+
+    if ( $0 =~ /Build.PL$/i ) {
+        $self->Build->write;
+    } else {
+        $self->check_nmake if $args{check_nmake};
+        unless ( $self->makemaker_args->{'PL_FILES'} ) {
+        	$self->makemaker_args( PL_FILES => {} );
+        }
+        if ($args{inline}) {
+            $self->Inline->write;
+        } else {
+            $self->Makefile->write;
+        }
+    }
+}
+
+1;
diff --git a/lib/RTx/Converter.pm b/lib/RTx/Converter.pm
new file mode 100644
index 0000000..ff081e4
--- /dev/null
+++ b/lib/RTx/Converter.pm
@@ -0,0 +1,103 @@
+package RTx::Converter;
+
+our $VERSION = '0.01';
+
+use warnings;
+use strict;
+use Carp;
+
+
+=head1 NAME
+
+RTx::Converter - base class for rtX-to-rt3 scripts
+
+
+=head1 SYNOPSIS
+
+    use RTx::Converter;
+    my $rt1converter = RTx::Converter->new( type => 'rt1' );
+    my $rt3converter = RTx::Converter->new( type => 'rt3' );
+
+    foreach my $user ($rt1converter->users) {
+        $rt3converter->add_user( $user );
+    }
+
+=head1 DESCRIPTION
+
+Top level Converter class, used to get access to the RT(1,2,3) converter
+objects.
+
+=head1 METHODS
+
+=head2 new
+
+Requires a type argument
+
+ new( type => 'RT1' );
+
+=cut
+
+sub new {
+    my $class = shift;
+    my %args = @_;
+
+    Carp::confess "Must pass a type [$args{type}]" unless $args{type};
+
+    my $subclass = "${class}::$args{type}";
+    eval "require $subclass";
+    if ($@) {
+        Carp::confess "Not a valid type $args{type} $subclass $@";
+    }
+
+    return $subclass->new;
+}
+
+
+=head1 BUGS AND LIMITATIONS
+
+No bugs have been reported.
+
+Please report any bugs or feature requests to
+C<bug-rtx-converter at rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org> or on C<rt-devel at lists.bestpractical.com>
+
+
+=head1 AUTHOR
+
+Kevin Falcone  C<< <falcone at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;
diff --git a/lib/RTx/Converter/Config.pm b/lib/RTx/Converter/Config.pm
new file mode 100644
index 0000000..de6e9ff
--- /dev/null
+++ b/lib/RTx/Converter/Config.pm
@@ -0,0 +1,78 @@
+package RTx::Converter::Config;
+
+use warnings;
+use strict;
+use base qw(Class::Accessor::Fast);
+__PACKAGE__->mk_accessors(qw(debug));
+
+
+=head1 NAME
+
+RTx::Converter::Config - config data for any importers
+
+
+=head1 SYNOPSIS
+
+Generally accessed from an RTx::Converter object
+
+ use RTx::Converter;
+ my $converter = RTx::Convert->new;
+ warn "debugging info" if $converter->config->debug;
+  
+=head1 DESCRIPTION
+
+Methods that we expect any converter to have access to.
+
+Config data that is specific to the RTx converters should
+go in RTx::Converter::RTx::Config.
+
+=head1 METHODS
+
+=head2 debug
+
+Whether or not debugging is enabled
+
+=cut
+
+
+=head1 AUTHOR
+
+Kevin Falcone  C<< <falcone at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;
+
+
diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
new file mode 100644
index 0000000..154c74a
--- /dev/null
+++ b/lib/RTx/Converter/RT1.pm
@@ -0,0 +1,175 @@
+package RTx::Converter::RT1;
+
+use warnings;
+use strict;
+use base qw(Class::Accessor::Fast);
+__PACKAGE__->mk_accessors(qw(config _handle _sth));
+
+use RTx::Converter::RT1::Config;
+use DBI;
+
+=head1 NAME
+
+RTx::Converter::RT1 - Handle the RT1 side of a conversion
+
+
+=head1 SYNOPSIS
+
+    use RTx::Converter::RT1;
+    my $converter = RTx::Converter::RT1->new;
+
+=head1 DESCRIPTION
+
+Object that should be used by converter scripts to 
+
+=head1 METHODS
+
+=head2 new
+
+Returns a converter object after setting up things such as the config
+
+=cut
+
+sub new {
+    my $class = shift;
+
+    my $self = $class->SUPER::new(@_);
+    $self->config(RTx::Converter::RT1::Config->new);
+    return $self;
+}
+
+=head2 config 
+
+Returns a config object
+
+=head2 _handle
+
+private method for the db handle of the RT1 database
+
+=head2 _connect
+
+conect to the RT1 database
+
+=cut
+
+# this probably really wants to be using DBIx::SearchBuilder or
+# some other ORM, but we're really just doing a few simple SQL calls
+# so we'll avoid having to map records, or do something else "hard"
+
+sub _connect {
+    my $self = shift;
+    my $config = $self->config;
+    
+    my $dsn = sprintf("DBI:mysql:database=%s;host=%s;",
+                      $config->database, $config->dbhost );
+    warn "connecting to $dsn" if $config->debug;
+    my $dbh = DBI->connect($dsn, $config->dbuser, $config->dbpassword) 
+        or die "Can't connect to RT1 database: ".$DBI::errstr;
+
+    return $self->_handle($dbh);
+}
+
+=head2 _run_query
+
+Takes a sql string and a list of placeholder values
+
+ _run_query( sql => $sql, placeholders => \@placeholders )
+
+Returns a statement handle
+
+=cut
+
+sub _run_query {
+    my $self = shift;
+    my %args = @_;
+
+    my $handle= $self->_handle|| $self->_connect;
+
+    my @placeholders = @{$args{placeholders}||[]};
+    
+    my $sth = $handle->prepare($args{sql});
+    $sth->execute(@placeholders) or 
+      die("Can't run query: $args{sql} - " . 
+          join(" ", at placeholders) . 
+          "\nReason:" . $DBI::errstr . "\n");
+    
+    return $sth;
+}
+
+=head2 get_user
+
+Intended to be called in a loop.
+Wraps over the DBH iterator.  When called for the first time, 
+will fetch the users and return one.  Will keep returning one
+until we run out.
+
+=cut
+
+sub get_user {
+    my $self = shift;
+
+    my $sth = $self->_sth;
+
+    unless ($sth) {
+        my $sql = <<ESQL;
+select user_id as Name, 
+       real_name as RealName, 
+       password as Password, 
+       email as EmailAddress, 
+       phone as WorkPhone, 
+       comments as Comments, 
+       admin_rt as SuperUser
+from users
+ESQL
+        $sth = $self->_run_query( sql => $sql );
+        $self->_sth($sth);
+    }
+
+    my $user_data = $sth->fetchrow_hashref;
+
+    if ($user_data && !$user_data->{EmailAddress}) {
+        $user_data->{EmailAddress} = $user_data->{Name}.'@'.$self->config->email_domain;
+    }
+
+    return $user_data;
+}
+
+=head1 AUTHOR
+
+Kevin Falcone  C<< <falcone at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;
diff --git a/lib/RTx/Converter/RT1/Config.pm b/lib/RTx/Converter/RT1/Config.pm
new file mode 100644
index 0000000..6317c82
--- /dev/null
+++ b/lib/RTx/Converter/RT1/Config.pm
@@ -0,0 +1,116 @@
+package RTx::Converter::RT1::Config;
+use base qw/RTx::Converter::Config/;
+use warnings;
+use strict;
+
+__PACKAGE__->mk_accessors(qw(dbuser dbpassword database dbhost data_directory 
+                             email_domain ));
+
+=head1 NAME
+
+RTx::Converter::RT1::Config - config data for the RT1 importer
+
+
+=head1 SYNOPSIS
+
+    use RTx::Converter::RT1::Config;
+    
+Usually retrieved from a converter object with
+
+    $rt1converter->config
+
+=head1 DESCRIPTION
+
+Useful config values for the RT1 converter.
+These include where to find rt data, how to log into the rt1 database, and some
+sane defaults about where to store tickets in the RT3 system
+
+=head1 METHODS
+
+=head2 dbuser
+
+user for the rt1 database
+
+=head2 dbpassword
+
+password for the rt1 database
+
+=head2 database
+
+name of the rt1 database
+
+=head2 dbhost
+
+host for the rt1 database
+
+=head2 data_directory
+
+where to find the rt1 transaction files
+
+=head2 email_domain
+
+users without emails will have emails autocreated like this
+
+ user_id at email_domain
+
+=head2 new
+
+creates an RT1 config object and sets some defaults 
+carried over from the old rt1 import script
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->dbuser("root");
+    $self->dbpassword("password");
+    $self->database("rt");
+    $self->dbhost("localhost");
+    $self->data_directory("/opr/rt/transactions");
+    $self->email_domain("example.com");
+
+    return $self;
+}
+
+=head1 AUTHOR
+
+Kevin Falcone  C<< <falcone at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;
+
diff --git a/t/00.load.t b/t/00.load.t
new file mode 100644
index 0000000..8db7c1a
--- /dev/null
+++ b/t/00.load.t
@@ -0,0 +1,12 @@
+use Test::More tests => 6;
+
+BEGIN {
+use_ok( 'RTx::Converter' );
+use_ok( 'RTx::Converter::Config' );
+use_ok( 'RTx::Converter::RT1' );
+use_ok( 'RTx::Converter::RT1::Config' );
+use_ok( 'RTx::Converter::RT3' );
+use_ok( 'RTx::Converter::RT3::Config' );
+}
+
+diag( "Testing RTx::Converter $RTx::Converter::VERSION" );
diff --git a/t/99.pod-coverage.t b/t/99.pod-coverage.t
new file mode 100644
index 0000000..703f91d
--- /dev/null
+++ b/t/99.pod-coverage.t
@@ -0,0 +1,6 @@
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@;
+all_pod_coverage_ok();
diff --git a/t/99.pod.t b/t/99.pod.t
new file mode 100644
index 0000000..976d7cd
--- /dev/null
+++ b/t/99.pod.t
@@ -0,0 +1,6 @@
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod 1.14";
+plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
+all_pod_files_ok();

commit df5d1fc5731c6464f80d9d1f205c41fea2a678d3
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:24:59 2007 +0000

    forgot these in the first commit
      handle the RT3 side of user/queue/ticket creation and config

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
new file mode 100644
index 0000000..ebaeb24
--- /dev/null
+++ b/lib/RTx/Converter/RT3.pm
@@ -0,0 +1,166 @@
+package RTx::Converter::RT3;
+
+use warnings;
+use strict;
+use base qw(Class::Accessor::Fast);
+__PACKAGE__->mk_accessors(qw(config));
+
+use RTx::Converter::RT3::Config;
+use RT::User;
+
+=head1 NAME
+
+RTx::Converter::RT3 - Handle the RT3 side of a conversion
+
+
+=head1 SYNOPSIS
+
+    use RTx::Converter::RT3;
+    my $converter = RTx::Converter::RT3->new;
+
+=head1 DESCRIPTION
+
+Object that should be used by converter scripts to 
+
+=head1 METHODS
+
+=head2 new
+
+Returns a converter object after setting up things such as the config
+
+=cut
+
+sub new {
+    my $class = shift;
+
+    my $self = $class->SUPER::new(@_);
+    $self->config(RTx::Converter::RT3::Config->new);
+    return $self;
+}
+
+=head2 config 
+
+Returns a config object
+
+=head2 create_user
+
+Creates a new user, expects a hash of valid values for RT3's
+User::Create method plus one special SuperUser argument
+that will cause SuperUser rights to be granted after creation
+
+returns an RT::User object, or undef on failure
+
+=cut
+
+sub create_user {
+    my $self = shift;
+    my %args = ( Privileged => 1, @_ );
+
+    # this is very RT1'y, because we kept super user rights
+    # in the users table
+    my $is_superuser = delete $args{SuperUser};
+
+	my $user = RT::User->new($RT::SystemUser);
+
+    $user->Load( $args{Name} );
+
+    if ($user->Id) {
+        print "\nLoaded ".$user->Name." from the database" if $self->config->debug;
+        return $user;
+    }
+    
+    local $RT::MinimumPasswordLength = 1; # some people from RT1 have short passwords
+	my ($val, $msg) =  $user->Create( %args );
+
+    if ($val) {
+        print "\nAdded queue ".$user->Name if $self->config->debug;
+        if ($is_superuser) {
+            $user->PrincipalObj->GrantRight( Right => 'SuperUser', Object => $RT::System );
+            print " as superuser" if $self->config->debug;
+        }
+        return $user;
+    } else {
+        print "\nfailed to create user $args{Name}: $msg";
+        return;
+    }
+
+}
+
+=head2 create_queue
+
+Creates a new queue, expects a hash of valid values for RT3's
+Queue::Create method
+
+returns an RT::Queue object, or undef on failure
+
+=cut
+
+sub create_queue {
+    my $self = shift;
+    my %args = @_;
+
+    # RT3 really doesn't like undef arguments
+    %args = map { $_ => $args{$_} } grep { defined $args{$_} } keys %args;
+
+	my $queue = RT::Queue->new($RT::SystemUser);
+
+	# Try to load up the current queue by name. avoids duplication.
+	$queue->Load($args{Name});
+	
+	#if the queue isn't there, create one.
+	if ($queue->id) {
+        print "\nLoaded queue ".$queue->Name." from the database" if $self->config->debug;
+        return $queue;
+    }
+
+    my ($val, $msg) = $queue->Create(%args);
+
+    if ($val) {
+        print "\nAdded queue ".$queue->Name if $self->config->debug;
+        return $queue;
+    } else {
+        print "\nfailed to create queue [$args{Name}]: $msg";
+        return;
+    }
+
+}
+
+=head1 AUTHOR
+
+Kevin Falcone  C<< <falcone at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;
diff --git a/lib/RTx/Converter/RT3/Config.pm b/lib/RTx/Converter/RT3/Config.pm
new file mode 100644
index 0000000..cd71f24
--- /dev/null
+++ b/lib/RTx/Converter/RT3/Config.pm
@@ -0,0 +1,85 @@
+package RTx::Converter::RT3::Config;
+use base qw/RTx::Converter::Config/;
+use warnings;
+use strict;
+
+__PACKAGE__->mk_accessors(qw(default_queue));
+
+=head1 NAME
+
+RTx::Converter::RT3::Config - config data for the RT3 importer
+
+
+=head1 SYNOPSIS
+
+    use RTx::Converter::RT1::Config;
+    
+Usually retrieved from a converter object with
+
+    $rt1converter->config
+
+=head1 DESCRIPTION
+
+Useful config values for the RT3 converter.
+
+=head1 METHODS
+
+=head2 default_queue
+
+Where to put tickets from deleted queues
+
+=head2 new
+
+creates an RT3 config object and sets some defaults 
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->default_queue("General");
+
+    return $self;
+}
+
+=head1 AUTHOR
+
+Kevin Falcone  C<< <falcone at bestpractical.com> >>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Best Practical Solutions, LLC.  All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+1;
+

commit 96bd5331ea7b583d380b72c3f02009d4f3c96d68
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:25:19 2007 +0000

    clean up readme
    * move RT::Config code out to the script from the perl module
    * clean up docs and handle errors better in User code

diff --git a/README b/README
index d37f311..17abfe9 100644
--- a/README
+++ b/README
@@ -1,23 +1,11 @@
 RTx-Converter version 0.0.1
 
-[ REPLACE THIS...
-
-  The README is used to introduce the module and provide instructions on
-  how to install the module, any machine dependencies it may have (for
-  example C compilers and installed libraries) and any other information
-  that should be understood before the module is installed.
-
-  A README file is required for CPAN modules since CPAN extracts the
-  README file from a module distribution so that people browsing the
-  archive can use it get an idea of the modules uses. It is usually a
-  good idea to provide version information here so that people can
-  decide whether fixes for the module are worth downloading.
-]
-
 
 INSTALLATION
 
 To install this module, run the following commands:
+You may need to set PERL5LIB=/opt/rt3/lib (or wherever RT is installed)
+for this module to test and install properly
 
     perl Makefile.PL
     make
@@ -26,11 +14,6 @@ To install this module, run the following commands:
 
 
 
-DEPENDENCIES
-
-None.
-
-
 COPYRIGHT AND LICENCE
 
 Copyright (C) 2007, Best Practical Solutions LLC.
diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index 8458498..50c72c2 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -3,11 +3,15 @@ use strict;
 use warnings;
 
 # this may need to change depending on your installation
-#use lib qw(/opt/rt3/lib);
+use lib qw(/opt/rt3/lib);
 use lib qw(lib); # assumes you run this uninstalled
-use lib qw(/Users/falcone/svk/rt/harvard/rt-3.6.3/lib);
 
-use Getopt::Long;
+use Data::Dumper;
+
+use RT;
+
+    RT::LoadConfig();
+    RT::Init();
 
 use RTx::Converter;
 
@@ -17,10 +21,15 @@ my $rt3 = RTx::Converter->new( type => 'RT3' );
 # should probably read a config file here
 $rt1->config->dbpassword('');
 $rt1->config->database('harvardrt1');
-use Data::Dumper;
 $rt1->config->debug(1);
 $rt3->config->debug(1);
 
+# migrate our users
+print "Migrating Users";
+$RT::Handle->SimpleQuery("DELETE FROM Users where Name = 'root'");
 while (my $user = $rt1->get_user) {
     my $user_obj = $rt3->create_user(%$user);
+    unless ($user_obj) {
+        die "Failed to import user ".Dumper($user);
+    }
 }
diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 154c74a..e6b2b95 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -54,15 +54,16 @@ conect to the RT1 database
 
 # this probably really wants to be using DBIx::SearchBuilder or
 # some other ORM, but we're really just doing a few simple SQL calls
-# so we'll avoid having to map records, or do something else "hard"
+# so we'll avoid having to map the old tables for now
 
 sub _connect {
     my $self = shift;
     my $config = $self->config;
     
     my $dsn = sprintf("DBI:mysql:database=%s;host=%s;",
-                      $config->database, $config->dbhost );
-    warn "connecting to $dsn" if $config->debug;
+                      $config->database, $config->dbhost,
+                      { RaiseError => 1 });
+    print "connecting to $dsn" if $config->debug;
     my $dbh = DBI->connect($dsn, $config->dbuser, $config->dbpassword) 
         or die "Can't connect to RT1 database: ".$DBI::errstr;
 
@@ -100,8 +101,8 @@ sub _run_query {
 
 Intended to be called in a loop.
 Wraps over the DBH iterator.  When called for the first time, 
-will fetch the users and return one.  Will keep returning one
-until we run out.
+will fetch the users and returns one as a hashref.  
+Will keep returning one until we run out.
 
 =cut
 

commit ce322ee23b9ba8b885de075cb0a65e50a3b2f73d
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:25:25 2007 +0000

    handle multiple sth's

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index e6b2b95..27996d7 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -3,7 +3,7 @@ package RTx::Converter::RT1;
 use warnings;
 use strict;
 use base qw(Class::Accessor::Fast);
-__PACKAGE__->mk_accessors(qw(config _handle _sth));
+__PACKAGE__->mk_accessors(qw(config _handle ));
 
 use RTx::Converter::RT1::Config;
 use DBI;
@@ -97,6 +97,47 @@ sub _run_query {
     return $sth;
 }
 
+=head2 _sth
+
+Stores several named sth's for this object (since multiple queries
+can be happening simultaneously).
+
+Takes 
+ Name => sth for set
+ Name for get
+
+=cut
+
+sub _sth {
+    my $self = shift;
+
+    if (@_ > 1) {
+        my ($name,$sth) = @_;
+        $self->{sths}{$name} = $sth;
+    } elsif (@_) {
+        my $name = shift;
+        $self->{sths}{$name};
+    } else {
+        die "You must pass at least a name to _sth";
+    }
+}
+
+=head3 _clean_sth
+
+finishes the sth and gets rid of it
+takes the name of the sth
+
+=cut
+
+sub _clean_sth {
+    my $self = shift;
+    my $name = shift;
+
+    $self->_sth($name)->finish;
+    $self->_sth($name,undef);
+    return;
+}
+
 =head2 get_user
 
 Intended to be called in a loop.
@@ -109,7 +150,7 @@ Will keep returning one until we run out.
 sub get_user {
     my $self = shift;
 
-    my $sth = $self->_sth;
+    my $sth = $self->_sth('User');
 
     unless ($sth) {
         my $sql = <<ESQL;
@@ -123,7 +164,7 @@ select user_id as Name,
 from users
 ESQL
         $sth = $self->_run_query( sql => $sql );
-        $self->_sth($sth);
+        $self->_sth(User => $sth);
     }
 
     my $user_data = $sth->fetchrow_hashref;
@@ -132,6 +173,8 @@ ESQL
         $user_data->{EmailAddress} = $user_data->{Name}.'@'.$self->config->email_domain;
     }
 
+    $self->_clean_sth('User') unless $user_data;
+
     return $user_data;
 }
 

commit 05ec3d799a80b73c778ec4c6b1eee104993b2f96
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:25:34 2007 +0000

    handle queues/areas from RT1

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 27996d7..05a89f3 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -178,6 +178,78 @@ ESQL
     return $user_data;
 }
 
+=head3 get_queue
+
+Intended to be called in a loop.
+Wraps over the DBH iterator.  When called for the first time, 
+will fetch the queues and returns one as a hashref.  
+Will keep returning one until we run out.
+
+=cut
+
+sub get_queue {
+    my $self = shift;
+
+    my $sth = $self->_sth('Queue');
+
+    unless ($sth) {
+        my $sql = <<ESQL;
+select queue_id as Name, 
+       mail_alias as CorrespondAddress, 
+       comment_alias as CommentAddress, 
+       default_prio as InitialPriority, 
+       default_final_prio as FinalPriority, 
+       default_due_in as DefaultDueIn
+from queues
+ESQL
+        $sth = $self->_run_query( sql => $sql );
+        $self->_sth(Queue => $sth);
+    }
+
+    my $queue_data = $sth->fetchrow_hashref;
+
+    if ($queue_data) {
+        $queue_data->{Description} = "Imported from RT 1.0";
+    }
+
+    $self->_clean_sth('Queue') unless $queue_data;
+
+    return $queue_data;
+
+}
+
+=head3 get_area
+
+Intended to be called in a loop.
+Wraps over the DBH iterator.  When called for the first time, 
+will fetch the areas for the queue and returns one as a hashref.  
+Will keep returning one until we run out.
+
+Takes one argument, Name => Queue's Name
+
+=cut
+
+sub get_area {
+    my $self = shift;
+    my %args = @_;
+
+    my $sth = $self->_sth('Area');
+
+    unless ($sth) {
+        my $sql = 'select area from queue_areas where queue_id = ?';
+        $sth = $self->_run_query( sql => $sql, placeholders => [$args{Name}] );
+        $self->_sth(Area => $sth);
+    }
+
+    my $area_data = $sth->fetchrow_hashref;
+
+    $self->_clean_sth('Area') unless $area_data;
+
+    return $area_data;
+
+
+}
+
 =head1 AUTHOR
 
 Kevin Falcone  C<< <falcone at bestpractical.com> >>

commit 8225a8e448ccb93784a47f18f4bafe34d5b1a358
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:25:41 2007 +0000

    refactor into functions
    * add queue moving

diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index 50c72c2..4c42fff 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -24,12 +24,36 @@ $rt1->config->database('harvardrt1');
 $rt1->config->debug(1);
 $rt3->config->debug(1);
 
+print "Migrating Users\n";
+migrate_users();
+print "\n\nMigrating Queues\n";
+migrate_queues();
+print "\n\nDone.  Time to check all the data\n";
+
 # migrate our users
-print "Migrating Users";
-$RT::Handle->SimpleQuery("DELETE FROM Users where Name = 'root'");
-while (my $user = $rt1->get_user) {
-    my $user_obj = $rt3->create_user(%$user);
-    unless ($user_obj) {
-        die "Failed to import user ".Dumper($user);
+sub migrate_users {
+    $RT::Handle->SimpleQuery("DELETE FROM Users where Name = 'root'");
+    while (my $user = $rt1->get_user) {
+        my $user_obj = $rt3->create_user(%$user);
+        unless ($user_obj) {
+            die "Failed to import user ".Dumper($user);
+        }
     }
 }
+
+sub migrate_queues {
+    $RT::Handle->SimpleQuery("DELETE FROM Queues where Name = 'general'");
+    while (my $queue = $rt1->get_queue) {
+        my $queue_obj = $rt3->create_queue(%$queue);
+        unless ($queue_obj) {
+            die "Failed to import queue ".Dumper($queue);
+        }
+        while (my $area = $rt1->get_area( Name => $queue_obj->Name )) {
+            if (my $msg = $rt3->create_queue_area( Queue => $queue_obj, 
+                    Area => $area->{area} )) {
+                warn "Failed to create area $area->{area} for queue ".$queue_obj->Name." $msg";
+            }
+        }
+    }
+}
+

commit 99f9222a2105300852fb7de9196f8635846e61c0
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:25:57 2007 +0000

    we need a default encoding for passing data into RT3

diff --git a/lib/RTx/Converter/RT3/Config.pm b/lib/RTx/Converter/RT3/Config.pm
index cd71f24..ae0bd90 100644
--- a/lib/RTx/Converter/RT3/Config.pm
+++ b/lib/RTx/Converter/RT3/Config.pm
@@ -3,7 +3,7 @@ use base qw/RTx::Converter::Config/;
 use warnings;
 use strict;
 
-__PACKAGE__->mk_accessors(qw(default_queue));
+__PACKAGE__->mk_accessors(qw(default_queue encoding));
 
 =head1 NAME
 
@@ -28,6 +28,10 @@ Useful config values for the RT3 converter.
 
 Where to put tickets from deleted queues
 
+=head2 encoding
+
+What encoding to put data in before handing it to RT3's methods
+
 =head2 new
 
 creates an RT3 config object and sets some defaults 
@@ -38,7 +42,8 @@ sub new {
     my $class = shift;
     my $self = $class->SUPER::new(@_);
 
-    $self->default_queue("General");
+    $self->default_queue('General');
+    $self->encoding('iso-8859-1');
 
     return $self;
 }

commit 3604a8a45effbb57c645691a346aced40b0ae686
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:04 2007 +0000

    add data encoding
    * handle queue and area creation

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index ebaeb24..f04a19d 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -7,6 +7,7 @@ __PACKAGE__->mk_accessors(qw(config));
 
 use RTx::Converter::RT3::Config;
 use RT::User;
+use Encode;
 
 =head1 NAME
 
@@ -62,6 +63,7 @@ sub create_user {
 
 	my $user = RT::User->new($RT::SystemUser);
 
+    %args = %{$self->_encode_data(\%args)};
     $user->Load( $args{Name} );
 
     if ($user->Id) {
@@ -104,6 +106,7 @@ sub create_queue {
 
 	my $queue = RT::Queue->new($RT::SystemUser);
 
+    %args = %{$self->_encode_data(\%args)};
 	# Try to load up the current queue by name. avoids duplication.
 	$queue->Load($args{Name});
 	
@@ -125,6 +128,117 @@ sub create_queue {
 
 }
 
+=head3 create_queue_area
+
+Takes 
+ Queue => RT::Queue, Area => Area's name
+
+Returns an error message if making the appropriate custom fields fails.
+Otherwise returns the empty string
+
+This is rather RT1 specific.  RT2 has a more hierarchical Keyword
+option that translates into CFs.  Areas are the RT1 "custom field" 
+but there was only one of them, so we just make an RT3 Custom Field
+called Area and whack a simple select list into it
+
+=cut
+
+sub create_queue_area {
+    my $self = shift;
+    my %args = @_;
+    my $queue = delete $args{Queue};
+
+    %args = %{$self->_encode_data(\%args)};
+
+    my $cf = $self->_create_queue_area_cf($queue);
+
+    if ($self->config->debug) {
+        print "\nAdding $args{Area} to the area for ".$queue->Name;
+    }
+
+    my ($val,$msg) = $cf->AddValue( Name => $args{Area} );
+    return $msg;
+}
+
+=head3 _create_queue_area_cf
+
+Wraps up the nasty logic of loading/creating a CF for the area
+
+=cut
+
+sub _create_queue_area_cf {
+    my $self = shift;
+    my $queue = shift;
+
+    # load up the custom field
+    my $cf = RT::CustomField->new($RT::SystemUser);
+    $cf->LoadByName(
+        Name  => 'Area',
+        Queue => $queue->Id
+    );  
+
+    # look for an existing cf not assigned to this queue yet
+    unless ($cf->Id) {
+        $cf->LoadByName( Name => 'Area' );
+        if ($cf->Id) {
+            $cf->AddToObject( $queue );
+        }   
+    }   
+
+    unless ($cf->Id) {
+        $cf->Create( 
+            Name     => 'Area',
+            Type     => 'SelectSingle',
+            Queue    => $queue->Id
+        );  
+    }   
+    unless ( $cf->Id ) {
+        print "\nCouldn't create custom field Area for queue" . $queue->Name;
+    }
+
+    return $cf;
+
+}
+
+=head3 _encode_data
+
+Used to make sure data gets properly unicode'd for RT3.6.
+Failure to use this in places will make non-americans unhappy
+
+Takes a hashref of arguments, returns an encoded hashref.
+
+=cut
+
+sub _encode_data {
+    my $self = shift;
+    my %args = %{shift||{}};
+
+    foreach my $key ( keys %args ) {
+        if ( !ref( $args{$key} ) ) {
+            $args{$key} = decode( $self->config->encoding, $args{$key} );
+        }
+        elsif ( ref( $args{$key} ) eq 'ARRAY' ) {
+            my @temp = @{ $args{$key} };
+            undef $args{$key};
+            foreach my $var (@temp) {
+                if ( ref($var) ) {
+
+                    push( @{ $args{$key} }, $var );
+                }
+                else {
+                    push( @{ $args{$key} }, decode( $self->config->encoding, $var ) );
+                }
+            }
+        }
+        else {
+            die "What do I do with $key for %args. It is a "
+              . ref( { $args{$key} } );
+        }
+    }
+
+    return \%args;
+}
+
 =head1 AUTHOR
 
 Kevin Falcone  C<< <falcone at bestpractical.com> >>

commit d4aa8310e04169688383e207c4487160007ce158
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:12 2007 +0000

    fix typo in message
    * make root always be a superuser

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index f04a19d..a01feea 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -60,6 +60,9 @@ sub create_user {
     # this is very RT1'y, because we kept super user rights
     # in the users table
     my $is_superuser = delete $args{SuperUser};
+    if ($args{Name} eq 'root') {
+        $is_superuser = 1;
+    }
 
 	my $user = RT::User->new($RT::SystemUser);
 
@@ -75,7 +78,7 @@ sub create_user {
 	my ($val, $msg) =  $user->Create( %args );
 
     if ($val) {
-        print "\nAdded queue ".$user->Name if $self->config->debug;
+        print "\nAdded user ".$user->Name if $self->config->debug;
         if ($is_superuser) {
             $user->PrincipalObj->GrantRight( Right => 'SuperUser', Object => $RT::System );
             print " as superuser" if $self->config->debug;

commit 41530a62ae28bb8fb2b3174f0cf6474f4ee34d99
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:27 2007 +0000

    ignore generated files

commit ed3bfa2567cc0ca711588137365764e35e97470b
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:33 2007 +0000

    move queue privileges from rt1 to rt3

diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index 4c42fff..94d6010 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -30,7 +30,6 @@ print "\n\nMigrating Queues\n";
 migrate_queues();
 print "\n\nDone.  Time to check all the data\n";
 
-# migrate our users
 sub migrate_users {
     $RT::Handle->SimpleQuery("DELETE FROM Users where Name = 'root'");
     while (my $user = $rt1->get_user) {
@@ -48,10 +47,20 @@ sub migrate_queues {
         unless ($queue_obj) {
             die "Failed to import queue ".Dumper($queue);
         }
+
+        # handle "Areas", turning them into CFs
         while (my $area = $rt1->get_area( Name => $queue_obj->Name )) {
             if (my $msg = $rt3->create_queue_area( Queue => $queue_obj, 
-                    Area => $area->{area} )) {
-                warn "Failed to create area $area->{area} for queue ".$queue_obj->Name." $msg";
+                                                   Area  => $area->{area} )) {
+                print "Failed to create area $area->{area} for queue ".$queue_obj->Name." $msg";
+            }
+        }
+
+        # convert the old Queue ACLs into new RT rights
+        while (my $acl = $rt1->get_queue_acl( Name => $queue_obj->Name )) {
+            if (my $msg = $rt3->create_queue_acl( Queue => $queue_obj, 
+                                                  Acl   => $acl )) {
+                print "Failed to create acl ".Dumper($acl)." for queue ".$queue_obj->Name." $msg";
             }
         }
     }
diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 05a89f3..48d7965 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -246,8 +246,36 @@ sub get_area {
     $self->_clean_sth('Area') unless $area_data;
 
     return $area_data;
+}
+
+=head3 get_queue_acl
+
+Intended to be called in a loop.
+Wraps over the DBH iterator.  When called for the first time, 
+will fetch the acls for the queue and returns one as a hashref.  
+Will keep returning one until we run out.
+
+Takes one argument, Name => Queue's Name
+
+=cut
+
+sub get_queue_acl {
+    my $self = shift;
+    my %args = @_;
+
+    my $sth = $self->_sth('ACL');
+
+    unless ($sth) {
+        my $sql = 'select user_id, display, manipulate, admin from queue_acl where queue_id = ?';
+        $sth = $self->_run_query( sql => $sql, placeholders => [$args{Name}] );
+        $self->_sth(ACL=> $sth);
+    }
+
+    my $acl_data = $sth->fetchrow_hashref;
 
+    $self->_clean_sth('ACL') unless $acl_data;
 
+    return $acl_data;
 }
 
 =head1 AUTHOR
diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index a01feea..165cad6 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -6,7 +6,6 @@ use base qw(Class::Accessor::Fast);
 __PACKAGE__->mk_accessors(qw(config));
 
 use RTx::Converter::RT3::Config;
-use RT::User;
 use Encode;
 
 =head1 NAME
@@ -203,6 +202,73 @@ sub _create_queue_area_cf {
 
 }
 
+=head2 create_queue_acl 
+
+Takes 
+ Queue => RT::Queue
+ Acl => acl data from RT1
+
+Sets a number of new rights based on the old display/manipulate/admin 
+categories.  This should probably be reworked manually to use groups
+once RT3 is being tested.  But, if you have a lot of users, this will
+at least get you converted.
+
+XXX Possibly create 3 groups, granting rights on the queues and
+adding users to the groups, rather than doing individual rights
+
+=cut
+
+sub create_queue_acl {
+    my $self = shift;
+    my %args = @_;
+
+    my $queue    = $args{Queue};
+    my $acl      = $args{Acl};
+    my $username = delete $acl->{user_id};
+
+
+    my %rightlist = (
+       display    => [qw(SeeQueue ShowTemplate ShowScrips 
+                         ShowTicket ShowTicketComments)],
+       manipulate => [qw(CreateTicket ReplyToTicket CommentOnTicket 
+                         OwnTicket ModifyTicket DeleteTicket)],
+       admin      => [qw(ModifyACL ModifyQueueWatchers AdminCustomField
+                         ModifyTemplate ModifyScrips)] 
+    );
+
+    my @rights = map { @{$rightlist{$_}||[]} } keys %$acl;
+
+    return unless @rights;
+    
+    my $user = RT::User->new($RT::SystemUser);
+    $user->Load($username);
+    
+    unless ($user->id) {
+        return "\nCouldn't find user $username Not granting rights\n";
+    }
+
+    my $principal = $user->PrincipalObj;
+    
+    print "\nAdding rights for $username to ".$queue->Name if $self->config->debug;
+    foreach my $right (@rights) {
+        print "...$right" if $self->config->debug;
+        my ($val,$msg) = $principal->GrantRight( Right  => $right,
+                                                 Object  => $queue);
+        unless ($val) {
+            return "\nFailed to grant $right to $username: $msg\n";
+        }
+    }
+    
+    print "...adding as AdminCc." if $self->config->debug;
+    my ($val,$msg) = $queue->AddWatcher( Type        => 'AdminCC', 
+                                         PrincipalId => $principal->Id );
+    unless ($val) {
+        return "\nFailed to make $username an AdminCc: $msg\n";
+    }
+
+    return;
+}
+
 =head3 _encode_data
 
 Used to make sure data gets properly unicode'd for RT3.6.

commit b9122a636cf3734ba9795f5cf3093007ef1c4d32
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:39 2007 +0000

    remove spurious tabs

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index 165cad6..a90a37f 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -63,7 +63,7 @@ sub create_user {
         $is_superuser = 1;
     }
 
-	my $user = RT::User->new($RT::SystemUser);
+    my $user = RT::User->new($RT::SystemUser);
 
     %args = %{$self->_encode_data(\%args)};
     $user->Load( $args{Name} );
@@ -74,7 +74,7 @@ sub create_user {
     }
     
     local $RT::MinimumPasswordLength = 1; # some people from RT1 have short passwords
-	my ($val, $msg) =  $user->Create( %args );
+    my ($val, $msg) =  $user->Create( %args );
 
     if ($val) {
         print "\nAdded user ".$user->Name if $self->config->debug;
@@ -106,14 +106,14 @@ sub create_queue {
     # RT3 really doesn't like undef arguments
     %args = map { $_ => $args{$_} } grep { defined $args{$_} } keys %args;
 
-	my $queue = RT::Queue->new($RT::SystemUser);
+    my $queue = RT::Queue->new($RT::SystemUser);
 
     %args = %{$self->_encode_data(\%args)};
-	# Try to load up the current queue by name. avoids duplication.
-	$queue->Load($args{Name});
-	
-	#if the queue isn't there, create one.
-	if ($queue->id) {
+    # Try to load up the current queue by name. avoids duplication.
+    $queue->Load($args{Name});
+    
+    #if the queue isn't there, create one.
+    if ($queue->id) {
         print "\nLoaded queue ".$queue->Name." from the database" if $self->config->debug;
         return $queue;
     }

commit 7f3f49313a6e412a1c4a654045328f9a862e079c
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:44 2007 +0000

    refactor all the boring icky bits of data fetching

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 48d7965..8bad9db 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -97,6 +97,33 @@ sub _run_query {
     return $sth;
 }
 
+=head3 _fetch_data 
+
+wrapper around _run_query to hide the boring
+bits of iterating over the data set and 
+cleaning up when we get to the end of the data.
+
+=cut
+
+sub _fetch_data {
+    my $self = shift;
+    my %args = @_;
+    my $name = delete $args{name};
+
+    my $sth = $self->_sth($name);
+
+    unless ($sth) {
+        $sth = $self->_run_query( %args );
+        $self->_sth( $name => $sth );
+    }
+
+    my $data = $sth->fetchrow_hashref;
+
+    $self->_clean_sth($name) unless $data;
+
+    return $data;
+}
+
 =head2 _sth
 
 Stores several named sth's for this object (since multiple queries
@@ -150,10 +177,7 @@ Will keep returning one until we run out.
 sub get_user {
     my $self = shift;
 
-    my $sth = $self->_sth('User');
-
-    unless ($sth) {
-        my $sql = <<ESQL;
+    my $sql = <<ESQL;
 select user_id as Name, 
        real_name as RealName, 
        password as Password, 
@@ -163,18 +187,13 @@ select user_id as Name,
        admin_rt as SuperUser
 from users
 ESQL
-        $sth = $self->_run_query( sql => $sql );
-        $self->_sth(User => $sth);
-    }
 
-    my $user_data = $sth->fetchrow_hashref;
+    my $user_data = $self->_fetch_data( name => 'User', sql => $sql );
 
     if ($user_data && !$user_data->{EmailAddress}) {
         $user_data->{EmailAddress} = $user_data->{Name}.'@'.$self->config->email_domain;
     }
 
-    $self->_clean_sth('User') unless $user_data;
-
     return $user_data;
 }
 
@@ -190,10 +209,7 @@ Will keep returning one until we run out.
 sub get_queue {
     my $self = shift;
 
-    my $sth = $self->_sth('Queue');
-
-    unless ($sth) {
-        my $sql = <<ESQL;
+    my $sql = <<ESQL;
 select queue_id as Name, 
        mail_alias as CorrespondAddress, 
        comment_alias as CommentAddress, 
@@ -202,18 +218,13 @@ select queue_id as Name,
        default_due_in as DefaultDueIn
 from queues
 ESQL
-        $sth = $self->_run_query( sql => $sql );
-        $self->_sth(Queue => $sth);
-    }
 
-    my $queue_data = $sth->fetchrow_hashref;
+    my $queue_data = $self->_fetch_data( name => 'Queue', sql => $sql );
 
     if ($queue_data) {
         $queue_data->{Description} = "Imported from RT 1.0";
     }
 
-    $self->_clean_sth('Queue') unless $queue_data;
-
     return $queue_data;
 
 }
@@ -233,17 +244,11 @@ sub get_area {
     my $self = shift;
     my %args = @_;
 
-    my $sth = $self->_sth('Area');
+    my $sql = 'select area from queue_areas where queue_id = ?';
 
-    unless ($sth) {
-        my $sql = 'select area from queue_areas where queue_id = ?';
-        $sth = $self->_run_query( sql => $sql, placeholders => [$args{Name}] );
-        $self->_sth(Area => $sth);
-    }
-
-    my $area_data = $sth->fetchrow_hashref;
-
-    $self->_clean_sth('Area') unless $area_data;
+    my $area_data = $self->_fetch_data( name => 'Area', 
+                                        sql => $sql, 
+                                        placeholders => [$args{Name}] );
 
     return $area_data;
 }
@@ -263,15 +268,14 @@ sub get_queue_acl {
     my $self = shift;
     my %args = @_;
 
-    my $sth = $self->_sth('ACL');
+    my $sql = 'select user_id, display, manipulate, admin from queue_acl where queue_id = ?';
 
-    unless ($sth) {
-        my $sql = 'select user_id, display, manipulate, admin from queue_acl where queue_id = ?';
-        $sth = $self->_run_query( sql => $sql, placeholders => [$args{Name}] );
-        $self->_sth(ACL=> $sth);
-    }
+    my $acl_data = $self->_fetch_data( name => 'ACL', 
+                                       sql => $sql, 
+                                       placeholders => [$args{Name}] );
 
-    my $acl_data = $sth->fetchrow_hashref;
+    return $acl_data;
+}
 
     $self->_clean_sth('ACL') unless $acl_data;
 

commit 832d84cc32d81898dd24bfcb93bc3c2c0e28551e
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:26:49 2007 +0000

    the script is keying on the presence of a message,
      don't return one on success

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index a90a37f..83b4e1f 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -159,7 +159,7 @@ sub create_queue_area {
     }
 
     my ($val,$msg) = $cf->AddValue( Name => $args{Area} );
-    return $msg;
+    return $val ? '' : $msg ;
 }
 
 =head3 _create_queue_area_cf

commit bca6dff757b2860f69b0400fb18df54e663656ee
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:27:08 2007 +0000

    add a ticket fetching routine, for now limit to 100 tickets for testing

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 8bad9db..91053bd 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -277,9 +277,23 @@ sub get_queue_acl {
     return $acl_data;
 }
 
-    $self->_clean_sth('ACL') unless $acl_data;
+=head3 get_ticket
 
-    return $acl_data;
+Intended to be called in a loop.
+Wraps over the DBH iterator.  When called for the first time, 
+will fetch all tickets and return one as a hashref.  
+Will keep returning one until we run out.
+
+=cut
+
+sub get_ticket {
+    my $self = shift;
+    my %args = @_;
+
+    my $sql = 'select * from each_req limit 100';
+    my $ticket_data = $self->_fetch_data( name => 'Ticket', sql => $sql );
+
+    return $ticket_data;
 }
 
 =head1 AUTHOR

commit 8a6819581ed59a4422ed4471a6cf5c235666dd11
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:27:14 2007 +0000

    fix general queue removal so we don't end up with an RT3 installation
      with a general queue
    * make some fatal errors more fatal so they don't get lost in the noise
    * add in calls to ticket creation

diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index 94d6010..0634103 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -28,6 +28,8 @@ print "Migrating Users\n";
 migrate_users();
 print "\n\nMigrating Queues\n";
 migrate_queues();
+print "\n\nMigrating Tickets\n";
+migrate_tickets();
 print "\n\nDone.  Time to check all the data\n";
 
 sub migrate_users {
@@ -41,8 +43,12 @@ sub migrate_users {
 }
 
 sub migrate_queues {
-    $RT::Handle->SimpleQuery("DELETE FROM Queues where Name = 'general'");
     while (my $queue = $rt1->get_queue) {
+        if ($queue->{Name} =~ /^general$/i) {
+            # RT1 didn't have a default queue, so the client might have named
+            # it "problems" and confused things in rt3 that want a general queue.
+            $RT::Handle->SimpleQuery("DELETE FROM Queues where Name = 'general'");
+        }
         my $queue_obj = $rt3->create_queue(%$queue);
         unless ($queue_obj) {
             die "Failed to import queue ".Dumper($queue);
@@ -52,7 +58,7 @@ sub migrate_queues {
         while (my $area = $rt1->get_area( Name => $queue_obj->Name )) {
             if (my $msg = $rt3->create_queue_area( Queue => $queue_obj, 
                                                    Area  => $area->{area} )) {
-                print "Failed to create area $area->{area} for queue ".$queue_obj->Name." $msg";
+                die "Failed to create area $area->{area} for queue ".$queue_obj->Name." $msg";
             }
         }
 
@@ -60,9 +66,17 @@ sub migrate_queues {
         while (my $acl = $rt1->get_queue_acl( Name => $queue_obj->Name )) {
             if (my $msg = $rt3->create_queue_acl( Queue => $queue_obj, 
                                                   Acl   => $acl )) {
-                print "Failed to create acl ".Dumper($acl)." for queue ".$queue_obj->Name." $msg";
+                die "Failed to create acl ".Dumper($acl)." for queue ".$queue_obj->Name." $msg";
             }
         }
     }
 }
 
+sub migrate_tickets {
+    while (my $ticket = $rt1->get_ticket) {
+        print "\n$ticket->{id}.";
+        $rt3->create_ticket(%$ticket);
+    }
+    #$rt3->create_links;
+}
+

commit 45eb2e1e965bb6e551d299cc40faf91d60090879
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:27:26 2007 +0000

    fix SQL query for RT1 to rename columns
    * add in simple ticket creation and merge tracking logic for the
      RT3 side of things

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 91053bd..ac3d4ac 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -290,7 +290,25 @@ sub get_ticket {
     my $self = shift;
     my %args = @_;
 
-    my $sql = 'select * from each_req limit 100';
+    my $sql = <<SQL;
+select serial_num as id,
+       effective_sn as EffectiveId,
+       status as Status,
+       requestors as Requestors,
+       owner as Owner,
+       subject as Subject,
+       priority as Priority,
+       final_priority as FinalPriority,
+       initial_priority as InitialPriority,
+       date_due as Due,
+       date_told as Told,
+       date_created as Created,
+       date_acted as Updated,
+       queue_id as Queue,
+       area as Area
+from each_req 
+limit 100
+SQL
     my $ticket_data = $self->_fetch_data( name => 'Ticket', sql => $sql );
 
     return $ticket_data;
diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index 83b4e1f..69ddc17 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -3,7 +3,7 @@ package RTx::Converter::RT3;
 use warnings;
 use strict;
 use base qw(Class::Accessor::Fast);
-__PACKAGE__->mk_accessors(qw(config));
+__PACKAGE__->mk_accessors(qw(config _merge_list));
 
 use RTx::Converter::RT3::Config;
 use Encode;
@@ -269,6 +269,88 @@ sub create_queue_acl {
     return;
 }
 
+=head3 create_ticket 
+
+Takes arguments similar to RT3::Ticket's Create.
+Will take a Requestors argument and try to chop it up into
+individual Requestor values.
+
+=cut
+
+sub create_ticket {
+    my $self = shift;
+    my %args = @_;
+
+    # track what merges need to be done later, after all
+    # the tickets are created (Rather than playing games
+    # to see if the ticket we're merging into has been 
+    # created yet)
+    if ($args{EffectiveId} && $args{EffectiveId} != $args{id}) {
+            print "merging into $args{EffectiveId}";
+            $self->_merges( ticket => $args{id},
+                            into   => $args{EffectiveId} );
+            $args{Status} = 'resolved';
+    }
+
+    if ($args{Status} eq 'dead') {
+        $args{Status} = 'resolved';
+    }
+
+    my @requestors = split(',',$args{Requestors});
+        
+    # if they had an old queue, stuff the new one into general
+    my $queue = new RT::Queue($RT::SystemUser);
+    $queue->Load($args{Queue});
+    unless ($queue->id) {
+        print "...can't find queue id for $args{id} queue $args{Queue} - using default";
+        $queue->Load($self->config->default_queue);
+    }
+    $args{Queue} = $queue;
+        
+    # RT1 stored dates in "Seconds from the epoch" so we 
+    # need to convert that to ISO so RT3 can grok it
+    foreach my $type (qw(Due Told Created Updated)) {
+        if (defined $args{$type} && $args{type} =~ /^\d+$/) {
+            my $date = new RT::Date($RT::SystemUser);
+            $date->Set( Format => 'unix', Value => $args{$type} );
+            $args{$type} = $date->ISO;
+        }
+    }
+
+    if ($args{Area} && (my $area = delete $args{Area})) {
+        print "setting Area $area" if $self->config->debug;
+        my $cf_obj = $queue->CustomField('Area');
+        $args{'CustomField-'.$cf_obj->Id} = $area;;
+    }
+
+    my $ticket = new RT::Ticket($RT::SystemUser);
+    my ($val, $msg) = $ticket->Import(Requestor => \@requestors, %args); 
+    die $msg unless $val;
+
+    return $ticket;
+}
+
+=head3 _merge_list
+
+private data storage routine to hold what tickets are merged where
+
+=head3 _merges
+
+takes ticket => id, into => otherid
+tracks what merges need doing after we're done
+creating all the tickets.
+
+=cut
+
+sub _merges {
+    my $self = shift;
+    my %args = @_;
+    my $list = $self->_merge_list;
+    $list->{$args{ticket}} = $args{into};
+    $self->_merge_list($list);
+    return;
+}
+
 =head3 _encode_data
 
 Used to make sure data gets properly unicode'd for RT3.6.

commit e6ec2a681327ae829b2770da045f55bb6bdcc617
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:27:40 2007 +0000

    fix bug in date parsing to actually pass dates through
    * manually set Told since Ticket->Import|Create don't handle it

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index 69ddc17..fee2a61 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -310,11 +310,11 @@ sub create_ticket {
     # RT1 stored dates in "Seconds from the epoch" so we 
     # need to convert that to ISO so RT3 can grok it
     foreach my $type (qw(Due Told Created Updated)) {
-        if (defined $args{$type} && $args{type} =~ /^\d+$/) {
+        if (defined $args{$type} && $args{$type} =~ /^\d+$/) {
             my $date = new RT::Date($RT::SystemUser);
             $date->Set( Format => 'unix', Value => $args{$type} );
             $args{$type} = $date->ISO;
-        }
+        } 
     }
 
     if ($args{Area} && (my $area = delete $args{Area})) {
@@ -327,6 +327,11 @@ sub create_ticket {
     my ($val, $msg) = $ticket->Import(Requestor => \@requestors, %args); 
     die $msg unless $val;
 
+    if ($args{Told}) {
+        # Create/Import doesn't bubble Told up properly in some RT3s
+        $ticket->__Set( Field => 'Told', Value => $args{Told} );
+    }
+
     return $ticket;
 }
 

commit af3028b3791f4e080f1dc36fc610258933adeba8
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:27:47 2007 +0000

    include transactions in ticket data

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index ac3d4ac..7d55df3 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -282,6 +282,8 @@ sub get_queue_acl {
 Intended to be called in a loop.
 Wraps over the DBH iterator.  When called for the first time, 
 will fetch all tickets and return one as a hashref.  
+This hashref will also contain all of the transactions
+on a given ticket in a special transactions key.
 Will keep returning one until we run out.
 
 =cut
@@ -311,6 +313,16 @@ limit 100
 SQL
     my $ticket_data = $self->_fetch_data( name => 'Ticket', sql => $sql );
 
+    if ($ticket_data) {
+        my $sql = 'select * from transactions where serial_num = ? order by trans_date asc';
+        while (my $transaction = $self->_fetch_data( name => 'Transaction', 
+                                                     sql => $sql, 
+                                                     placeholders => [$ticket_data->{id}]) ) {
+            push @{$ticket_data->{transactions}},$transaction;
+        }
+
+    }
+
     return $ticket_data;
 }
 

commit 322d88e62b7cdc03bf70e8cbc63daacae4850154
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:27:53 2007 +0000

    create links (merged tickets) in RT3

diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index 0634103..e29611a 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -77,6 +77,6 @@ sub migrate_tickets {
         print "\n$ticket->{id}.";
         $rt3->create_ticket(%$ticket);
     }
-    #$rt3->create_links;
+    $rt3->create_links;
 }
 
diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index fee2a61..c95d2e8 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -335,6 +335,55 @@ sub create_ticket {
     return $ticket;
 }
 
+=head3 create_links 
+
+creates all accumulated links.
+We do this at the end so that all the tickets will exist, rather
+than doing it during ticket creation and having to work around
+future tickets not being imported yet.
+
+=cut
+
+sub create_links {
+    my $self = shift;
+
+    my $merges = $self->_merges;
+    
+    foreach my $ticket (keys %$merges) {
+        my $into = $merges->{$ticket};
+        if ($self->config->debug) {
+            print "\nMerging $ticket into $into" 
+        } else {
+            print ".";
+        }
+
+        my $mergeinto = RT::Ticket->new($RT::SystemUser);
+        $mergeinto->Load($into);
+
+        unless ($mergeinto->Id) {
+            print "Skipping $ticket => $into because $into doesn't exist";
+            next;
+        }
+
+        # Store the link in the DB.
+        my $link = RT::Link->new($RT::SystemUser);
+        my ($linkid) = $link->Create(Target => $into,
+                                     Base => $ticket, 
+                                     Type => 'MergedInto');
+        
+        my $ticket_obj = RT::Ticket->new($RT::SystemUser);
+        $ticket_obj->Load($ticket);
+        
+        if ($ticket_obj->id != $ticket) {
+            die "Ticket mismatch ".$ticket_obj->id ." and $ticket\n";
+        }
+        my ($val, $msg) = $ticket_obj->__Set( Field => 'EffectiveId', Value => $into );
+    
+        print " couldn't set EffectiveId: $msg\n" unless ($val);
+    }
+
+}
+
 =head3 _merge_list
 
 private data storage routine to hold what tickets are merged where
@@ -345,10 +394,18 @@ takes ticket => id, into => otherid
 tracks what merges need doing after we're done
 creating all the tickets.
 
+When called without arguments, returns a hashref
+containing ticketid => ticket to merge into
+
 =cut
 
 sub _merges {
     my $self = shift;
+
+    unless (@_) {
+        return $self->_merge_list;
+    } 
+
     my %args = @_;
     my $list = $self->_merge_list;
     $list->{$args{ticket}} = $args{into};

commit 68d29586801d0cca998d332e9dbe03d751714752
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:28:07 2007 +0000

    I should really be consisten.  Transactions, not transactions

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 7d55df3..d7d6869 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -283,7 +283,7 @@ Intended to be called in a loop.
 Wraps over the DBH iterator.  When called for the first time, 
 will fetch all tickets and return one as a hashref.  
 This hashref will also contain all of the transactions
-on a given ticket in a special transactions key.
+on a given ticket in a special Transactions key.
 Will keep returning one until we run out.
 
 =cut
@@ -318,7 +318,7 @@ SQL
         while (my $transaction = $self->_fetch_data( name => 'Transaction', 
                                                      sql => $sql, 
                                                      placeholders => [$ticket_data->{id}]) ) {
-            push @{$ticket_data->{transactions}},$transaction;
+            push @{$ticket_data->{Transactions}},$transaction;
         }
 
     }

commit 97cedb3978b944eedc45dbbd925dd522f5312026
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:28:14 2007 +0000

    add new requirements for the conversion

diff --git a/META.yml b/META.yml
index 2d4a6f4..aa69b14 100644
--- a/META.yml
+++ b/META.yml
@@ -10,6 +10,8 @@ no_index:
     - t
 requires: 
   Class::Accessor::Fast: 0
+  Date::Format: 0
+  MIME::Parser: 0
   RT: 3.6
   Test::More: 0
 version: 0.01
diff --git a/Makefile.PL b/Makefile.PL
index f691fa9..64a0ced 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -6,5 +6,7 @@ all_from('lib/RTx/Converter.pm');
 requires('Test::More');
 requires('Class::Accessor::Fast');
 requires('RT' => '3.6');
+requires('Date::Format');
+requires('MIME::Parser');
 
 &WriteAll;

commit 04eb2149b3a7c9e95fe314d13ff82e41abd20bc7
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:28:20 2007 +0000

    Create transactions while importing tickets

diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index e29611a..57640e0 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -75,8 +75,12 @@ sub migrate_queues {
 sub migrate_tickets {
     while (my $ticket = $rt1->get_ticket) {
         print "\n$ticket->{id}.";
-        $rt3->create_ticket(%$ticket);
+        my $tick_obj = $rt3->create_ticket(%$ticket);
+        my $transactions = $rt1->get_transactions($tick_obj->Id);
+        $rt3->create_transactions( Ticket => $tick_obj, Transactions => $transactions,
+                                   Path => $rt1->config->data_directory );
     }
+    print "\nCreating links\n\n";
     $rt3->create_links;
 }
 

commit 93dfd7d541addafc907a1f2a9f9cc156894a82a5
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:28:26 2007 +0000

    actually, we need to get transactions separately from the tickets

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index d7d6869..8af04c8 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -282,8 +282,6 @@ sub get_queue_acl {
 Intended to be called in a loop.
 Wraps over the DBH iterator.  When called for the first time, 
 will fetch all tickets and return one as a hashref.  
-This hashref will also contain all of the transactions
-on a given ticket in a special Transactions key.
 Will keep returning one until we run out.
 
 =cut
@@ -313,17 +311,31 @@ limit 100
 SQL
     my $ticket_data = $self->_fetch_data( name => 'Ticket', sql => $sql );
 
-    if ($ticket_data) {
-        my $sql = 'select * from transactions where serial_num = ? order by trans_date asc';
-        while (my $transaction = $self->_fetch_data( name => 'Transaction', 
-                                                     sql => $sql, 
-                                                     placeholders => [$ticket_data->{id}]) ) {
-            push @{$ticket_data->{Transactions}},$transaction;
-        }
+    return $ticket_data;
+}
 
-    }
+=head2 get_transactions 
 
-    return $ticket_data;
+Takes the ticketid passed in and returns an arrayref
+of transaction data.
+
+=cut
+
+sub get_transactions {
+    my $self = shift;
+    my $ticket_id = shift;
+    my $transactions;
+
+    my $sql = 'select * from transactions where serial_num = ? order by trans_date asc';
+    while (my $transaction = $self->_fetch_data( name => 'Transaction', 
+            sql => $sql, 
+            placeholders => [$ticket_id]) ) {
+        if ($transaction->{actor} && $transaction->{actor} !~ /\@/) {
+            $transaction->{actor} .= $self->config->email_domain;
+        }
+        push @$transaction,$transaction;
+    }
+    return $transactions;
 }
 
 =head1 AUTHOR

commit 0c5583166664913851ad417e88e25fd1e3c12658
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:29:57 2007 +0000

    initial port of the really tricky transaction translating code
      this stuff is gross, even after some cleanup, but what it is doing
      is a necesarily gross activity

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index c95d2e8..ed375b4 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -7,6 +7,9 @@ __PACKAGE__->mk_accessors(qw(config _merge_list));
 
 use RTx::Converter::RT3::Config;
 use Encode;
+use Date::Format;
+use MIME::Parser;
+use Carp;
 
 =head1 NAME
 
@@ -328,13 +331,349 @@ sub create_ticket {
     die $msg unless $val;
 
     if ($args{Told}) {
-        # Create/Import doesn't bubble Told up properly in some RT3s
+        # Create/Import doesn't bubble Told up properly in some RT3.6.3 and earlier
         $ticket->__Set( Field => 'Told', Value => $args{Told} );
     }
 
+    $ticket = $self->create_transactions( Ticket => $ticket,
+                                          Transactions => $args{Transactions} );
+
     return $ticket;
 }
 
+sub create_transactions {
+    my $self = shift;
+    my %args = @_;
+    my $ticket = $args{Ticket};
+    my $path = $args{Path};
+
+    my $Status = "open";
+    my $Queue = "(unknown)";
+    my $Area = '';
+    my $Subject = '';
+    my $Owner = $RT::Nobody->Id;
+    my $Priority = $ticket->InitialPriority();
+    my $FinalPriority = $ticket->Priority();
+
+    foreach my $txn (@{$args{Transactions}}) {
+        my (%trans_args, $MIMEObj);
+        
+        print "t$txn->{id}";
+        
+        my $load_content = 0;
+        $trans_args{'Type'} = '';
+        $trans_args{'Field'} = '';
+        
+        if ( ( $txn->{type} eq 'create' ) or ($txn->{type} eq 'import') ) {
+            $load_content = 1;
+            $trans_args{'Type'} = "Create";
+        } 
+        elsif ( $txn->{type} eq 'status' ) {
+            $trans_args{'Type'} = "Status";
+            $trans_args{'Field'} = "Status";
+            $trans_args{'OldValue'} = $Status;
+            $trans_args{'NewValue'} = $txn->{trans_data};
+            $Status = $txn->{trans_data};
+        } 
+        elsif ( $txn->{type} eq 'correspond' ) {
+            $load_content = 1;
+            $trans_args{'Type'} = "Correspond";
+        } 
+        elsif ( $txn->{type} eq 'comments' ) {
+            $load_content = 1;
+            $trans_args{'Type'} = "Comment";
+        } 
+        elsif ( $txn->{type} eq 'queue_id' ) {
+            $trans_args{'Type'} = "Set";
+            $trans_args{'Field'} = "Queue";
+            $trans_args{'OldValue'} = $Queue;
+            $trans_args{'NewValue'} = $txn->{trans_data};
+            $Queue = $txn->{trans_data};
+        } 
+        elsif ( $txn->{type} eq 'owner' ) {
+
+            $trans_args{'Type'} = "Owner";
+            $trans_args{'Field'} ="Owner";
+            $trans_args{'OldValue'} = $Owner;
+            $txn->{trans_data} ||= 'Nobody';
+            
+            my $new_user = new RT::User($RT::SystemUser);
+            $new_user->Load($txn->{'trans_data'});
+            $trans_args{'NewValue'} = $new_user->Id;
+            
+            my $actor = new RT::User($RT::SystemUser);
+            $txn->{actor} = 'RT_System' if ($txn->{actor} eq '_rt_system');
+            $actor->Load($txn->{actor});
+            
+            #take/give
+        
+            $Owner = $RT::Nobody->Id unless ($Owner);
+
+            if ($Owner == $RT::Nobody->Id &&
+                $txn->{trans_data} eq $txn->{actor} ) {
+                $trans_args{'Type'} = 'Take';
+            } elsif ( $Owner == $actor->Id  &&
+                      $new_user->Id == $RT::Nobody->Id ) {
+                $trans_args{'Type'} = 'Untake';
+            } elsif ( $Owner != $RT::Nobody->Id) {
+                $trans_args{'Type'} = 'Steal';
+            } else {
+                $trans_args{'Type'} = 'Give';
+            }        
+            
+            $Owner = $new_user->Id;
+            
+        } 
+        elsif ( $txn->{type} eq 'effective_sn' ) {
+            $trans_args{'Type'} = "AddLink";
+            $trans_args{'Field'} ="MemberOf";
+            $trans_args{'Data'} = "Ticket ". $ticket->Id.
+              " MergedInto ticket ". $txn->{trans_data};
+            
+        } 
+        elsif ( $txn->{type} eq 'area' ) {
+            $trans_args{'Type'} = "CustomField";
+            $trans_args{'OldValue'} = $Area;
+            $trans_args{'NewValue'} = $txn->{trans_data};
+            $Area = $txn->{trans_data};
+        } 
+        elsif ( $txn->{type} eq 'requestors' ) {
+            $trans_args{'Type'} = "AddWatcher";
+            $trans_args{'Field'} ="Requestor";
+            $trans_args{'NewValue'} = $txn->{trans_data};
+
+        } 
+        elsif ( $txn->{type} eq 'date_due' ) {
+            $trans_args{'Type'} = "Set";
+            $trans_args{'Field'} ="Due";
+            my $date = new RT::Date($RT::SystemUser);
+            $date->Set( Format=>'unix', Value=>$txn->{trans_data} );
+            $trans_args{'NewValue'} = $date->ISO();
+        } 
+        elsif ( $txn->{type} eq 'subject' ) {
+            $trans_args{'Type'} = "Set";
+            $trans_args{'Field'} ="Subject";
+            $trans_args{'OldValue'} = $Subject;
+            $trans_args{'NewValue'} = $txn->{trans_data};
+            $Subject = $txn->{trans_data};
+            
+        } 
+        elsif ( $txn->{type} eq 'priority' ) {
+            $trans_args{'Type'} = "Set";
+            $trans_args{'Field'} ="Priority";
+            $trans_args{'OldValue'} = $Priority;
+            $trans_args{'NewValue'} = $txn->{'trans_data'};
+            $Priority = $txn->{'trans_data'};
+            
+        } 
+        elsif ( $txn->{type} eq 'final_priority' ) {
+            $trans_args{'Type'} = "Set";
+            $trans_args{'Field'} ="FinalPriority";
+            $trans_args{'OldValue'} = $FinalPriority;
+            $trans_args{'NewValue'} = $txn->{'trans_data'};
+            $FinalPriority = $txn->{'trans_data'};
+            
+        } 
+        elsif ( $txn->{type} eq 'date_told' ) {
+            $trans_args{'Type'} = "Set";
+            $trans_args{'Field'} = "Told";
+            
+            my $date = new RT::Date($RT::SystemUser);
+            $date->Set( Format=>'unix', Value=>$txn->{trans_data} );
+            $trans_args{'NewValue'} = $date->ISO();
+            
+        } else {
+            die "unrecognized transaction type: $txn->{type}";
+        }
+
+        my $filename = $txn->{serial_num}.".".$txn->{id};
+        
+        if ( $load_content ) {
+            if (my $trans_file = $self->_find_transaction_file(Path => $args{Path}, 
+                                                               Date => $txn->{trans_date},
+                                                               Filename => $filename ) ) {
+                $MIMEObj = $self->_process_transaction_file(File => $trans_file);
+            }
+        }
+        
+        
+        if ( $trans_args{'Type'} ) {
+            
+            my $User = RT::User->new($RT::SystemUser);
+            
+            if ($txn->{actor}) {
+                    $User->Load( $txn->{actor} );
+                    unless ($User->Id) {
+                        $User->LoadByEmail($txn->{actor});
+                    }
+                    unless ($User->Id) {
+                        my ($val, $msg) = $User->Create(EmailAddress => $txn->{actor},
+                                                            Password => undef,
+                                                            Privileged => 0,
+                                                            Comments => undef
+                        );
+
+                        unless ($val) {
+                            die "couldn't create User for $txn->{actor}: $msg\n";
+                        }
+                    }
+            } else {
+                $User->Load($RT::Nobody->Id);
+            }
+
+            unless ($User->Id) {
+                carp "We couldn't find or create ".$txn->{actor}. ". This should never happen"
+            }
+            
+            my $created = new RT::Date($RT::SystemUser);
+            $created->Set( Format=>'unix', Value=>$txn->{'trans_date'});
+                
+            my $trans = new RT::Transaction($User);
+            
+            # Allow us to set the 'Created' attribute. 
+            $trans->{'_AccessibleCache'}{Created} = { 'read'=>1, 'write'=>1 };
+            $trans->{'_AccessibleCache'}{Creator} = { 'read'=>1, 'auto'=>1 };
+            
+            my ($transaction, $msg) = 
+              $trans->Create( Ticket => $ticket->Id,
+                              Type => $trans_args{'Type'},
+                              Data => $trans_args{'Data'},
+                              Field => $trans_args{'Field'},
+                              NewValue => $trans_args{'NewValue'},
+                              OldValue => $trans_args{'OldValue'},
+                              MIMEObj => $MIMEObj,
+                              Created => $created->ISO,
+                              ActivateScrips => 0
+                            );
+            
+            unless ($transaction) {
+                die("Couldn't create transaction for $txn->{id} $msg\n") 
+            }
+                
+            print "=".$trans->id if $self->config->debug;
+            print ".";
+        } else {
+            die "Couldn't parse ". $txn->{id};
+        }
+        
+    }
+}
+
+=head3 _find_transaction_file
+
+RT1 would sometimes get confused about timezones and store
+a file in tomorrow or yesterday's path.  Go find the file.
+
+=cut
+
+sub _find_transaction_file {
+    my $self = shift;
+    my %args = @_;
+
+    my @files;
+    foreach my $date ($args{Date},$args{Date}+43200,$args{Date}-43200) {
+
+        my $file = time2str("$args{Path}/%Y/%b/%e/",$date,'PST');
+        $file .= $args{Filename};
+        $file =~ s/ //;
+
+        print "Testing $file" if $self->config->debug;
+        if (-e $file) {
+            return $file
+        } else {
+            push @files,$file;
+        }
+    }
+    warn "none of @files exist\n";
+}
+
+=head3 _process_transaction_file
+
+We need to turn the RT1 files back into MIME objects
+This means converting the old Headers Follow line and
+the broken MIME headers into something MIME::Parser
+won't choke on.
+
+=cut
+
+sub _process_transaction_file {
+    my $self = shift;
+    my %args = @_;
+    my $trans_file = $args{Path};
+
+    print "processing file $trans_file\n" if $self->config->debug;
+            
+    open(FILE,"<$trans_file") or die $!;
+            
+            
+    my(@headers, @body);
+    my $headers = 0;
+    while (<FILE>) {
+        if ( /^--- Headers Follow ---$/ ) {
+            $headers = 1;
+            next;
+        } elsif ( $headers ) {
+            next if /^\s*$/;
+            next if /^>From /;
+            push @headers, $_;
+        } else {
+            push @body, $_;
+        }
+    }
+            
+    #clean up files with false multipart Content-type
+    my @n_headers;
+    while ( my $header = shift @headers ) {
+        if ( $header =~ /^content-type:\s*multipart\/(alternative|mixed|report|signed|digest|related)\s*;/i ) {
+            my $two = 0;
+            my $boundary;
+            if ( $header =~ /;\s*boundary=\s*"?([\-\w\.\=\/\+\%\#]+)"?/i ) {
+                $boundary = $1;
+            } elsif (( $header =~ /;\s*boundary=\s*$/i ) and  ($headers[0] =~ /\s*"?([\-\w\.\=\/\+\%\#]+)"?/i)) {
+                #special case for actual boundary on next line
+                $boundary = $1;
+                $two = 1;
+            } elsif ( $headers[0] =~ /(^|;)\s*boundary=\s*"([ \-\w\.\=\/\+\%\#]+)"/i ) { #embedded space, quotes not optional
+                $boundary = $2;
+                $two = 1;
+            } elsif ( $headers[0] =~ /(^|;)\s*boundary=\s*"?([\-\w\.\=\/\+\%\#]+)"?/i ) {
+                $boundary = $2;
+                $two = 1;
+            } elsif ( $headers[1] =~ /(^|;)\s*boundary=\s*"?([\-\w\.\=\/\+\%\#]+)"?/i ) {
+                $boundary = $2;
+                $two = 2;
+            } elsif ( $headers[2] =~ /(^|;)\s*boundary=\s*"?([\-\w\.\=\/\+\%\#]+)"?/i ) {
+                #terrible false laziness.
+                $boundary = $2;
+                $two = 3;
+            } else {
+                warn "can\'t parse $header for boundry";
+            }
+            print "looking for $boundary in body\n" if $self->config->debug;
+            unless ( grep /^(\-\-)?\Q$boundary\E(\-\-)?$/, @body ) {
+                splice(@headers, 0, $two);
+                until ( !scalar(@headers) || $headers[0] =~ /^\S/ ) {
+                    warn "**WARNING throwing away header fragment: ". shift @headers;
+                }
+                warn "false Content-type: header removed\n";
+                push @n_headers, "Content-Type: text/plain\n";
+                push @n_headers, "X-Content-Type-Munged-By: RT import tool\n";
+
+                next; #This is here so we don't push into n_headers
+            }
+        }
+        push @n_headers, $header;
+    }
+            
+    print "..parsing.." if $self->config->debug;
+    my $parser = new MIME::Parser;
+    $parser->output_to_core(1);
+    $parser->extract_nested_messages(0);
+    my $MIMEObj = $parser->parse_data( [ @n_headers, "\n", "\n", @body ] );
+    print "..parsed.." if $self->config->debug;
+    return $MIMEObj;
+} 
+
 =head3 create_links 
 
 creates all accumulated links.

commit 825479e62f4a838323c59faa6c4acaa196d11eed
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:32:04 2007 +0000

    fix some simple bugs that were breaking in testing

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 8af04c8..f134b2d 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -333,7 +333,7 @@ sub get_transactions {
         if ($transaction->{actor} && $transaction->{actor} !~ /\@/) {
             $transaction->{actor} .= $self->config->email_domain;
         }
-        push @$transaction,$transaction;
+        push @$transactions,$transaction;
     }
     return $transactions;
 }
diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index ed375b4..b34583b 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -335,9 +335,6 @@ sub create_ticket {
         $ticket->__Set( Field => 'Told', Value => $args{Told} );
     }
 
-    $ticket = $self->create_transactions( Ticket => $ticket,
-                                          Transactions => $args{Transactions} );
-
     return $ticket;
 }
 
@@ -577,7 +574,7 @@ sub _find_transaction_file {
         $file .= $args{Filename};
         $file =~ s/ //;
 
-        print "Testing $file" if $self->config->debug;
+        print "\nTesting $file" if $self->config->debug;
         if (-e $file) {
             return $file
         } else {
@@ -599,9 +596,9 @@ won't choke on.
 sub _process_transaction_file {
     my $self = shift;
     my %args = @_;
-    my $trans_file = $args{Path};
+    my $trans_file = $args{File};
 
-    print "processing file $trans_file\n" if $self->config->debug;
+    print "\nprocessing file $trans_file" if $self->config->debug;
             
     open(FILE,"<$trans_file") or die $!;
             

commit 3343c6332b066a6946688f3183aae7f3d95bb563
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:01 2007 +0000

    * remove some client specific bits
    * document where to find the options you can set

diff --git a/bin/rt1-to-rt3 b/bin/rt1-to-rt3
index 57640e0..d33640f 100644
--- a/bin/rt1-to-rt3
+++ b/bin/rt1-to-rt3
@@ -19,8 +19,7 @@ my $rt1 = RTx::Converter->new( type => 'RT1' );
 my $rt3 = RTx::Converter->new( type => 'RT3' );
 
 # should probably read a config file here
-$rt1->config->dbpassword('');
-$rt1->config->database('harvardrt1');
+# perldoc RTx::Converter::RT1::Config RTx::Converter::RT3::Config for options
 $rt1->config->debug(1);
 $rt3->config->debug(1);
 
@@ -74,7 +73,7 @@ sub migrate_queues {
 
 sub migrate_tickets {
     while (my $ticket = $rt1->get_ticket) {
-        print "\n$ticket->{id}.";
+        print "\n $ticket->{id}.";
         my $tick_obj = $rt3->create_ticket(%$ticket);
         my $transactions = $rt1->get_transactions($tick_obj->Id);
         $rt3->create_transactions( Ticket => $tick_obj, Transactions => $transactions,

commit 056740f215fdce65599fe8a3b85ca10842c96d54
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:06 2007 +0000

    * removing testing restriction

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index f134b2d..57f160d 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -307,7 +307,6 @@ select serial_num as id,
        queue_id as Queue,
        area as Area
 from each_req 
-limit 100
 SQL
     my $ticket_data = $self->_fetch_data( name => 'Ticket', sql => $sql );
 

commit f5b23e044a0d805661cf8a02e7d77c5b9a6e5875
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:16 2007 +0000

    * it helps when email addresses have @ in them

diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Converter/RT1.pm
index 57f160d..aefedeb 100644
--- a/lib/RTx/Converter/RT1.pm
+++ b/lib/RTx/Converter/RT1.pm
@@ -330,7 +330,7 @@ sub get_transactions {
             sql => $sql, 
             placeholders => [$ticket_id]) ) {
         if ($transaction->{actor} && $transaction->{actor} !~ /\@/) {
-            $transaction->{actor} .= $self->config->email_domain;
+            $transaction->{actor} .= '@'.$self->config->email_domain;
         }
         push @$transactions,$transaction;
     }

commit 4ecd1c2b64fb073d8b55eef53dd8a3cdcde11984
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:30 2007 +0000

    * clean up debugging output to be more useful

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index b34583b..ac45fe6 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -355,7 +355,7 @@ sub create_transactions {
     foreach my $txn (@{$args{Transactions}}) {
         my (%trans_args, $MIMEObj);
         
-        print "t$txn->{id}";
+        print "t";
         
         my $load_content = 0;
         $trans_args{'Type'} = '';
@@ -394,7 +394,7 @@ sub create_transactions {
             $trans_args{'OldValue'} = $Owner;
             $txn->{trans_data} ||= 'Nobody';
             
-            my $new_user = new RT::User($RT::SystemUser);
+            my $new_user = RT::User->new($RT::SystemUser);
             $new_user->Load($txn->{'trans_data'});
             $trans_args{'NewValue'} = $new_user->Id;
             
@@ -546,13 +546,9 @@ sub create_transactions {
             unless ($transaction) {
                 die("Couldn't create transaction for $txn->{id} $msg\n") 
             }
-                
-            print "=".$trans->id if $self->config->debug;
-            print ".";
         } else {
             die "Couldn't parse ". $txn->{id};
         }
-        
     }
 }
 
@@ -667,7 +663,7 @@ sub _process_transaction_file {
     $parser->output_to_core(1);
     $parser->extract_nested_messages(0);
     my $MIMEObj = $parser->parse_data( [ @n_headers, "\n", "\n", @body ] );
-    print "..parsed.." if $self->config->debug;
+    print "parsed.." if $self->config->debug;
     return $MIMEObj;
 } 
 

commit 2635abc43dbbdd404a3e781281237c7f6cdf0144
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:36 2007 +0000

    * give a useful error message if you can't find the transaction file

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index ac45fe6..7fa7639 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -596,7 +596,7 @@ sub _process_transaction_file {
 
     print "\nprocessing file $trans_file" if $self->config->debug;
             
-    open(FILE,"<$trans_file") or die $!;
+    open (FILE,"<$trans_file") or die "can't open [$trans_file] $!";
             
             
     my(@headers, @body);

commit be525753d87565c009c7c30e3df73d1c36d162c8
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:41 2007 +0000

    * explicit return so we don't try to open a nonexistant file

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index 7fa7639..ddc8ab1 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -578,6 +578,7 @@ sub _find_transaction_file {
         }
     }
     warn "none of @files exist\n";
+    return;
 }
 
 =head3 _process_transaction_file

commit 9ee5c356c6ddd8e33c2ac317baff26ffcead0bf1
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 16:34:49 2007 +0000

    * Turn emailaddresses from RT1 into user ids for RT3
    * Handle the DelWatcher case

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index ddc8ab1..b6d6eeb 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -349,6 +349,7 @@ sub create_transactions {
     my $Area = '';
     my $Subject = '';
     my $Owner = $RT::Nobody->Id;
+    my $Requestor = $RT::Nobody->Id;
     my $Priority = $ticket->InitialPriority();
     my $FinalPriority = $ticket->Priority();
 
@@ -435,10 +436,37 @@ sub create_transactions {
             $Area = $txn->{trans_data};
         } 
         elsif ( $txn->{type} eq 'requestors' ) {
-            $trans_args{'Type'} = "AddWatcher";
+            # RT1 removed requestors by recording a transaction with
+            # '' for trans_data.  For RT3 we need to say "DelWatcher" 
+            # AND tell RT which requestor we're nuking.
             $trans_args{'Field'} ="Requestor";
-            $trans_args{'NewValue'} = $txn->{trans_data};
 
+            if ($txn->{trans_data}) {
+                $trans_args{'Type'} = "AddWatcher";
+                # earlier RTs stored email addresses in the Transaction
+                # RT3 calls Load on that address and goes splody
+                # since Load only works on id/username
+                my $user = RT::User->new($RT::SystemUser);
+                $user->Load($txn->{trans_data});
+                unless ($user->Id) {
+                    $user->LoadByEmail($txn->{trans_data});
+                }
+                unless ($user->Id) {
+                    my ($val, $msg) = $user->Create(Name => $txn->{trans_data},
+                                                    EmailAddress => $txn->{trans_data},
+                                                    Password => undef,
+                                                    Privileged => 0,
+                                                    Comments => undef);
+                    unless ($val) {
+                        die "Can't create user for $txn->{trans_data}: $msg";
+                    }
+                }
+                $trans_args{NewValue} = $user->Id;
+                $Requestor = $user->Id;
+            } else {
+                $trans_args{Type} = "DelWatcher";
+                $trans_args{OldValue} = $Requestor;
+            }
         } 
         elsif ( $txn->{type} eq 'date_due' ) {
             $trans_args{'Type'} = "Set";

commit aa4707da554bc306f92e6ffacdf11fcc146f0f9e
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 17:03:19 2007 +0000

    * document create_transactions

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index b6d6eeb..d90caab 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -338,6 +338,13 @@ sub create_ticket {
     return $ticket;
 }
 
+=head3 create_transactions
+
+takes Path => /path/to/transaction/file, Ticket => RT::Ticket, 
+Transactions => [arrayref of transaction data]
+
+=cut
+
 sub create_transactions {
     my $self = shift;
     my %args = @_;

commit e611ffde4a3e835c4476edab5908b57b71d1ec7a
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 17:03:22 2007 +0000

    * add new files for packaging

diff --git a/MANIFEST b/MANIFEST
index 08b6c5a..f860168 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,10 +1,23 @@
+bin/rt1-to-rt3
 Changes
-MANIFEST
-META.yml # Will be created by "make dist"
-Makefile.PL
-README
+inc/Module/Install.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
 lib/RTx/Converter.pm
+lib/RTx/Converter/Config.pm
 lib/RTx/Converter/RT1.pm
+lib/RTx/Converter/RT1/Config.pm
+lib/RTx/Converter/RT3.pm
+lib/RTx/Converter/RT3/Config.pm
+Makefile.PL
+MANIFEST			This list of files
+META.yml
+README
 t/00.load.t
 t/99.pod-coverage.t
 t/99.pod.t

commit f3d764063300b98cbead123d2992f526e0308524
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 17:03:25 2007 +0000

    * refactor user loading logic

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index d90caab..d5c9d47 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -453,21 +453,7 @@ sub create_transactions {
                 # earlier RTs stored email addresses in the Transaction
                 # RT3 calls Load on that address and goes splody
                 # since Load only works on id/username
-                my $user = RT::User->new($RT::SystemUser);
-                $user->Load($txn->{trans_data});
-                unless ($user->Id) {
-                    $user->LoadByEmail($txn->{trans_data});
-                }
-                unless ($user->Id) {
-                    my ($val, $msg) = $user->Create(Name => $txn->{trans_data},
-                                                    EmailAddress => $txn->{trans_data},
-                                                    Password => undef,
-                                                    Privileged => 0,
-                                                    Comments => undef);
-                    unless ($val) {
-                        die "Can't create user for $txn->{trans_data}: $msg";
-                    }
-                }
+                my $user = $self->_load_or_create_user(EmailAddress => $txn->{trans_data});
                 $trans_args{NewValue} = $user->Id;
                 $Requestor = $user->Id;
             } else {
@@ -531,32 +517,13 @@ sub create_transactions {
         
         if ( $trans_args{'Type'} ) {
             
-            my $User = RT::User->new($RT::SystemUser);
-            
+            my $User;
             if ($txn->{actor}) {
-                    $User->Load( $txn->{actor} );
-                    unless ($User->Id) {
-                        $User->LoadByEmail($txn->{actor});
-                    }
-                    unless ($User->Id) {
-                        my ($val, $msg) = $User->Create(EmailAddress => $txn->{actor},
-                                                            Password => undef,
-                                                            Privileged => 0,
-                                                            Comments => undef
-                        );
-
-                        unless ($val) {
-                            die "couldn't create User for $txn->{actor}: $msg\n";
-                        }
-                    }
+               $User = $self->_load_or_create_user(EmailAddress => $txn->{actor});
             } else {
+                $User = RT::User->new($RT::System);
                 $User->Load($RT::Nobody->Id);
             }
-
-            unless ($User->Id) {
-                carp "We couldn't find or create ".$txn->{actor}. ". This should never happen"
-            }
-            
             my $created = new RT::Date($RT::SystemUser);
             $created->Set( Format=>'unix', Value=>$txn->{'trans_date'});
                 
@@ -585,6 +552,7 @@ sub create_transactions {
             die "Couldn't parse ". $txn->{id};
         }
     }
+    return $ticket;
 }
 
 =head3 _find_transaction_file
@@ -703,6 +671,47 @@ sub _process_transaction_file {
     return $MIMEObj;
 } 
 
+=head3 _load_or_create_user
+
+Given an EmailAddress, Name (username)
+will try to load the user by username first and
+then by EmailAddress.  If that fails, a new unprivileged 
+user will be created with Name => Name|EmailAddress
+
+Will carp if loading AND creating fail
+Otherwise returns a valid user object
+
+=cut
+
+sub _load_or_create_user {
+    my $self = shift;
+    my %args = @_;
+    $args{Name} ||= $args{EmailAddress};
+
+    my $user_obj = RT::user_obj->new($RT::Systemuser_obj);
+
+    $user_obj->Load( $args{Name} );
+    unless ($user_obj->Id) {
+        $user_obj->LoadByEmail($args{EmailAddress});
+    }
+    unless ($user_obj->Id) {
+        my ($val, $msg) = $user_obj->Create(%args,
+                                            Password => undef,
+                                            Privileged => 0,
+                                            Comments => undef
+        );
+
+        unless ($val) {
+            die "couldn't create user_obj for %args{Name}: $msg\n";
+        }
+    }
+
+    unless ($user_obj->Id) {
+        carp "We couldn't find or create $args{Name}. This should never happen"
+    }
+    return $user_obj;
+}
+            
 =head3 create_links 
 
 creates all accumulated links.

commit d1686f845316f9d73c9e1927929477c9f5209962
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:18 2007 +0000

    * fix a :%s/// bug

diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Converter/RT3.pm
index d5c9d47..ab61bb4 100644
--- a/lib/RTx/Converter/RT3.pm
+++ b/lib/RTx/Converter/RT3.pm
@@ -688,7 +688,7 @@ sub _load_or_create_user {
     my %args = @_;
     $args{Name} ||= $args{EmailAddress};
 
-    my $user_obj = RT::user_obj->new($RT::Systemuser_obj);
+    my $user_obj = RT::User->new($RT::SystemUser);
 
     $user_obj->Load( $args{Name} );
     unless ($user_obj->Id) {

commit c6b997ef322b661b9a698f7171336c77d4fd9f24
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:29 2007 +0000

    * version bump

diff --git a/lib/RTx/Converter.pm b/lib/RTx/Converter.pm
index ff081e4..37e7f86 100644
--- a/lib/RTx/Converter.pm
+++ b/lib/RTx/Converter.pm
@@ -1,6 +1,6 @@
 package RTx::Converter;
 
-our $VERSION = '0.01';
+our $VERSION = '0.02';
 
 use warnings;
 use strict;

commit 38a8747f6e737ec7c4d90ffa339f2a7fef798054
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:32 2007 +0000

    * ignore build detritus

commit 528ff39d92f29e452e32104a81fd56296e13282a
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:35 2007 +0000

    * change namespaces from RTx to RT-Extension

diff --git a/lib/RTx/Converter.pm b/lib/RTx/Extension/Converter.pm
similarity index 100%
rename from lib/RTx/Converter.pm
rename to lib/RTx/Extension/Converter.pm
diff --git a/lib/RTx/Converter/Config.pm b/lib/RTx/Extension/Converter/Config.pm
similarity index 100%
rename from lib/RTx/Converter/Config.pm
rename to lib/RTx/Extension/Converter/Config.pm
diff --git a/lib/RTx/Converter/RT1.pm b/lib/RTx/Extension/Converter/RT1.pm
similarity index 100%
rename from lib/RTx/Converter/RT1.pm
rename to lib/RTx/Extension/Converter/RT1.pm
diff --git a/lib/RTx/Converter/RT1/Config.pm b/lib/RTx/Extension/Converter/RT1/Config.pm
similarity index 100%
rename from lib/RTx/Converter/RT1/Config.pm
rename to lib/RTx/Extension/Converter/RT1/Config.pm
diff --git a/lib/RTx/Converter/RT3.pm b/lib/RTx/Extension/Converter/RT3.pm
similarity index 100%
rename from lib/RTx/Converter/RT3.pm
rename to lib/RTx/Extension/Converter/RT3.pm
diff --git a/lib/RTx/Converter/RT3/Config.pm b/lib/RTx/Extension/Converter/RT3/Config.pm
similarity index 100%
rename from lib/RTx/Converter/RT3/Config.pm
rename to lib/RTx/Extension/Converter/RT3/Config.pm

commit be24968f6199a32eb997f58fd9df680418e05502
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:38 2007 +0000

    * rename from RTx to RT-Extension

diff --git a/lib/RTx/Extension/Converter.pm b/lib/RT/Extension/Converter.pm
similarity index 100%
rename from lib/RTx/Extension/Converter.pm
rename to lib/RT/Extension/Converter.pm
diff --git a/lib/RTx/Extension/Converter/Config.pm b/lib/RT/Extension/Converter/Config.pm
similarity index 100%
rename from lib/RTx/Extension/Converter/Config.pm
rename to lib/RT/Extension/Converter/Config.pm
diff --git a/lib/RTx/Extension/Converter/RT1.pm b/lib/RT/Extension/Converter/RT1.pm
similarity index 100%
rename from lib/RTx/Extension/Converter/RT1.pm
rename to lib/RT/Extension/Converter/RT1.pm
diff --git a/lib/RTx/Extension/Converter/RT1/Config.pm b/lib/RT/Extension/Converter/RT1/Config.pm
similarity index 100%
rename from lib/RTx/Extension/Converter/RT1/Config.pm
rename to lib/RT/Extension/Converter/RT1/Config.pm
diff --git a/lib/RTx/Extension/Converter/RT3.pm b/lib/RT/Extension/Converter/RT3.pm
similarity index 100%
rename from lib/RTx/Extension/Converter/RT3.pm
rename to lib/RT/Extension/Converter/RT3.pm
diff --git a/lib/RTx/Extension/Converter/RT3/Config.pm b/lib/RT/Extension/Converter/RT3/Config.pm
similarity index 100%
rename from lib/RTx/Extension/Converter/RT3/Config.pm
rename to lib/RT/Extension/Converter/RT3/Config.pm

commit 16c8cc8a0b664ec8b576092b292e47729111e742
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:42 2007 +0000

    * change package names for the RTx->RT-Extension move

diff --git a/lib/RT/Extension/Converter.pm b/lib/RT/Extension/Converter.pm
index 37e7f86..5453602 100644
--- a/lib/RT/Extension/Converter.pm
+++ b/lib/RT/Extension/Converter.pm
@@ -1,4 +1,4 @@
-package RTx::Converter;
+package RT::Extension::Converter;
 
 our $VERSION = '0.02';
 
@@ -9,14 +9,14 @@ use Carp;
 
 =head1 NAME
 
-RTx::Converter - base class for rtX-to-rt3 scripts
+RT::Extension::Converter - base class for rtX-to-rt3 scripts
 
 
 =head1 SYNOPSIS
 
-    use RTx::Converter;
-    my $rt1converter = RTx::Converter->new( type => 'rt1' );
-    my $rt3converter = RTx::Converter->new( type => 'rt3' );
+    use RT::Extension::Converter;
+    my $rt1converter = RT::Extension::Converter->new( type => 'rt1' );
+    my $rt3converter = RT::Extension::Converter->new( type => 'rt3' );
 
     foreach my $user ($rt1converter->users) {
         $rt3converter->add_user( $user );
diff --git a/lib/RT/Extension/Converter/Config.pm b/lib/RT/Extension/Converter/Config.pm
index de6e9ff..bf0a563 100644
--- a/lib/RT/Extension/Converter/Config.pm
+++ b/lib/RT/Extension/Converter/Config.pm
@@ -1,4 +1,4 @@
-package RTx::Converter::Config;
+package RT::Extension::Converter::Config;
 
 use warnings;
 use strict;
@@ -8,23 +8,23 @@ __PACKAGE__->mk_accessors(qw(debug));
 
 =head1 NAME
 
-RTx::Converter::Config - config data for any importers
+RT::Extension::Converter::Config - config data for any importers
 
 
 =head1 SYNOPSIS
 
-Generally accessed from an RTx::Converter object
+Generally accessed from an RT::Extension::Converter object
 
- use RTx::Converter;
- my $converter = RTx::Convert->new;
+ use RT::Extension::Converter;
+ my $converter = RT::Extension::Convert->new;
  warn "debugging info" if $converter->config->debug;
   
 =head1 DESCRIPTION
 
 Methods that we expect any converter to have access to.
 
-Config data that is specific to the RTx converters should
-go in RTx::Converter::RTx::Config.
+Config data that is specific to the RT::Extension converters should
+go in RT::Extension::Converter::RTx::Config.
 
 =head1 METHODS
 
diff --git a/lib/RT/Extension/Converter/RT1.pm b/lib/RT/Extension/Converter/RT1.pm
index aefedeb..fc940ff 100644
--- a/lib/RT/Extension/Converter/RT1.pm
+++ b/lib/RT/Extension/Converter/RT1.pm
@@ -1,22 +1,22 @@
-package RTx::Converter::RT1;
+package RT::Extension::Converter::RT1;
 
 use warnings;
 use strict;
 use base qw(Class::Accessor::Fast);
 __PACKAGE__->mk_accessors(qw(config _handle ));
 
-use RTx::Converter::RT1::Config;
+use RT::Extension::Converter::RT1::Config;
 use DBI;
 
 =head1 NAME
 
-RTx::Converter::RT1 - Handle the RT1 side of a conversion
+RT::Extension::Converter::RT1 - Handle the RT1 side of a conversion
 
 
 =head1 SYNOPSIS
 
-    use RTx::Converter::RT1;
-    my $converter = RTx::Converter::RT1->new;
+    use RT::Extension::Converter::RT1;
+    my $converter = RT::Extension::Converter::RT1->new;
 
 =head1 DESCRIPTION
 
@@ -34,7 +34,7 @@ sub new {
     my $class = shift;
 
     my $self = $class->SUPER::new(@_);
-    $self->config(RTx::Converter::RT1::Config->new);
+    $self->config(RT::Extension::Converter::RT1::Config->new);
     return $self;
 }
 
diff --git a/lib/RT/Extension/Converter/RT1/Config.pm b/lib/RT/Extension/Converter/RT1/Config.pm
index 6317c82..50b2556 100644
--- a/lib/RT/Extension/Converter/RT1/Config.pm
+++ b/lib/RT/Extension/Converter/RT1/Config.pm
@@ -1,5 +1,5 @@
-package RTx::Converter::RT1::Config;
-use base qw/RTx::Converter::Config/;
+package RT::Extension::Converter::RT1::Config;
+use base qw/RT::Extension::Converter::Config/;
 use warnings;
 use strict;
 
@@ -8,12 +8,12 @@ __PACKAGE__->mk_accessors(qw(dbuser dbpassword database dbhost data_directory
 
 =head1 NAME
 
-RTx::Converter::RT1::Config - config data for the RT1 importer
+RT::Extension::Converter::RT1::Config - config data for the RT1 importer
 
 
 =head1 SYNOPSIS
 
-    use RTx::Converter::RT1::Config;
+    use RT::Extension::Converter::RT1::Config;
     
 Usually retrieved from a converter object with
 
diff --git a/lib/RT/Extension/Converter/RT3.pm b/lib/RT/Extension/Converter/RT3.pm
index ab61bb4..1be5533 100644
--- a/lib/RT/Extension/Converter/RT3.pm
+++ b/lib/RT/Extension/Converter/RT3.pm
@@ -1,11 +1,11 @@
-package RTx::Converter::RT3;
+package RT::Extension::Converter::RT3;
 
 use warnings;
 use strict;
 use base qw(Class::Accessor::Fast);
 __PACKAGE__->mk_accessors(qw(config _merge_list));
 
-use RTx::Converter::RT3::Config;
+use RT::Extension::Converter::RT3::Config;
 use Encode;
 use Date::Format;
 use MIME::Parser;
@@ -13,13 +13,13 @@ use Carp;
 
 =head1 NAME
 
-RTx::Converter::RT3 - Handle the RT3 side of a conversion
+RT::Extension::Converter::RT3 - Handle the RT3 side of a conversion
 
 
 =head1 SYNOPSIS
 
-    use RTx::Converter::RT3;
-    my $converter = RTx::Converter::RT3->new;
+    use RT::Extension::Converter::RT3;
+    my $converter = RT::Extension::Converter::RT3->new;
 
 =head1 DESCRIPTION
 
@@ -37,7 +37,7 @@ sub new {
     my $class = shift;
 
     my $self = $class->SUPER::new(@_);
-    $self->config(RTx::Converter::RT3::Config->new);
+    $self->config(RT::Extension::Converter::RT3::Config->new);
     return $self;
 }
 
diff --git a/lib/RT/Extension/Converter/RT3/Config.pm b/lib/RT/Extension/Converter/RT3/Config.pm
index ae0bd90..3e8fcba 100644
--- a/lib/RT/Extension/Converter/RT3/Config.pm
+++ b/lib/RT/Extension/Converter/RT3/Config.pm
@@ -1,5 +1,5 @@
-package RTx::Converter::RT3::Config;
-use base qw/RTx::Converter::Config/;
+package RT::Extension::Converter::RT3::Config;
+use base qw/RT::Extension::Converter::Config/;
 use warnings;
 use strict;
 
@@ -7,12 +7,12 @@ __PACKAGE__->mk_accessors(qw(default_queue encoding));
 
 =head1 NAME
 
-RTx::Converter::RT3::Config - config data for the RT3 importer
+RT::Extension::Converter::RT3::Config - config data for the RT3 importer
 
 
 =head1 SYNOPSIS
 
-    use RTx::Converter::RT1::Config;
+    use RT::Extension::Converter::RT1::Config;
     
 Usually retrieved from a converter object with
 

commit 24a61536da018f03dcd4533416e1f0fdf99cdc22
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:45 2007 +0000

    * rename RTx to RT-Extension

diff --git a/t/00.load.t b/t/00.load.t
index 8db7c1a..11bab06 100644
--- a/t/00.load.t
+++ b/t/00.load.t
@@ -1,12 +1,12 @@
 use Test::More tests => 6;
 
 BEGIN {
-use_ok( 'RTx::Converter' );
-use_ok( 'RTx::Converter::Config' );
-use_ok( 'RTx::Converter::RT1' );
-use_ok( 'RTx::Converter::RT1::Config' );
-use_ok( 'RTx::Converter::RT3' );
-use_ok( 'RTx::Converter::RT3::Config' );
+use_ok( 'RT::Extension::Converter' );
+use_ok( 'RT::Extension::Converter::Config' );
+use_ok( 'RT::Extension::Converter::RT1' );
+use_ok( 'RT::Extension::Converter::RT1::Config' );
+use_ok( 'RT::Extension::Converter::RT3' );
+use_ok( 'RT::Extension::Converter::RT3::Config' );
 }
 
-diag( "Testing RTx::Converter $RTx::Converter::VERSION" );
+diag( "Testing RT::Extension::Converter $RTx::Converter::VERSION" );

commit c57501bcd572cb1f4dea1bd1820dbfe3721e2581
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:23:58 2007 +0000

    * rename RTx to RT-Extension and bump version

diff --git a/Changes b/Changes
index 494c720..94eb0ac 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,7 @@
-Revision history for RTx-Converter
+Revision history for RT-Extension-Converter
 
-0.01  Thu Jan 18 13:09:25 2007
-       Initial release.
+0.03  Rename from RTx to RT-Extension
 
+0.02  Countless bugfixes
+
+0.01  Initial release.
diff --git a/MANIFEST b/MANIFEST
index f860168..89f0982 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -8,12 +8,12 @@ inc/Module/Install/Makefile.pm
 inc/Module/Install/Metadata.pm
 inc/Module/Install/Win32.pm
 inc/Module/Install/WriteAll.pm
-lib/RTx/Converter.pm
-lib/RTx/Converter/Config.pm
-lib/RTx/Converter/RT1.pm
-lib/RTx/Converter/RT1/Config.pm
-lib/RTx/Converter/RT3.pm
-lib/RTx/Converter/RT3/Config.pm
+lib/RT/Extension/Converter.pm
+lib/RT/Extension/Converter/Config.pm
+lib/RT/Extension/Converter/RT1.pm
+lib/RT/Extension/Converter/RT1/Config.pm
+lib/RT/Extension/Converter/RT3.pm
+lib/RT/Extension/Converter/RT3/Config.pm
 Makefile.PL
 MANIFEST			This list of files
 META.yml
diff --git a/META.yml b/META.yml
index aa69b14..8328b3a 100644
--- a/META.yml
+++ b/META.yml
@@ -3,7 +3,7 @@ author: Kevin Falcone  C<< <falcone at bestpractical.com> >>
 distribution_type: module
 generated_by: Module::Install version 0.64
 license: perl
-name: RTx-Converter
+name: RT-Extension-Converter
 no_index: 
   directory: 
     - inc
@@ -14,4 +14,4 @@ requires:
   MIME::Parser: 0
   RT: 3.6
   Test::More: 0
-version: 0.01
+version: 0.03
diff --git a/Makefile.PL b/Makefile.PL
index 64a0ced..f1f5e99 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,8 +1,8 @@
 use inc::Module::Install;
 
-name ('RTx-Converter');
+name ('RT-Extension-Converter');
 abstract('Convert RT1 installations to RT3');
-all_from('lib/RTx/Converter.pm');
+all_from('lib/RT/Extension/Converter.pm');
 requires('Test::More');
 requires('Class::Accessor::Fast');
 requires('RT' => '3.6');
diff --git a/lib/RT/Extension/Converter.pm b/lib/RT/Extension/Converter.pm
index 5453602..5d21f7b 100644
--- a/lib/RT/Extension/Converter.pm
+++ b/lib/RT/Extension/Converter.pm
@@ -1,6 +1,6 @@
 package RT::Extension::Converter;
 
-our $VERSION = '0.02';
+our $VERSION = '0.03';
 
 use warnings;
 use strict;

commit 097af7a38b447cbb524fbc154f06af66d1ba646d
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Wed Apr 4 23:24:02 2007 +0000

    * basic tests of object creation and config.

diff --git a/t/basic.t b/t/basic.t
new file mode 100644
index 0000000..ab902ac
--- /dev/null
+++ b/t/basic.t
@@ -0,0 +1,20 @@
+use RT::Extension::Converter;
+use Test::More tests => 7;
+
+use lib 't/lib';
+
+my $rt1 = RT::Extension::Converter->new( type => 'RT1' );
+isa_ok($rt1,"RT::Extension::Converter::RT1");
+isa_ok($rt1->config,"RT::Extension::Converter::RT1::Config");
+
+# check some defaults
+# check that we can blank out the password
+is($rt1->config->dbuser,'root');
+is($rt1->config->dbpassword,'password');
+$rt1->config->dbpassword('');
+is($rt1->config->dbpassword,'');
+
+
+my $rt3 = RT::Extension::Converter->new( type => 'RT3' );
+isa_ok($rt1,"RT::Extension::Converter::RT1");
+isa_ok($rt1->config,"RT::Extension::Converter::RT1::Config");

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list