starting to work...
[freeside.git] / rt / lib / RT / Handle.pm
index 46070ce..bb19aa9 100644 (file)
@@ -1,40 +1,40 @@
 # BEGIN BPS TAGGED BLOCK {{{
 # BEGIN BPS TAGGED BLOCK {{{
-# 
+#
 # COPYRIGHT:
 # COPYRIGHT:
-# 
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-#                                          <jesse@bestpractical.com>
-# 
+#
+# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+#                                          <sales@bestpractical.com>
+#
 # (Except where explicitly superseded by other copyright notices)
 # (Except where explicitly superseded by other copyright notices)
-# 
-# 
+#
+#
 # LICENSE:
 # LICENSE:
-# 
+#
 # This work is made available to you under the terms of Version 2 of
 # the GNU General Public License. A copy of that license should have
 # been provided with this software, but in any event can be snarfed
 # from www.gnu.org.
 # This work is made available to you under the terms of Version 2 of
 # the GNU General Public License. A copy of that license should have
 # been provided with this software, but in any event can be snarfed
 # from www.gnu.org.
-# 
+#
 # This work is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # General Public License for more details.
 # This work is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 or visit their web page on the internet at
 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 or visit their web page on the internet at
 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-# 
-# 
+#
+#
 # CONTRIBUTION SUBMISSION POLICY:
 # CONTRIBUTION SUBMISSION POLICY:
-# 
+#
 # (The following paragraph is not intended to limit the rights granted
 # to you to modify and distribute this software under the terms of
 # the GNU General Public License and is only of importance to you if
 # you choose to contribute your changes and enhancements to the
 # community by submitting them to Best Practical Solutions, LLC.)
 # (The following paragraph is not intended to limit the rights granted
 # to you to modify and distribute this software under the terms of
 # the GNU General Public License and is only of importance to you if
 # you choose to contribute your changes and enhancements to the
 # community by submitting them to Best Practical Solutions, LLC.)
-# 
+#
 # By intentionally submitting any modifications, corrections or
 # derivatives to this work, or any other work intended for use with
 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
 # By intentionally submitting any modifications, corrections or
 # derivatives to this work, or any other work intended for use with
 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
@@ -43,7 +43,7 @@
 # royalty-free, perpetual, license to use, copy, create derivative
 # works based on those contributions, and sublicense and distribute
 # those contributions and any derivatives thereof.
 # royalty-free, perpetual, license to use, copy, create derivative
 # works based on those contributions, and sublicense and distribute
 # those contributions and any derivatives thereof.
-# 
+#
 # END BPS TAGGED BLOCK }}}
 
 =head1 NAME
 # END BPS TAGGED BLOCK }}}
 
 =head1 NAME
@@ -70,7 +70,8 @@ package RT::Handle;
 
 use strict;
 use warnings;
 
 use strict;
 use warnings;
-use vars qw/@ISA/;
+
+use File::Spec;
 
 =head1 METHODS
 
 
 =head1 METHODS
 
