1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
51 RT::Handle - RT's database handle
56 BEGIN { RT::LoadConfig() };
61 C<RT::Handle> is RT specific wrapper over one of L<DBIx::SearchBuilder::Handle>
62 classes. As RT works with different types of DBs we subclass repsective handler
63 from L<DBIx::SerachBuilder>. Type of the DB is defined by C<DatabasseType> RT's
64 config option. You B<must> load this module only when the configs have been
77 =head2 FinalizeDatabaseType
79 Sets RT::Handle's superclass to the correct subclass of
80 L<DBIx::SearchBuilder::Handle>, using the C<DatabaseType> configuration.
84 sub FinalizeDatabaseType {
85 eval "use DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .";
86 \@ISA= qw(DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .");";
89 die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
90 "Perhaps you've picked an invalid database type or spelled it incorrectly.\n".
97 Connects to RT's database using credentials and options from the RT config.
105 my $db_type = RT->Config->Get('DatabaseType');
106 if ( $db_type eq 'Oracle' ) {
107 $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
108 $ENV{'NLS_NCHAR'} = "AL32UTF8";
111 $self->SUPER::Connect(
112 User => RT->Config->Get('DatabaseUser'),
113 Password => RT->Config->Get('DatabasePassword'),
116 if ( $db_type eq 'mysql' ) {
117 my $version = $self->DatabaseVersion;
118 ($version) = $version =~ /^(\d+\.\d+)/;
119 $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
122 $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
127 Build the DSN for the RT database. Doesn't take any parameters, draws all that
136 # Unless the database port is a positive integer, we really don't want to pass it.
137 my $db_port = RT->Config->Get('DatabasePort');
138 $db_port = undef unless (defined $db_port && $db_port =~ /^(\d+)$/);
139 my $db_host = RT->Config->Get('DatabaseHost');
140 $db_host = undef unless $db_host;
141 my $db_name = RT->Config->Get('DatabaseName');
142 my $db_type = RT->Config->Get('DatabaseType');
143 $db_name = File::Spec->catfile($RT::VarPath, $db_name)
144 if $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name);
148 Database => $db_name,
151 RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
152 DisconnectHandleOnDestroy => 1,
154 if ( $db_type eq 'Oracle' && $db_host ) {
155 $args{'SID'} = delete $args{'Database'};
157 $self->SUPER::BuildDSN( %args );
162 Returns the DSN for this handle. In order to get correct value you must
163 build DSN first, see L</BuildDSN>.
165 This is method can be called as class method, in this case creates
166 temporary handle object, L</BuildDSN builds DSN> and returns it.
172 return $self->SUPER::DSN if ref $self;
174 my $handle = $self->new;
181 Returns a DSN suitable for database creates and drops
182 and user creates and drops.
184 Gets RT's DSN first (see L<DSN>) and then change it according
185 to requirements of a database system RT's using.
192 my $db_name = RT->Config->Get('DatabaseName');
193 my $db_type = RT->Config->Get('DatabaseType');
195 my $dsn = $self->DSN;
196 if ( $db_type eq 'mysql' ) {
197 # with mysql, you want to connect sans database to funge things
198 $dsn =~ s/dbname=\Q$db_name//;
200 elsif ( $db_type eq 'Pg' ) {
201 # with postgres, you want to connect to template1 database
202 $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
204 elsif ( $db_type eq 'Informix' ) {
205 # with Informix, you want to connect sans database:
206 $dsn =~ s/Informix:\Q$db_name/Informix:/;
211 =head2 Database compatibility and integrity checks
220 my $dsn = $self->DSN;
221 my $user = RT->Config->Get('DatabaseUser');
222 my $pass = RT->Config->Get('DatabasePassword');
224 my $dbh = DBI->connect(
226 { RaiseError => 0, PrintError => 0 },
229 return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr);
232 RT::ConnectToDatabase();
235 require RT::CurrentUser;
236 my $test_user = new RT::CurrentUser;
237 $test_user->Load('RT_System');
238 unless ( $test_user->id ) {
239 return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'");
242 $test_user = new RT::CurrentUser;
243 $test_user->Load('Nobody');
244 unless ( $test_user->id ) {
245 return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'");
251 sub CheckCompatibility {
254 my $state = shift || 'post';
256 my $db_type = RT->Config->Get('DatabaseType');
257 if ( $db_type eq "mysql" ) {
258 # Check which version we're running
259 my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
260 return (0, "couldn't get version of the mysql server")
263 ($version) = $version =~ /^(\d+\.\d+)/;
264 return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
267 # MySQL must have InnoDB support
268 my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
269 if ( lc $innodb eq "no" ) {
270 return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
271 "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
272 } elsif ( lc $innodb eq "disabled" ) {
273 return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
274 "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
277 if ( $state eq 'post' ) {
278 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Tickets")->[1];
279 unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
280 return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
283 if ( $version >= 4.1 && $state eq 'post' ) {
284 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
285 unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
286 return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
287 ."Follow instructions in the UPGRADING.mysql file.");
294 =head2 Database maintanance
296 =head3 CreateDatabase $DBH
298 Creates a new database. This method can be used as class method.
300 Takes DBI handle. Many database systems require special handle to
301 allow you to create a new database, so you have to use L<SystemDSN>
302 method during connection.
304 Fetches type and name of the DB from the config.
310 my $dbh = shift or return (0, "No DBI handle provided");
311 my $db_type = RT->Config->Get('DatabaseType');
312 my $db_name = RT->Config->Get('DatabaseName');
315 if ( $db_type eq 'SQLite' ) {
316 return (1, 'Skipped as SQLite doesn\'t need any action');
318 elsif ( $db_type eq 'Oracle' ) {
319 my $db_user = RT->Config->Get('DatabaseUser');
320 my $db_pass = RT->Config->Get('DatabasePassword');
322 "CREATE USER $db_user IDENTIFIED BY $db_pass"
323 ." default tablespace USERS"
324 ." temporary tablespace TEMP"
325 ." quota unlimited on USERS"
328 return $status, "Couldn't create user $db_user identified by $db_pass."
329 ."\nError: ". $dbh->errstr;
331 $status = $dbh->do( "GRANT connect, resource TO $db_user" );
333 return $status, "Couldn't grant connect and resource to $db_user."
334 ."\nError: ". $dbh->errstr;
336 return (1, "Created user $db_user. All RT's objects should be in his schema.");
338 elsif ( $db_type eq 'Pg' ) {
339 # XXX: as we get external DBH we don't know if RaiseError or PrintError
340 # are enabled, so we have to setup it here and restore them back
341 $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0")
342 || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0");
344 elsif ( $db_type eq 'Informix' ) {
345 local $ENV{'DB_LOCALE'} = 'en_us.utf8';
346 $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG");
349 $status = $dbh->do("CREATE DATABASE $db_name");
351 return ($status, $DBI::errstr);
354 =head3 DropDatabase $DBH [Force => 0]
356 Drops RT's database. This method can be used as class method.
358 Takes DBI handle as first argument. Many database systems require
359 special handle to allow you to create a new database, so you have
360 to use L<SystemDSN> method during connection.
362 Fetches type and name of the DB from the config.
368 my $dbh = shift or return (0, "No DBI handle provided");
370 my $db_type = RT->Config->Get('DatabaseType');
371 my $db_name = RT->Config->Get('DatabaseName');
373 if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) {
374 my $db_user = RT->Config->Get('DatabaseUser');
375 my $status = $dbh->do( "DROP USER $db_user CASCADE" );
377 return 0, "Couldn't drop user $db_user."
378 ."\nError: ". $dbh->errstr;
380 return (1, "Successfully dropped user '$db_user' with his schema.");
382 elsif ( $db_type eq 'SQLite' ) {
384 $path = "$RT::VarPath/$path" unless substr($path, 0, 1) eq '/';
385 unlink $path or return (0, "Couldn't remove '$path': $!");
388 $dbh->do("DROP DATABASE ". $db_name)
389 or return (0, $DBI::errstr);
401 my $base_path = shift || $RT::EtcPath;
403 my $db_type = RT->Config->Get('DatabaseType');
404 return (1) if $db_type eq 'SQLite';
406 $dbh = $self->dbh if !$dbh && ref $self;
407 return (0, "No DBI handle provided") unless $dbh;
409 return (0, "'$base_path' doesn't exist") unless -e $base_path;
412 if ( -d $base_path ) {
413 $path = File::Spec->catfile( $base_path, "acl.$db_type");
414 $path = $self->GetVersionFile($dbh, $path);
416 $path = File::Spec->catfile( $base_path, "acl")
417 unless $path && -e $path;
418 return (0, "Couldn't find ACLs for $db_type")
425 do $path || return (0, "Couldn't load ACLs: " . $@);
427 foreach my $statement (@acl) {
428 my $sth = $dbh->prepare($statement)
429 or return (0, "Couldn't prepare SQL query:\n $statement\n\nERROR: ". $dbh->errstr);
430 unless ( $sth->execute ) {
431 return (0, "Couldn't run SQL query:\n $statement\n\nERROR: ". $sth->errstr);
444 my $base_path = (shift || $RT::EtcPath);
446 $dbh = $self->dbh if !$dbh && ref $self;
447 return (0, "No DBI handle provided") unless $dbh;
449 my $db_type = RT->Config->Get('DatabaseType');
452 if ( -d $base_path ) {
453 $file = $base_path . "/schema." . $db_type;
458 $file = $self->GetVersionFile( $dbh, $file );
460 return (0, "Couldn't find schema file(s) '$file*'");
462 unless ( -f $file && -r $file ) {
463 return (0, "File '$file' doesn't exist or couldn't be read");
468 open my $fh_schema, "<$file";
471 open my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type )
475 foreach my $line ( <$fh_schema>, ($_ = ';;'), $has_local? <$fh_schema_local>: () ) {
479 if ( $line =~ /;(\s*)$/ ) {
480 $statement =~ s/;(\s*)$//g;
481 push @schema, $statement;
485 close $fh_schema; close $fh_schema_local;
487 if ( $db_type eq 'Oracle' ) {
488 my $db_user = RT->Config->Get('DatabaseUser');
489 my $status = $dbh->do( "ALTER SESSION SET CURRENT_SCHEMA=$db_user" );
491 return $status, "Couldn't set current schema to $db_user."
492 ."\nError: ". $dbh->errstr;
496 local $SIG{__WARN__} = sub {};
498 $dbh->begin_work or return (0, "Couldn't begin transaction: ". $dbh->errstr);
499 foreach my $statement (@schema) {
500 if ( $statement =~ /^\s*;$/ ) {
504 my $sth = $dbh->prepare($statement)
505 or return (0, "Couldn't prepare SQL query:\n$statement\n\nERROR: ". $dbh->errstr);
506 unless ( $sth->execute or $is_local ) {
507 return (0, "Couldn't run SQL query:\n$statement\n\nERROR: ". $sth->errstr);
510 $dbh->commit or return (0, "Couldn't commit transaction: ". $dbh->errstr);
514 =head1 GetVersionFile
516 Takes base name of the file as argument, scans for <base name>-<version> named
517 files and returns file name with closest version to the version of the RT DB.
524 my $base_name = shift;
526 my $db_version = ref $self
527 ? $self->DatabaseVersion
529 my $tmp = RT::Handle->new;
531 $tmp->DatabaseVersion;
535 my @files = File::Glob::bsd_glob("$base_name*");
536 return '' unless @files;
538 my %version = map { $_ =~ /\.\w+-([-\w\.]+)$/; ($1||0) => $_ } @files;
540 foreach ( reverse sort cmp_version keys %version ) {
541 if ( cmp_version( $db_version, $_ ) >= 0 ) {
547 return defined $version? $version{ $version } : undef;
550 sub cmp_version($$) {
553 my @a = split /[^0-9]+/, $a;
554 my @b = split /[^0-9]+/, $b;
555 for ( my $i = 0; $i < @a; $i++ ) {
556 return 1 unless defined $b[$i];
557 return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
559 return 0 if @a == @b;
564 =head2 InsertInitialData
566 Inserts system objects into RT's DB, like system user or 'nobody',
567 internal groups and other records required. However, this method
568 doesn't insert any real users like 'root' and you have to use
569 InsertData or another way to do that.
571 Takes no arguments. Returns status and message tuple.
573 It's safe to call this method even if those objects already exist.
577 sub InsertInitialData {
582 # create RT_System user and grant him rights
584 require RT::CurrentUser;
586 my $test_user = RT::User->new( new RT::CurrentUser );
587 $test_user->Load('RT_System');
588 if ( $test_user->id ) {
589 push @warns, "Found system user in the DB.";
592 my $user = RT::User->new( new RT::CurrentUser );
593 my ( $val, $msg ) = $user->_BootstrapCreate(
595 RealName => 'The RT System itself',
596 Comments => 'Do not delete or modify this user. '
597 . 'It is integral to RT\'s internal database structures',
599 LastUpdatedBy => '1',
601 return ($val, $msg) unless $val;
603 DBIx::SearchBuilder::Record::Cachable->FlushCache;
606 # init RT::SystemUser and RT::System objects
607 RT::InitSystemObjects();
608 unless ( $RT::SystemUser->id ) {
609 return (0, "Couldn't load system user");
612 # grant SuperUser right to system user
614 my $test_ace = RT::ACE->new( $RT::SystemUser );
615 $test_ace->LoadByCols(
616 PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
617 PrincipalType => 'Group',
618 RightName => 'SuperUser',
619 ObjectType => 'RT::System',
622 if ( $test_ace->id ) {
623 push @warns, "System user has global SuperUser right.";
625 my $ace = RT::ACE->new( $RT::SystemUser );
626 my ( $val, $msg ) = $ace->_BootstrapCreate(
627 PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
628 PrincipalType => 'Group',
629 RightName => 'SuperUser',
630 ObjectType => 'RT::System',
633 return ($val, $msg) unless $val;
635 DBIx::SearchBuilder::Record::Cachable->FlushCache;
639 # $self->loc('Everyone'); # For the string extractor to get a string to localize
640 # $self->loc('Privileged'); # For the string extractor to get a string to localize
641 # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
642 foreach my $name (qw(Everyone Privileged Unprivileged)) {
643 my $group = RT::Group->new( $RT::SystemUser );
644 $group->LoadSystemInternalGroup( $name );
646 push @warns, "System group '$name' already exists.";
650 $group = RT::Group->new( $RT::SystemUser );
651 my ( $val, $msg ) = $group->_Create(
653 Domain => 'SystemInternal',
654 Description => 'Pseudogroup for internal use', # loc
658 return ($val, $msg) unless $val;
663 my $user = RT::User->new( $RT::SystemUser );
664 $user->Load('Nobody');
666 push @warns, "Found 'Nobody' user in the DB.";
669 my ( $val, $msg ) = $user->Create(
671 RealName => 'Nobody in particular',
672 Comments => 'Do not delete or modify this user. It is integral '
673 .'to RT\'s internal data structures',
676 return ($val, $msg) unless $val;
679 if ( $user->HasRight( Right => 'OwnTicket', Object => $RT::System ) ) {
680 push @warns, "User 'Nobody' has global OwnTicket right.";
682 my ( $val, $msg ) = $user->PrincipalObj->GrantRight(
683 Right => 'OwnTicket',
684 Object => $RT::System,
686 return ($val, $msg) unless $val;
690 # rerun to get init Nobody as well
691 RT::InitSystemObjects();
694 foreach my $name (qw(Owner Requestor Cc AdminCc)) {
695 my $group = RT::Group->new( $RT::SystemUser );
696 $group->LoadSystemRoleGroup( $name );
698 push @warns, "System role '$name' already exists.";
702 $group = RT::Group->new( $RT::SystemUser );
703 my ( $val, $msg ) = $group->_Create(
705 Domain => 'RT::System-Role',
706 Description => 'SystemRolegroup for internal use', # loc
710 return ($val, $msg) unless $val;
713 push @warns, "You appear to have a functional RT database."
716 return (1, join "\n", @warns);
721 Load some sort of data into the database, takes path to a file.
727 my $datafile = shift;
729 # Slurp in stuff to insert from the datafile. Possible things to go in here:-
730 our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
731 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
732 local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
733 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
736 $RT::Logger->debug("Going to load '$datafile' data file");
737 eval { require $datafile }
738 or return (0, "Couldn't load data from '$datafile' for import:\n\nERROR:". $@);
741 $RT::Logger->debug("Running initial actions...");
742 foreach ( @Initial ) {
744 eval { $_->(); 1 } or return (0, "One of initial functions failed: $@");
746 $RT::Logger->debug("Done.");
749 $RT::Logger->debug("Creating groups...");
750 foreach my $item (@Groups) {
751 my $new_entry = RT::Group->new( $RT::SystemUser );
752 my $member_of = delete $item->{'MemberOf'};
753 my ( $return, $msg ) = $new_entry->_Create(%$item);
755 $RT::Logger->error( $msg );
758 $RT::Logger->debug($return .".");
761 $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
762 foreach( @$member_of ) {
763 my $parent = RT::Group->new($RT::SystemUser);
764 if ( ref $_ eq 'HASH' ) {
765 $parent->LoadByCols( %$_ );
768 $parent->LoadUserDefinedGroup( $_ );
772 "(Error: wrong format of MemberOf field."
773 ." Should be name of user defined group or"
774 ." hash reference with 'column => value' pairs."
775 ." Use array reference to add to multiple groups)"
779 unless ( $parent->Id ) {
780 $RT::Logger->error("(Error: couldn't load group to add member)");
783 my ( $return, $msg ) = $parent->AddMember( $new_entry->Id );
785 $RT::Logger->error( $msg );
787 $RT::Logger->debug( $return ."." );
792 $RT::Logger->debug("done.");
795 $RT::Logger->debug("Creating users...");
796 foreach my $item (@Users) {
797 my $new_entry = new RT::User( $RT::SystemUser );
798 my ( $return, $msg ) = $new_entry->Create(%$item);
800 $RT::Logger->error( $msg );
802 $RT::Logger->debug( $return ."." );
805 $RT::Logger->debug("done.");
808 $RT::Logger->debug("Creating queues...");
809 for my $item (@Queues) {
810 my $new_entry = new RT::Queue($RT::SystemUser);
811 my ( $return, $msg ) = $new_entry->Create(%$item);
813 $RT::Logger->error( $msg );
815 $RT::Logger->debug( $return ."." );
818 $RT::Logger->debug("done.");
820 if ( @CustomFields ) {
821 $RT::Logger->debug("Creating custom fields...");
822 for my $item ( @CustomFields ) {
823 my $new_entry = new RT::CustomField( $RT::SystemUser );
824 my $values = delete $item->{'Values'};
827 # if ref then it's list of queues, so we do things ourself
828 if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
829 $item->{'LookupType'} = 'RT::Queue-RT::Ticket';
830 @queues = @{ delete $item->{'Queue'} };
833 my ( $return, $msg ) = $new_entry->Create(%$item);
835 $RT::Logger->error( $msg );
839 foreach my $value ( @{$values} ) {
840 my ( $return, $msg ) = $new_entry->AddValue(%$value);
841 $RT::Logger->error( $msg ) unless $return;
845 if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
846 my $ocf = RT::ObjectCustomField->new($RT::SystemUser);
847 $ocf->Create( CustomField => $new_entry->Id );
850 for my $q (@queues) {
851 my $q_obj = RT::Queue->new($RT::SystemUser);
853 unless ( $q_obj->Id ) {
854 $RT::Logger->error("Could not find queue ". $q );
857 my $OCF = RT::ObjectCustomField->new($RT::SystemUser);
858 ( $return, $msg ) = $OCF->Create(
859 CustomField => $new_entry->Id,
860 ObjectId => $q_obj->Id,
862 $RT::Logger->error( $msg ) unless $return and $OCF->Id;
866 $RT::Logger->debug("done.");
869 $RT::Logger->debug("Creating ACL...");
870 for my $item (@ACL) {
872 my ($princ, $object);
874 # Global rights or Queue rights?
875 if ( $item->{'CF'} ) {
876 $object = RT::CustomField->new( $RT::SystemUser );
877 my @columns = ( Name => $item->{'CF'} );
878 push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
879 $object->LoadByName( @columns );
880 } elsif ( $item->{'Queue'} ) {
881 $object = RT::Queue->new($RT::SystemUser);
882 $object->Load( $item->{'Queue'} );
884 $object = $RT::System;
887 $RT::Logger->error("Couldn't load object") and next unless $object and $object->Id;
889 # Group rights or user rights?
890 if ( $item->{'GroupDomain'} ) {
891 $princ = RT::Group->new($RT::SystemUser);
892 if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
893 $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
894 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
895 $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
896 } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
897 $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
898 } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
901 $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
902 Queue => $object->id);
904 $princ->Load( $item->{'GroupId'} );
907 $princ = RT::User->new($RT::SystemUser);
908 $princ->Load( $item->{'UserId'} );
912 my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
913 Right => $item->{'Right'},
917 $RT::Logger->error( $msg );
920 $RT::Logger->debug( $return ."." );
923 $RT::Logger->debug("done.");
926 if ( @ScripActions ) {
927 $RT::Logger->debug("Creating ScripActions...");
929 for my $item (@ScripActions) {
930 my $new_entry = RT::ScripAction->new($RT::SystemUser);
931 my ( $return, $msg ) = $new_entry->Create(%$item);
933 $RT::Logger->error( $msg );
936 $RT::Logger->debug( $return ."." );
940 $RT::Logger->debug("done.");
943 if ( @ScripConditions ) {
944 $RT::Logger->debug("Creating ScripConditions...");
946 for my $item (@ScripConditions) {
947 my $new_entry = RT::ScripCondition->new($RT::SystemUser);
948 my ( $return, $msg ) = $new_entry->Create(%$item);
950 $RT::Logger->error( $msg );
953 $RT::Logger->debug( $return ."." );
957 $RT::Logger->debug("done.");
961 $RT::Logger->debug("Creating templates...");
963 for my $item (@Templates) {
964 my $new_entry = new RT::Template($RT::SystemUser);
965 my ( $return, $msg ) = $new_entry->Create(%$item);
967 $RT::Logger->error( $msg );
970 $RT::Logger->debug( $return ."." );
973 $RT::Logger->debug("done.");
976 $RT::Logger->debug("Creating scrips...");
978 for my $item (@Scrips) {
979 my $new_entry = new RT::Scrip($RT::SystemUser);
981 my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
982 push @queues, 0 unless @queues; # add global queue at least
984 foreach my $q ( @queues ) {
985 my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
987 $RT::Logger->error( $msg );
990 $RT::Logger->debug( $return ."." );
994 $RT::Logger->debug("done.");
997 $RT::Logger->debug("Creating predefined searches...");
998 my $sys = RT::System->new($RT::SystemUser);
1000 for my $item (@Attributes) {
1001 my $obj = delete $item->{Object}; # XXX: make this something loadable
1003 my ( $return, $msg ) = $obj->AddAttribute (%$item);
1004 unless ( $return ) {
1005 $RT::Logger->error( $msg );
1008 $RT::Logger->debug( $return ."." );
1011 $RT::Logger->debug("done.");
1014 $RT::Logger->debug("Running final actions...");
1018 $RT::Logger->error( "Failed to run one of final actions: $@" )
1021 $RT::Logger->debug("done.");
1024 my $db_type = RT->Config->Get('DatabaseType');
1025 $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
1027 $RT::Logger->debug("Done setting up database content.");
1029 # TODO is it ok to return 1 here? If so, the previous codes in this sub
1030 # should return (0, $msg) if error happens instead of just warning.
1031 # anyway, we need to return something here to tell if everything is ok
1032 return( 1, 'Done inserting data' );
1035 =head2 ACLEquivGroupId
1037 Given a userid, return that user's acl equivalence group
1041 sub ACLEquivGroupId {
1044 my $cu = $RT::SystemUser;
1046 require RT::CurrentUser;
1047 $cu = new RT::CurrentUser;
1048 $cu->LoadByName('RT_System');
1049 warn "Couldn't load RT_System user" unless $cu->id;
1052 my $equiv_group = RT::Group->new( $cu );
1053 $equiv_group->LoadACLEquivalenceGroup( $id );
1054 return $equiv_group->Id;
1057 __PACKAGE__->FinalizeDatabaseType;
1059 eval "require RT::Handle_Vendor";
1060 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm});
1061 eval "require RT::Handle_Local";
1062 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Local.pm});