1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
6 # <sales@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;
123 if ( $db_type eq 'Pg' ) {
124 my $version = $self->DatabaseVersion;
125 ($version) = $version =~ /^(\d+\.\d+)/;
126 $self->dbh->do("SET bytea_output = 'escape'") if $version >= 9.0;
131 $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
136 Build the DSN for the RT database. Doesn't take any parameters, draws all that
145 # Unless the database port is a positive integer, we really don't want to pass it.
146 my $db_port = RT->Config->Get('DatabasePort');
147 $db_port = undef unless (defined $db_port && $db_port =~ /^(\d+)$/);
148 my $db_host = RT->Config->Get('DatabaseHost');
149 $db_host = undef unless $db_host;
150 my $db_name = RT->Config->Get('DatabaseName');
151 my $db_type = RT->Config->Get('DatabaseType');
152 $db_name = File::Spec->catfile($RT::VarPath, $db_name)
153 if $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name);
157 Database => $db_name,
160 RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
161 DisconnectHandleOnDestroy => 1,
163 if ( $db_type eq 'Oracle' && $db_host ) {
164 $args{'SID'} = delete $args{'Database'};
166 $self->SUPER::BuildDSN( %args );
171 Returns the DSN for this handle. In order to get correct value you must
172 build DSN first, see L</BuildDSN>.
174 This is method can be called as class method, in this case creates
175 temporary handle object, L</BuildDSN builds DSN> and returns it.
181 return $self->SUPER::DSN if ref $self;
183 my $handle = $self->new;
190 Returns a DSN suitable for database creates and drops
191 and user creates and drops.
193 Gets RT's DSN first (see L<DSN>) and then change it according
194 to requirements of a database system RT's using.
201 my $db_name = RT->Config->Get('DatabaseName');
202 my $db_type = RT->Config->Get('DatabaseType');
204 my $dsn = $self->DSN;
205 if ( $db_type eq 'mysql' ) {
206 # with mysql, you want to connect sans database to funge things
207 $dsn =~ s/dbname=\Q$db_name//;
209 elsif ( $db_type eq 'Pg' ) {
210 # with postgres, you want to connect to template1 database
211 $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
213 elsif ( $db_type eq 'Informix' ) {
214 # with Informix, you want to connect sans database:
215 $dsn =~ s/Informix:\Q$db_name/Informix:/;
220 =head2 Database compatibility and integrity checks
229 my $dsn = $self->DSN;
230 my $user = RT->Config->Get('DatabaseUser');
231 my $pass = RT->Config->Get('DatabasePassword');
233 my $dbh = DBI->connect(
235 { RaiseError => 0, PrintError => 0 },
238 return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr);
241 RT::ConnectToDatabase();
244 require RT::CurrentUser;
245 my $test_user = new RT::CurrentUser;
246 $test_user->Load('RT_System');
247 unless ( $test_user->id ) {
248 return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'");
251 $test_user = new RT::CurrentUser;
252 $test_user->Load('Nobody');
253 unless ( $test_user->id ) {
254 return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'");
260 sub CheckCompatibility {
263 my $state = shift || 'post';
265 my $db_type = RT->Config->Get('DatabaseType');
266 if ( $db_type eq "mysql" ) {
267 # Check which version we're running
268 my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
269 return (0, "couldn't get version of the mysql server")
272 ($version) = $version =~ /^(\d+\.\d+)/;
273 return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
276 # MySQL must have InnoDB support
277 my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
278 if ( lc $innodb eq "no" ) {
279 return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
280 "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
281 } elsif ( lc $innodb eq "disabled" ) {
282 return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
283 "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
286 if ( $state eq 'post' ) {
287 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Tickets")->[1];
288 unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
289 return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
292 if ( $version >= 4.1 && $state eq 'post' ) {
293 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
294 unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
295 return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
296 ."Follow instructions in the UPGRADING.mysql file.");
303 =head2 Database maintanance
305 =head3 CreateDatabase $DBH
307 Creates a new database. This method can be used as class method.
309 Takes DBI handle. Many database systems require special handle to
310 allow you to create a new database, so you have to use L<SystemDSN>
311 method during connection.
313 Fetches type and name of the DB from the config.
319 my $dbh = shift or return (0, "No DBI handle provided");
320 my $db_type = RT->Config->Get('DatabaseType');
321 my $db_name = RT->Config->Get('DatabaseName');
324 if ( $db_type eq 'SQLite' ) {
325 return (1, 'Skipped as SQLite doesn\'t need any action');
327 elsif ( $db_type eq 'Oracle' ) {
328 my $db_user = RT->Config->Get('DatabaseUser');
329 my $db_pass = RT->Config->Get('DatabasePassword');
331 "CREATE USER $db_user IDENTIFIED BY $db_pass"
332 ." default tablespace USERS"
333 ." temporary tablespace TEMP"
334 ." quota unlimited on USERS"
337 return $status, "Couldn't create user $db_user identified by $db_pass."
338 ."\nError: ". $dbh->errstr;
340 $status = $dbh->do( "GRANT connect, resource TO $db_user" );
342 return $status, "Couldn't grant connect and resource to $db_user."
343 ."\nError: ". $dbh->errstr;
345 return (1, "Created user $db_user. All RT's objects should be in his schema.");
347 elsif ( $db_type eq 'Pg' ) {
348 # XXX: as we get external DBH we don't know if RaiseError or PrintError
349 # are enabled, so we have to setup it here and restore them back
350 $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0")
351 || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0");
353 elsif ( $db_type eq 'Informix' ) {
354 local $ENV{'DB_LOCALE'} = 'en_us.utf8';
355 $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG");
358 $status = $dbh->do("CREATE DATABASE $db_name");
360 return ($status, $DBI::errstr);
363 =head3 DropDatabase $DBH [Force => 0]
365 Drops RT's database. This method can be used as class method.
367 Takes DBI handle as first argument. Many database systems require
368 special handle to allow you to create a new database, so you have
369 to use L<SystemDSN> method during connection.
371 Fetches type and name of the DB from the config.
377 my $dbh = shift or return (0, "No DBI handle provided");
379 my $db_type = RT->Config->Get('DatabaseType');
380 my $db_name = RT->Config->Get('DatabaseName');
382 if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) {
383 my $db_user = RT->Config->Get('DatabaseUser');
384 my $status = $dbh->do( "DROP USER $db_user CASCADE" );
386 return 0, "Couldn't drop user $db_user."
387 ."\nError: ". $dbh->errstr;
389 return (1, "Successfully dropped user '$db_user' with his schema.");
391 elsif ( $db_type eq 'SQLite' ) {
393 $path = "$RT::VarPath/$path" unless substr($path, 0, 1) eq '/';
394 unlink $path or return (0, "Couldn't remove '$path': $!");
397 $dbh->do("DROP DATABASE ". $db_name)
398 or return (0, $DBI::errstr);
410 my $base_path = shift || $RT::EtcPath;
412 my $db_type = RT->Config->Get('DatabaseType');
413 return (1) if $db_type eq 'SQLite';
415 $dbh = $self->dbh if !$dbh && ref $self;
416 return (0, "No DBI handle provided") unless $dbh;
418 return (0, "'$base_path' doesn't exist") unless -e $base_path;
421 if ( -d $base_path ) {
422 $path = File::Spec->catfile( $base_path, "acl.$db_type");
423 $path = $self->GetVersionFile($dbh, $path);
425 $path = File::Spec->catfile( $base_path, "acl")
426 unless $path && -e $path;
427 return (0, "Couldn't find ACLs for $db_type")
434 do $path || return (0, "Couldn't load ACLs: " . $@);
436 foreach my $statement (@acl) {
437 my $sth = $dbh->prepare($statement)
438 or return (0, "Couldn't prepare SQL query:\n $statement\n\nERROR: ". $dbh->errstr);
439 unless ( $sth->execute ) {
440 return (0, "Couldn't run SQL query:\n $statement\n\nERROR: ". $sth->errstr);
453 my $base_path = (shift || $RT::EtcPath);
455 $dbh = $self->dbh if !$dbh && ref $self;
456 return (0, "No DBI handle provided") unless $dbh;
458 my $db_type = RT->Config->Get('DatabaseType');
461 if ( -d $base_path ) {
462 $file = $base_path . "/schema." . $db_type;
467 $file = $self->GetVersionFile( $dbh, $file );
469 return (0, "Couldn't find schema file(s) '$file*'");
471 unless ( -f $file && -r $file ) {
472 return (0, "File '$file' doesn't exist or couldn't be read");
477 open my $fh_schema, "<$file";
480 open my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type )
484 foreach my $line ( <$fh_schema>, ($_ = ';;'), $has_local? <$fh_schema_local>: () ) {
488 if ( $line =~ /;(\s*)$/ ) {
489 $statement =~ s/;(\s*)$//g;
490 push @schema, $statement;
494 close $fh_schema; close $fh_schema_local;
496 if ( $db_type eq 'Oracle' ) {
497 my $db_user = RT->Config->Get('DatabaseUser');
498 my $status = $dbh->do( "ALTER SESSION SET CURRENT_SCHEMA=$db_user" );
500 return $status, "Couldn't set current schema to $db_user."
501 ."\nError: ". $dbh->errstr;
505 local $SIG{__WARN__} = sub {};
507 $dbh->begin_work or return (0, "Couldn't begin transaction: ". $dbh->errstr);
508 foreach my $statement (@schema) {
509 if ( $statement =~ /^\s*;$/ ) {
513 my $sth = $dbh->prepare($statement)
514 or return (0, "Couldn't prepare SQL query:\n$statement\n\nERROR: ". $dbh->errstr);
515 unless ( $sth->execute or $is_local ) {
516 return (0, "Couldn't run SQL query:\n$statement\n\nERROR: ". $sth->errstr);
519 $dbh->commit or return (0, "Couldn't commit transaction: ". $dbh->errstr);
523 =head1 GetVersionFile
525 Takes base name of the file as argument, scans for <base name>-<version> named
526 files and returns file name with closest version to the version of the RT DB.
533 my $base_name = shift;
535 my $db_version = ref $self
536 ? $self->DatabaseVersion
538 my $tmp = RT::Handle->new;
540 $tmp->DatabaseVersion;
544 my @files = File::Glob::bsd_glob("$base_name*");
545 return '' unless @files;
547 my %version = map { $_ =~ /\.\w+-([-\w\.]+)$/; ($1||0) => $_ } @files;
549 foreach ( reverse sort cmp_version keys %version ) {
550 if ( cmp_version( $db_version, $_ ) >= 0 ) {
556 return defined $version? $version{ $version } : undef;
559 sub cmp_version($$) {
562 my @a = split /[^0-9]+/, $a;
563 my @b = split /[^0-9]+/, $b;
564 for ( my $i = 0; $i < @a; $i++ ) {
565 return 1 unless defined $b[$i];
566 return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
568 return 0 if @a == @b;
573 =head2 InsertInitialData
575 Inserts system objects into RT's DB, like system user or 'nobody',
576 internal groups and other records required. However, this method
577 doesn't insert any real users like 'root' and you have to use
578 InsertData or another way to do that.
580 Takes no arguments. Returns status and message tuple.
582 It's safe to call this method even if those objects already exist.
586 sub InsertInitialData {
591 # create RT_System user and grant him rights
593 require RT::CurrentUser;
595 my $test_user = RT::User->new( new RT::CurrentUser );
596 $test_user->Load('RT_System');
597 if ( $test_user->id ) {
598 push @warns, "Found system user in the DB.";
601 my $user = RT::User->new( new RT::CurrentUser );
602 my ( $val, $msg ) = $user->_BootstrapCreate(
604 RealName => 'The RT System itself',
605 Comments => 'Do not delete or modify this user. '
606 . 'It is integral to RT\'s internal database structures',
608 LastUpdatedBy => '1',
610 return ($val, $msg) unless $val;
612 DBIx::SearchBuilder::Record::Cachable->FlushCache;
615 # init RT::SystemUser and RT::System objects
616 RT::InitSystemObjects();
617 unless ( $RT::SystemUser->id ) {
618 return (0, "Couldn't load system user");
621 # grant SuperUser right to system user
623 my $test_ace = RT::ACE->new( $RT::SystemUser );
624 $test_ace->LoadByCols(
625 PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
626 PrincipalType => 'Group',
627 RightName => 'SuperUser',
628 ObjectType => 'RT::System',
631 if ( $test_ace->id ) {
632 push @warns, "System user has global SuperUser right.";
634 my $ace = RT::ACE->new( $RT::SystemUser );
635 my ( $val, $msg ) = $ace->_BootstrapCreate(
636 PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
637 PrincipalType => 'Group',
638 RightName => 'SuperUser',
639 ObjectType => 'RT::System',
642 return ($val, $msg) unless $val;
644 DBIx::SearchBuilder::Record::Cachable->FlushCache;
648 # $self->loc('Everyone'); # For the string extractor to get a string to localize
649 # $self->loc('Privileged'); # For the string extractor to get a string to localize
650 # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
651 foreach my $name (qw(Everyone Privileged Unprivileged)) {
652 my $group = RT::Group->new( $RT::SystemUser );
653 $group->LoadSystemInternalGroup( $name );
655 push @warns, "System group '$name' already exists.";
659 $group = RT::Group->new( $RT::SystemUser );
660 my ( $val, $msg ) = $group->_Create(
662 Domain => 'SystemInternal',
663 Description => 'Pseudogroup for internal use', # loc
667 return ($val, $msg) unless $val;
672 my $user = RT::User->new( $RT::SystemUser );
673 $user->Load('Nobody');
675 push @warns, "Found 'Nobody' user in the DB.";
678 my ( $val, $msg ) = $user->Create(
680 RealName => 'Nobody in particular',
681 Comments => 'Do not delete or modify this user. It is integral '
682 .'to RT\'s internal data structures',
685 return ($val, $msg) unless $val;
688 if ( $user->HasRight( Right => 'OwnTicket', Object => $RT::System ) ) {
689 push @warns, "User 'Nobody' has global OwnTicket right.";
691 my ( $val, $msg ) = $user->PrincipalObj->GrantRight(
692 Right => 'OwnTicket',
693 Object => $RT::System,
695 return ($val, $msg) unless $val;
699 # rerun to get init Nobody as well
700 RT::InitSystemObjects();
703 foreach my $name (qw(Owner Requestor Cc AdminCc)) {
704 my $group = RT::Group->new( $RT::SystemUser );
705 $group->LoadSystemRoleGroup( $name );
707 push @warns, "System role '$name' already exists.";
711 $group = RT::Group->new( $RT::SystemUser );
712 my ( $val, $msg ) = $group->_Create(
714 Domain => 'RT::System-Role',
715 Description => 'SystemRolegroup for internal use', # loc
719 return ($val, $msg) unless $val;
722 push @warns, "You appear to have a functional RT database."
725 return (1, join "\n", @warns);
730 Load some sort of data into the database, takes path to a file.
736 my $datafile = shift;
738 # Slurp in stuff to insert from the datafile. Possible things to go in here:-
739 our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
740 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
741 local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
742 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
745 $RT::Logger->debug("Going to load '$datafile' data file");
746 eval { require $datafile }
747 or return (0, "Couldn't load data from '$datafile' for import:\n\nERROR:". $@);
750 $RT::Logger->debug("Running initial actions...");
751 foreach ( @Initial ) {
753 eval { $_->(); 1 } or return (0, "One of initial functions failed: $@");
755 $RT::Logger->debug("Done.");
758 $RT::Logger->debug("Creating groups...");
759 foreach my $item (@Groups) {
760 my $new_entry = RT::Group->new( $RT::SystemUser );
761 my $member_of = delete $item->{'MemberOf'};
762 my ( $return, $msg ) = $new_entry->_Create(%$item);
764 $RT::Logger->error( $msg );
767 $RT::Logger->debug($return .".");
770 $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
771 foreach( @$member_of ) {
772 my $parent = RT::Group->new($RT::SystemUser);
773 if ( ref $_ eq 'HASH' ) {
774 $parent->LoadByCols( %$_ );
777 $parent->LoadUserDefinedGroup( $_ );
781 "(Error: wrong format of MemberOf field."
782 ." Should be name of user defined group or"
783 ." hash reference with 'column => value' pairs."
784 ." Use array reference to add to multiple groups)"
788 unless ( $parent->Id ) {
789 $RT::Logger->error("(Error: couldn't load group to add member)");
792 my ( $return, $msg ) = $parent->AddMember( $new_entry->Id );
794 $RT::Logger->error( $msg );
796 $RT::Logger->debug( $return ."." );
801 $RT::Logger->debug("done.");
804 $RT::Logger->debug("Creating users...");
805 foreach my $item (@Users) {
806 my $new_entry = new RT::User( $RT::SystemUser );
807 my ( $return, $msg ) = $new_entry->Create(%$item);
809 $RT::Logger->error( $msg );
811 $RT::Logger->debug( $return ."." );
814 $RT::Logger->debug("done.");
817 $RT::Logger->debug("Creating queues...");
818 for my $item (@Queues) {
819 my $new_entry = new RT::Queue($RT::SystemUser);
820 my ( $return, $msg ) = $new_entry->Create(%$item);
822 $RT::Logger->error( $msg );
824 $RT::Logger->debug( $return ."." );
827 $RT::Logger->debug("done.");
829 if ( @CustomFields ) {
830 $RT::Logger->debug("Creating custom fields...");
831 for my $item ( @CustomFields ) {
832 my $new_entry = new RT::CustomField( $RT::SystemUser );
833 my $values = delete $item->{'Values'};
836 # if ref then it's list of queues, so we do things ourself
837 if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
838 $item->{'LookupType'} ||= 'RT::Queue-RT::Ticket';
839 @queues = @{ delete $item->{'Queue'} };
842 my ( $return, $msg ) = $new_entry->Create(%$item);
844 $RT::Logger->error( $msg );
848 foreach my $value ( @{$values} ) {
849 my ( $return, $msg ) = $new_entry->AddValue(%$value);
850 $RT::Logger->error( $msg ) unless $return;
854 if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
855 my $ocf = RT::ObjectCustomField->new($RT::SystemUser);
856 $ocf->Create( CustomField => $new_entry->Id );
859 for my $q (@queues) {
860 my $q_obj = RT::Queue->new($RT::SystemUser);
862 unless ( $q_obj->Id ) {
863 $RT::Logger->error("Could not find queue ". $q );
866 my $OCF = RT::ObjectCustomField->new($RT::SystemUser);
867 ( $return, $msg ) = $OCF->Create(
868 CustomField => $new_entry->Id,
869 ObjectId => $q_obj->Id,
871 $RT::Logger->error( $msg ) unless $return and $OCF->Id;
875 $RT::Logger->debug("done.");
878 $RT::Logger->debug("Creating ACL...");
879 for my $item (@ACL) {
881 my ($princ, $object);
883 # Global rights or Queue rights?
884 if ( $item->{'CF'} ) {
885 $object = RT::CustomField->new( $RT::SystemUser );
886 my @columns = ( Name => $item->{'CF'} );
887 push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
888 $object->LoadByName( @columns );
889 } elsif ( $item->{'Queue'} ) {
890 $object = RT::Queue->new($RT::SystemUser);
891 $object->Load( $item->{'Queue'} );
893 $object = $RT::System;
896 $RT::Logger->error("Couldn't load object") and next unless $object and $object->Id;
898 # Group rights or user rights?
899 if ( $item->{'GroupDomain'} ) {
900 $princ = RT::Group->new($RT::SystemUser);
901 if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
902 $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
903 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
904 $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
905 } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
906 $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
907 } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
910 $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
911 Queue => $object->id);
913 $princ->Load( $item->{'GroupId'} );
916 $princ = RT::User->new($RT::SystemUser);
917 $princ->Load( $item->{'UserId'} );
921 my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
922 Right => $item->{'Right'},
926 $RT::Logger->error( $msg );
929 $RT::Logger->debug( $return ."." );
932 $RT::Logger->debug("done.");
935 if ( @ScripActions ) {
936 $RT::Logger->debug("Creating ScripActions...");
938 for my $item (@ScripActions) {
939 my $new_entry = RT::ScripAction->new($RT::SystemUser);
940 my ( $return, $msg ) = $new_entry->Create(%$item);
942 $RT::Logger->error( $msg );
945 $RT::Logger->debug( $return ."." );
949 $RT::Logger->debug("done.");
952 if ( @ScripConditions ) {
953 $RT::Logger->debug("Creating ScripConditions...");
955 for my $item (@ScripConditions) {
956 my $new_entry = RT::ScripCondition->new($RT::SystemUser);
957 my ( $return, $msg ) = $new_entry->Create(%$item);
959 $RT::Logger->error( $msg );
962 $RT::Logger->debug( $return ."." );
966 $RT::Logger->debug("done.");
970 $RT::Logger->debug("Creating templates...");
972 for my $item (@Templates) {
973 my $new_entry = new RT::Template($RT::SystemUser);
974 my ( $return, $msg ) = $new_entry->Create(%$item);
976 $RT::Logger->error( $msg );
979 $RT::Logger->debug( $return ."." );
982 $RT::Logger->debug("done.");
985 $RT::Logger->debug("Creating scrips...");
987 for my $item (@Scrips) {
988 my $new_entry = new RT::Scrip($RT::SystemUser);
990 my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
991 push @queues, 0 unless @queues; # add global queue at least
993 foreach my $q ( @queues ) {
994 my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
996 $RT::Logger->error( $msg );
999 $RT::Logger->debug( $return ."." );
1003 $RT::Logger->debug("done.");
1005 if ( @Attributes ) {
1006 $RT::Logger->debug("Creating predefined searches...");
1007 my $sys = RT::System->new($RT::SystemUser);
1009 for my $item (@Attributes) {
1010 my $obj = delete $item->{Object}; # XXX: make this something loadable
1012 my ( $return, $msg ) = $obj->AddAttribute (%$item);
1013 unless ( $return ) {
1014 $RT::Logger->error( $msg );
1017 $RT::Logger->debug( $return ."." );
1020 $RT::Logger->debug("done.");
1023 $RT::Logger->debug("Running final actions...");
1027 $RT::Logger->error( "Failed to run one of final actions: $@" )
1030 $RT::Logger->debug("done.");
1033 my $db_type = RT->Config->Get('DatabaseType');
1034 $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
1036 $RT::Logger->debug("Done setting up database content.");
1038 # TODO is it ok to return 1 here? If so, the previous codes in this sub
1039 # should return (0, $msg) if error happens instead of just warning.
1040 # anyway, we need to return something here to tell if everything is ok
1041 return( 1, 'Done inserting data' );
1044 =head2 ACLEquivGroupId
1046 Given a userid, return that user's acl equivalence group
1050 sub ACLEquivGroupId {
1053 my $cu = $RT::SystemUser;
1055 require RT::CurrentUser;
1056 $cu = new RT::CurrentUser;
1057 $cu->LoadByName('RT_System');
1058 warn "Couldn't load RT_System user" unless $cu->id;
1061 my $equiv_group = RT::Group->new( $cu );
1062 $equiv_group->LoadACLEquivalenceGroup( $id );
1063 return $equiv_group->Id;
1066 __PACKAGE__->FinalizeDatabaseType;
1068 eval "require RT::Handle_Vendor";
1069 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm});
1070 eval "require RT::Handle_Local";
1071 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Local.pm});