@@ -82,8 +83,9 @@ L<DBIx::SearchBuilder::Handle>, using the C<DatabaseType> configuration.
 =cut
 
 sub FinalizeDatabaseType {
 =cut
 
 sub FinalizeDatabaseType {
-    eval "use DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .";
-    \@ISA= qw(DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .");";
+    eval {
+        use base "DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType');
+    };
 
     if ($@) {
         die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
 
     if ($@) {
         die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
@@ -101,6 +103,7 @@ Takes nothing.
 
 sub Connect {
     my $self = shift;
 
 sub Connect {
     my $self = shift;
+    my %args = (@_);
 
     my $db_type = RT->Config->Get('DatabaseType');
     if ( $db_type eq 'Oracle' ) {
 
     my $db_type = RT->Config->Get('DatabaseType');
     if ( $db_type eq 'Oracle' ) {
@@ -111,6 +114,7 @@ sub Connect {
     $self->SUPER::Connect(
         User => RT->Config->Get('DatabaseUser'),
         Password => RT->Config->Get('DatabasePassword'),
     $self->SUPER::Connect(
         User => RT->Config->Get('DatabaseUser'),
         Password => RT->Config->Get('DatabasePassword'),
+        %args,
     );
 
     if ( $db_type eq 'mysql' ) {
     );
 
     if ( $db_type eq 'mysql' ) {
@@ -119,6 +123,15 @@ sub Connect {
         $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
     }
 
         $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
     }
 
+
+    if ( $db_type eq 'Pg' ) {
+        my $version = $self->DatabaseVersion;
+        ($version) = $version =~ /^(\d+\.\d+)/;
+        $self->dbh->do("SET bytea_output = 'escape'") if $version >= 9.0;
+    }
+
+
+
     $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
 }
 
     $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
 }
 
@@ -129,7 +142,6 @@ from the config.
 
 =cut
 
 
 =cut
 
-require File::Spec;
 
 sub BuildDSN {
     my $self = shift;
 
 sub BuildDSN {
     my $self = shift;
@@ -201,10 +213,6 @@ sub SystemDSN {
         # with postgres, you want to connect to template1 database
         $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
     }
         # with postgres, you want to connect to template1 database
         $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
     }
-    elsif ( $db_type eq 'Informix' ) {
-        # with Informix, you want to connect sans database:
-        $dsn =~ s/Informix:\Q$db_name/Informix:/;
-    }
     return $dsn;
 }
 
     return $dsn;
 }
 
@@ -216,36 +224,31 @@ sub SystemDSN {
 
 sub CheckIntegrity {
     my $self = shift;
 
 sub CheckIntegrity {
     my $self = shift;
-    
-    my $dsn = $self->DSN;
-    my $user = RT->Config->Get('DatabaseUser');
-    my $pass = RT->Config->Get('DatabasePassword');
+    $self = new $self unless ref $self;
 
 
-    my $dbh = DBI->connect(
-        $dsn, $user, $pass,
-        { RaiseError => 0, PrintError => 0 },
-    );
-    unless ( $dbh ) {
-        return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr);
-    }
+    do {
+        local $@;
+        unless ( eval { RT::ConnectToDatabase(); 1 } ) {
+            return (0, 'no connection', "$@");
+        }
+    };
 
 
-    RT::ConnectToDatabase();
     RT::InitLogging();
 
     require RT::CurrentUser;
     RT::InitLogging();
 
     require RT::CurrentUser;
-    my $test_user = new RT::CurrentUser;
+    my $test_user = RT::CurrentUser->new;
     $test_user->Load('RT_System');
     unless ( $test_user->id ) {
     $test_user->Load('RT_System');
     unless ( $test_user->id ) {
-        return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'");
+        return (0, 'no system user', "Couldn't find RT_System user in the DB '". $self->DSN ."'");
     }
 
     }
 
-    $test_user = new RT::CurrentUser;
+    $test_user = RT::CurrentUser->new;
     $test_user->Load('Nobody');
     unless ( $test_user->id ) {
     $test_user->Load('Nobody');
     unless ( $test_user->id ) {
-        return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'");
+        return (0, 'no nobody user', "Couldn't find Nobody user in the DB '". $self->DSN ."'");
     }
 
     }
 
-    return $dbh;
+    return $RT::Handle->dbh;
 }
 
 sub CheckCompatibility {
 }
 
 sub CheckCompatibility {
@@ -291,6 +294,21 @@ sub CheckCompatibility {
     return (1)
 }
 
     return (1)
 }
 
+sub CheckSphinxSE {
+    my $self = shift;
+
+    my $dbh = $RT::Handle->dbh;
+    local $dbh->{'RaiseError'} = 0;
+    local $dbh->{'PrintError'} = 0;
+    my $has = ($dbh->selectrow_array("show variables like 'have_sphinx'"))[1];
+    $has ||= ($dbh->selectrow_array(
+        "select 'yes' from INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME = 'sphinx' AND PLUGIN_STATUS='active'"
+    ))[0];
+
+    return 0 unless lc($has||'') eq "yes";
+    return 1;
+}
+
 =head2 Database maintanance
 
 =head3 CreateDatabase $DBH
 =head2 Database maintanance
 
 =head3 CreateDatabase $DBH
@@ -336,14 +354,7 @@ sub CreateDatabase {
         return (1, "Created user $db_user. All RT's objects should be in his schema.");
     }
     elsif ( $db_type eq 'Pg' ) {
         return (1, "Created user $db_user. All RT's objects should be in his schema.");
     }
     elsif ( $db_type eq 'Pg' ) {
-        # XXX: as we get external DBH we don't know if RaiseError or PrintError
-        # are enabled, so we have to setup it here and restore them back
-        $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0")
-            || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0");
-    }
-    elsif ( $db_type eq 'Informix' ) {
-        local $ENV{'DB_LOCALE'} = 'en_us.utf8';
-        $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG");
+        $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0");
     }
     else {
         $status = $dbh->do("CREATE DATABASE $db_name");
     }
     else {
         $status = $dbh->do("CREATE DATABASE $db_name");
@@ -351,15 +362,15 @@ sub CreateDatabase {
     return ($status, $DBI::errstr);
 }
 
     return ($status, $DBI::errstr);
 }
 
-=head3 DropDatabase $DBH [Force => 0]
+=head3 DropDatabase $DBH
 
 Drops RT's database. This method can be used as class method.
 
 Takes DBI handle as first argument. Many database systems require
 
 Drops RT's database. This method can be used as class method.
 
 Takes DBI handle as first argument. Many database systems require
-special handle to allow you to create a new database, so you have
-to use L<SystemDSN> method during connection.
+a special handle to allow you to drop a database, so you may have
+to use L<SystemDSN> when acquiring the DBI handle.
 
 
-Fetches type and name of the DB from the config.
+Fetches the type and name of the database from the config.
 
 =cut
 
 
 =cut
 
@@ -370,7 +381,7 @@ sub DropDatabase {
     my $db_type = RT->Config->Get('DatabaseType');
     my $db_name = RT->Config->Get('DatabaseName');
 
     my $db_type = RT->Config->Get('DatabaseType');
     my $db_name = RT->Config->Get('DatabaseName');
 
-    if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) {
+    if ( $db_type eq 'Oracle' ) {
         my $db_user = RT->Config->Get('DatabaseUser');
         my $status = $dbh->do( "DROP USER $db_user CASCADE" );
         unless ( $status ) {
         my $db_user = RT->Config->Get('DatabaseUser');
         my $status = $dbh->do( "DROP USER $db_user CASCADE" );
         unless ( $status ) {
@@ -465,10 +476,10 @@ sub InsertSchema {
 
     my (@schema);
 
 
     my (@schema);
 
-    open my $fh_schema, "<$file";
+    open( my $fh_schema, '<', $file ) or die $!;
 
     my $has_local = 0;
 
     my $has_local = 0;
-    open my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type )
+    open( my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type ))
         and $has_local = 1;
 
     my $statement = "";
         and $has_local = 1;
 
     my $statement = "";
@@ -547,18 +558,29 @@ sub GetVersionFile {
     return defined $version? $version{ $version } : undef;
 }
 
     return defined $version? $version{ $version } : undef;
 }
 
+{ my %word = (
+    a     => -4,
+    alpha => -4,
+    b     => -3,
+    beta  => -3,
+    pre   => -2,
+    rc    => -1,
+    head  => 9999,
+);
 sub cmp_version($$) {
     my ($a, $b) = (@_);
 sub cmp_version($$) {
     my ($a, $b) = (@_);
-    $b =~ s/HEAD$/9999/;
-    my @a = split /[^0-9]+/, $a;
-    my @b = split /[^0-9]+/, $b;
+    my @a = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef }
+        split /([^0-9]+)/, $a;
+    my @b = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef }
+        split /([^0-9]+)/, $b;
+    @a > @b
+        ? push @b, (0) x (@a-@b)
+        : push @a, (0) x (@b-@a);
     for ( my $i = 0; $i < @a; $i++ ) {
     for ( my $i = 0; $i < @a; $i++ ) {
-        return 1 unless defined $b[$i];
         return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
     }
         return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
     }
-    return 0 if @a == @b;
-    return -1;
-}
+    return 0;
+}}
 
 
 =head2 InsertInitialData
 
 
 =head2 InsertInitialData
@@ -583,13 +605,13 @@ sub InsertInitialData {
     {
         require RT::CurrentUser;
 
     {
         require RT::CurrentUser;
 
-        my $test_user = RT::User->new( new RT::CurrentUser );
+        my $test_user = RT::User->new( RT::CurrentUser->new() );
         $test_user->Load('RT_System');
         if ( $test_user->id ) {
             push @warns, "Found system user in the DB.";
         }
         else {
         $test_user->Load('RT_System');
         if ( $test_user->id ) {
             push @warns, "Found system user in the DB.";
         }
         else {
-            my $user = RT::User->new( new RT::CurrentUser );
+            my $user = RT::User->new( RT::CurrentUser->new() );
             my ( $val, $msg ) = $user->_BootstrapCreate(
                 Name     => 'RT_System',
                 RealName => 'The RT System itself',
             my ( $val, $msg ) = $user->_BootstrapCreate(
                 Name     => 'RT_System',
                 RealName => 'The RT System itself',
@@ -605,15 +627,15 @@ sub InsertInitialData {
 
     # init RT::SystemUser and RT::System objects
     RT::InitSystemObjects();
 
     # init RT::SystemUser and RT::System objects
     RT::InitSystemObjects();
-    unless ( $RT::SystemUser->id ) {
+    unless ( RT->SystemUser->id ) {
         return (0, "Couldn't load system user");
     }
 
     # grant SuperUser right to system user
     {
         return (0, "Couldn't load system user");
     }
 
     # grant SuperUser right to system user
     {
-        my $test_ace = RT::ACE->new( $RT::SystemUser );
+        my $test_ace = RT::ACE->new( RT->SystemUser );
         $test_ace->LoadByCols(
         $test_ace->LoadByCols(
-            PrincipalId   => ACLEquivGroupId( $RT::SystemUser->Id ),
+            PrincipalId   => ACLEquivGroupId( RT->SystemUser->Id ),
             PrincipalType => 'Group',
             RightName     => 'SuperUser',
             ObjectType    => 'RT::System',
             PrincipalType => 'Group',
             RightName     => 'SuperUser',
             ObjectType    => 'RT::System',
@@ -622,9 +644,9 @@ sub InsertInitialData {
         if ( $test_ace->id ) {
             push @warns, "System user has global SuperUser right.";
         } else {
         if ( $test_ace->id ) {
             push @warns, "System user has global SuperUser right.";
         } else {
-            my $ace = RT::ACE->new( $RT::SystemUser );
+            my $ace = RT::ACE->new( RT->SystemUser );
             my ( $val, $msg ) = $ace->_BootstrapCreate(
             my ( $val, $msg ) = $ace->_BootstrapCreate(
-                PrincipalId   => ACLEquivGroupId( $RT::SystemUser->Id ),
+                PrincipalId   => ACLEquivGroupId( RT->SystemUser->Id ),
                 PrincipalType => 'Group',
                 RightName     => 'SuperUser',
                 ObjectType    => 'RT::System',
                 PrincipalType => 'Group',
                 RightName     => 'SuperUser',
                 ObjectType    => 'RT::System',
@@ -640,14 +662,14 @@ sub InsertInitialData {
     # $self->loc('Privileged'); # For the string extractor to get a string to localize
     # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
     foreach my $name (qw(Everyone Privileged Unprivileged)) {
     # $self->loc('Privileged'); # For the string extractor to get a string to localize
     # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
     foreach my $name (qw(Everyone Privileged Unprivileged)) {
-        my $group = RT::Group->new( $RT::SystemUser );
+        my $group = RT::Group->new( RT->SystemUser );
         $group->LoadSystemInternalGroup( $name );
         if ( $group->id ) {
             push @warns, "System group '$name' already exists.";
             next;
         }
 
         $group->LoadSystemInternalGroup( $name );
         if ( $group->id ) {
             push @warns, "System group '$name' already exists.";
             next;
         }
 
-        $group = RT::Group->new( $RT::SystemUser );
+        $group = RT::Group->new( RT->SystemUser );
         my ( $val, $msg ) = $group->_Create(
             Type        => $name,
             Domain      => 'SystemInternal',
         my ( $val, $msg ) = $group->_Create(
             Type        => $name,
             Domain      => 'SystemInternal',
@@ -660,7 +682,7 @@ sub InsertInitialData {
 
     # nobody
     {
 
     # nobody
     {
-        my $user = RT::User->new( $RT::SystemUser );
+        my $user = RT::User->new( RT->SystemUser );
         $user->Load('Nobody');
         if ( $user->id ) {
             push @warns, "Found 'Nobody' user in the DB.";
         $user->Load('Nobody');
         if ( $user->id ) {
             push @warns, "Found 'Nobody' user in the DB.";
@@ -692,14 +714,14 @@ sub InsertInitialData {
 
     # system role groups
     foreach my $name (qw(Owner Requestor Cc AdminCc)) {
 
     # system role groups
     foreach my $name (qw(Owner Requestor Cc AdminCc)) {
-        my $group = RT::Group->new( $RT::SystemUser );
+        my $group = RT::Group->new( RT->SystemUser );
         $group->LoadSystemRoleGroup( $name );
         if ( $group->id ) {
             push @warns, "System role '$name' already exists.";
             next;
         }
 
         $group->LoadSystemRoleGroup( $name );
         if ( $group->id ) {
             push @warns, "System role '$name' already exists.";
             next;
         }
 
-        $group = RT::Group->new( $RT::SystemUser );
+        $group = RT::Group->new( RT->SystemUser );
         my ( $val, $msg ) = $group->_Create(
             Type        => $name,
             Domain      => 'RT::System-Role',
         my ( $val, $msg ) = $group->_Create(
             Type        => $name,
             Domain      => 'RT::System-Role',
@@ -725,6 +747,7 @@ Load some sort of data into the database, takes path to a file.
 sub InsertData {
     my $self     = shift;
     my $datafile = shift;
 sub InsertData {
     my $self     = shift;
     my $datafile = shift;
+    my $root_password = shift;
 
     # Slurp in stuff to insert from the datafile. Possible things to go in here:-
     our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
 
     # Slurp in stuff to insert from the datafile. Possible things to go in here:-
     our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
@@ -748,7 +771,7 @@ sub InsertData {
     if ( @Groups ) {
         $RT::Logger->debug("Creating groups...");
         foreach my $item (@Groups) {
     if ( @Groups ) {
         $RT::Logger->debug("Creating groups...");
         foreach my $item (@Groups) {
-            my $new_entry = RT::Group->new( $RT::SystemUser );
+            my $new_entry = RT::Group->new( RT->SystemUser );
             my $member_of = delete $item->{'MemberOf'};
             my ( $return, $msg ) = $new_entry->_Create(%$item);
             unless ( $return ) {
             my $member_of = delete $item->{'MemberOf'};
             my ( $return, $msg ) = $new_entry->_Create(%$item);
             unless ( $return ) {
@@ -760,7 +783,7 @@ sub InsertData {
             if ( $member_of ) {
                 $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
                 foreach( @$member_of ) {
             if ( $member_of ) {
                 $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
                 foreach( @$member_of ) {
-                    my $parent = RT::Group->new($RT::SystemUser);
+                    my $parent = RT::Group->new(RT->SystemUser);
                     if ( ref $_ eq 'HASH' ) {
                         $parent->LoadByCols( %$_ );
                     }
                     if ( ref $_ eq 'HASH' ) {
                         $parent->LoadByCols( %$_ );
                     }
@@ -794,7 +817,10 @@ sub InsertData {
     if ( @Users ) {
         $RT::Logger->debug("Creating users...");
         foreach my $item (@Users) {
     if ( @Users ) {
         $RT::Logger->debug("Creating users...");
         foreach my $item (@Users) {
-            my $new_entry = new RT::User( $RT::SystemUser );
+            if ( $item->{'Name'} eq 'root' && $root_password ) {
+                $item->{'Password'} = $root_password;
+            }
+            my $new_entry = RT::User->new( RT->SystemUser );
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
@@ -807,7 +833,7 @@ sub InsertData {
     if ( @Queues ) {
         $RT::Logger->debug("Creating queues...");
         for my $item (@Queues) {
     if ( @Queues ) {
         $RT::Logger->debug("Creating queues...");
         for my $item (@Queues) {
-            my $new_entry = new RT::Queue($RT::SystemUser);
+            my $new_entry = RT::Queue->new(RT->SystemUser);
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
@@ -820,13 +846,13 @@ sub InsertData {
     if ( @CustomFields ) {
         $RT::Logger->debug("Creating custom fields...");
         for my $item ( @CustomFields ) {
     if ( @CustomFields ) {
         $RT::Logger->debug("Creating custom fields...");
         for my $item ( @CustomFields ) {
-            my $new_entry = new RT::CustomField( $RT::SystemUser );
+            my $new_entry = RT::CustomField->new( RT->SystemUser );
             my $values    = delete $item->{'Values'};
 
             my @queues;
             # if ref then it's list of queues, so we do things ourself
             if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
             my $values    = delete $item->{'Values'};
 
             my @queues;
             # if ref then it's list of queues, so we do things ourself
             if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
-                $item->{'LookupType'} = 'RT::Queue-RT::Ticket';
+                $item->{'LookupType'} ||= 'RT::Queue-RT::Ticket';
                 @queues = @{ delete $item->{'Queue'} };
             }
 
                 @queues = @{ delete $item->{'Queue'} };
             }
 
@@ -836,6 +862,22 @@ sub InsertData {
                 next;
             }
 
                 next;
             }
 
+            if ( $item->{'BasedOn'} ) {
+                my $basedon = RT::CustomField->new($RT::SystemUser);
+                my ($ok, $msg ) = $basedon->LoadByCols( Name => $item->{'BasedOn'},
+                                                        LookupType => $new_entry->LookupType );
+                if ($ok) {
+                    ($ok, $msg) = $new_entry->SetBasedOn( $basedon );
+                    if ($ok) {
+                        $RT::Logger->debug("Added BasedOn $item->{BasedOn}: $msg");
+                    } else {
+                        $RT::Logger->error("Failed to add basedOn $item->{BasedOn}: $msg");
+                    }
+                } else {
+                    $RT::Logger->error("Unable to load $item->{BasedOn} as a $item->{LookupType} CF.  Skipping BasedOn");
+                }
+            }
+
             foreach my $value ( @{$values} ) {
                 my ( $return, $msg ) = $new_entry->AddValue(%$value);
                 $RT::Logger->error( $msg ) unless $return;
             foreach my $value ( @{$values} ) {
                 my ( $return, $msg ) = $new_entry->AddValue(%$value);
                 $RT::Logger->error( $msg ) unless $return;
@@ -843,18 +885,18 @@ sub InsertData {
 
             # apply by default
             if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
 
             # apply by default
             if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
-                my $ocf = RT::ObjectCustomField->new($RT::SystemUser);
+                my $ocf = RT::ObjectCustomField->new(RT->SystemUser);
                 $ocf->Create( CustomField => $new_entry->Id );
             }
 
             for my $q (@queues) {
                 $ocf->Create( CustomField => $new_entry->Id );
             }
 
             for my $q (@queues) {
-                my $q_obj = RT::Queue->new($RT::SystemUser);
+                my $q_obj = RT::Queue->new(RT->SystemUser);
                 $q_obj->Load($q);
                 unless ( $q_obj->Id ) {
                     $RT::Logger->error("Could not find queue ". $q );
                     next;
                 }
                 $q_obj->Load($q);
                 unless ( $q_obj->Id ) {
                     $RT::Logger->error("Could not find queue ". $q );
                     next;
                 }
-                my $OCF = RT::ObjectCustomField->new($RT::SystemUser);
+                my $OCF = RT::ObjectCustomField->new(RT->SystemUser);
                 ( $return, $msg ) = $OCF->Create(
                     CustomField => $new_entry->Id,
                     ObjectId    => $q_obj->Id,
                 ( $return, $msg ) = $OCF->Create(
                     CustomField => $new_entry->Id,
                     ObjectId    => $q_obj->Id,
@@ -873,12 +915,12 @@ sub InsertData {
 
             # Global rights or Queue rights?
             if ( $item->{'CF'} ) {
 
             # Global rights or Queue rights?
             if ( $item->{'CF'} ) {
-                $object = RT::CustomField->new( $RT::SystemUser );
+                $object = RT::CustomField->new( RT->SystemUser );
                 my @columns = ( Name => $item->{'CF'} );
                 push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
                 $object->LoadByName( @columns );
             } elsif ( $item->{'Queue'} ) {
                 my @columns = ( Name => $item->{'CF'} );
                 push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
                 $object->LoadByName( @columns );
             } elsif ( $item->{'Queue'} ) {
-                $object = RT::Queue->new($RT::SystemUser);
+                $object = RT::Queue->new(RT->SystemUser);
                 $object->Load( $item->{'Queue'} );
             } else {
                 $object = $RT::System;
                 $object->Load( $item->{'Queue'} );
             } else {
                 $object = $RT::System;
@@ -888,7 +930,7 @@ sub InsertData {
 
             # Group rights or user rights?
             if ( $item->{'GroupDomain'} ) {
 
             # Group rights or user rights?
             if ( $item->{'GroupDomain'} ) {
-                $princ = RT::Group->new($RT::SystemUser);
+                $princ = RT::Group->new(RT->SystemUser);
                 if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
                   $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
                 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
                 if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
                   $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
                 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
@@ -903,9 +945,17 @@ sub InsertData {
                 } else {
                   $princ->Load( $item->{'GroupId'} );
                 }
                 } else {
                   $princ->Load( $item->{'GroupId'} );
                 }
+                unless ( $princ->Id ) {
+                    RT->Logger->error("Unable to load Group: GroupDomain => $item->{GroupDomain}, GroupId => $item->{GroupId}, Queue => $item->{Queue}");
+                    next;
+                }
             } else {
             } else {
-                $princ = RT::User->new($RT::SystemUser);
-                $princ->Load( $item->{'UserId'} );
+                $princ = RT::User->new(RT->SystemUser);
+                my ($ok, $msg) = $princ->Load( $item->{'UserId'} );
+                unless ( $ok ) {
+                    RT->Logger->error("Unable to load user: $item->{UserId} : $msg");
+                    next;
+                }
             }
 
             # Grant it
             }
 
             # Grant it
@@ -927,7 +977,7 @@ sub InsertData {
         $RT::Logger->debug("Creating ScripActions...");
 
         for my $item (@ScripActions) {
         $RT::Logger->debug("Creating ScripActions...");
 
         for my $item (@ScripActions) {
-            my $new_entry = RT::ScripAction->new($RT::SystemUser);
+            my $new_entry = RT::ScripAction->new(RT->SystemUser);
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
@@ -944,7 +994,7 @@ sub InsertData {
         $RT::Logger->debug("Creating ScripConditions...");
 
         for my $item (@ScripConditions) {
         $RT::Logger->debug("Creating ScripConditions...");
 
         for my $item (@ScripConditions) {
-            my $new_entry = RT::ScripCondition->new($RT::SystemUser);
+            my $new_entry = RT::ScripCondition->new(RT->SystemUser);
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
@@ -961,7 +1011,7 @@ sub InsertData {
         $RT::Logger->debug("Creating templates...");
 
         for my $item (@Templates) {
         $RT::Logger->debug("Creating templates...");
 
         for my $item (@Templates) {
-            my $new_entry = new RT::Template($RT::SystemUser);
+            my $new_entry = RT::Template->new(RT->SystemUser);
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
             my ( $return, $msg ) = $new_entry->Create(%$item);
             unless ( $return ) {
                 $RT::Logger->error( $msg );
@@ -976,7 +1026,7 @@ sub InsertData {
         $RT::Logger->debug("Creating scrips...");
 
         for my $item (@Scrips) {
         $RT::Logger->debug("Creating scrips...");
 
         for my $item (@Scrips) {
-            my $new_entry = new RT::Scrip($RT::SystemUser);
+            my $new_entry = RT::Scrip->new(RT->SystemUser);
 
             my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
             push @queues, 0 unless @queues; # add global queue at least
 
             my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
             push @queues, 0 unless @queues; # add global queue at least
@@ -994,8 +1044,8 @@ sub InsertData {
         $RT::Logger->debug("done.");
     }
     if ( @Attributes ) {
         $RT::Logger->debug("done.");
     }
     if ( @Attributes ) {
-        $RT::Logger->debug("Creating predefined searches...");
-        my $sys = RT::System->new($RT::SystemUser);
+        $RT::Logger->debug("Creating attributes...");
+        my $sys = RT::System->new(RT->SystemUser);
 
         for my $item (@Attributes) {
             my $obj = delete $item->{Object}; # XXX: make this something loadable
 
         for my $item (@Attributes) {
             my $obj = delete $item->{Object}; # XXX: make this something loadable
@@ -1041,10 +1091,10 @@ Given a userid, return that user's acl equivalence group
 sub ACLEquivGroupId {
     my $id = shift;
 
 sub ACLEquivGroupId {
     my $id = shift;
 
-    my $cu = $RT::SystemUser;
+    my $cu = RT->SystemUser;
     unless ( $cu ) {
         require RT::CurrentUser;
     unless ( $cu ) {
         require RT::CurrentUser;
-        $cu = new RT::CurrentUser;
+        $cu = RT::CurrentUser->new;
         $cu->LoadByName('RT_System');
         warn "Couldn't load RT_System user" unless $cu->id;
     }
         $cu->LoadByName('RT_System');
         warn "Couldn't load RT_System user" unless $cu->id;
     }
@@ -1054,11 +1104,84 @@ sub ACLEquivGroupId {
     return $equiv_group->Id;
 }
 
     return $equiv_group->Id;
 }
 
+=head2 QueryHistory
+
+Returns the SQL query history associated with this handle. The top level array
+represents a lists of request. Each request is a hash with metadata about the
+request (such as the URL) and a list of queries. You'll probably not be using this.
+
+=cut
+
+sub QueryHistory {
+    my $self = shift;
+
+    return $self->{QueryHistory};
+}
+
+=head2 AddRequestToHistory
+
+Adds a web request to the query history. It must be a hash with keys Path (a
+string) and Queries (an array reference of arrays, where elements are time,
+sql, bind parameters, and duration).
+
+=cut
+
+sub AddRequestToHistory {
+    my $self    = shift;
+    my $request = shift;
+
+    push @{ $self->{QueryHistory} }, $request;
+}
+
+=head2 Quote
+
+Returns the parameter quoted by DBI. B<You almost certainly do not need this.>
+Use bind parameters (C<?>) instead. This is used only outside the scope of interacting
+with the database.
+
+=cut
+
+sub Quote {
+    my $self = shift;
+    my $value = shift;
+
+    return $self->dbh->quote($value);
+}
+
+=head2 FillIn
+
+Takes a SQL query and an array reference of bind parameters and fills in the
+query's C<?> parameters.
+
+=cut
+
+sub FillIn {
+    my $self = shift;
+    my $sql  = shift;
+    my $bind = shift;
+
+    my $b = 0;
+
+    # is this regex sufficient?
+    $sql =~ s{\?}{$self->Quote($bind->[$b++])}eg;
+
+    return $sql;
+}
+
+# log a mason stack trace instead of a Carp::longmess because it's less painful
+# and uses mason component paths properly
+sub _LogSQLStatement {
+    my $self = shift;
+    my $statement = shift;
+    my $duration = shift;
+    my @bind = @_;
+
+    require HTML::Mason::Exceptions;
+    push @{$self->{'StatementLog'}} , ([Time::HiRes::time(), $statement, [@bind], $duration, HTML::Mason::Exception->new->as_string]);
+}
+
 __PACKAGE__->FinalizeDatabaseType;
 
 __PACKAGE__->FinalizeDatabaseType;
 
-eval "require RT::Handle_Vendor";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm});
-eval "require RT::Handle_Local";
-die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Local.pm});
+RT::Base->_ImportOverlays();
 
 1;
 
 1;