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 {
86 use base "DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType');
90 die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
91 "Perhaps you've picked an invalid database type or spelled it incorrectly.\n".
98 Connects to RT's database using credentials and options from the RT config.
106 my $db_type = RT->Config->Get('DatabaseType');
107 if ( $db_type eq 'Oracle' ) {
108 $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
109 $ENV{'NLS_NCHAR'} = "AL32UTF8";
112 $self->SUPER::Connect(
113 User => RT->Config->Get('DatabaseUser'),
114 Password => RT->Config->Get('DatabasePassword'),
117 if ( $db_type eq 'mysql' ) {
118 my $version = $self->DatabaseVersion;
119 ($version) = $version =~ /^(\d+\.\d+)/;
120 $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
124 if ( $db_type eq 'Pg' ) {
125 my $version = $self->DatabaseVersion;
126 ($version) = $version =~ /^(\d+\.\d+)/;
127 $self->dbh->do("SET bytea_output = 'escape'") if $version >= 9.0;
132 $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
137 Build the DSN for the RT database. Doesn't take any parameters, draws all that
146 # Unless the database port is a positive integer, we really don't want to pass it.
147 my $db_port = RT->Config->Get('DatabasePort');
148 $db_port = undef unless (defined $db_port && $db_port =~ /^(\d+)$/);
149 my $db_host = RT->Config->Get('DatabaseHost');
150 $db_host = undef unless $db_host;
151 my $db_name = RT->Config->Get('DatabaseName');
152 my $db_type = RT->Config->Get('DatabaseType');
153 $db_name = File::Spec->catfile($RT::VarPath, $db_name)
154 if $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name);
158 Database => $db_name,
161 RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
162 DisconnectHandleOnDestroy => 1,
164 if ( $db_type eq 'Oracle' && $db_host ) {
165 $args{'SID'} = delete $args{'Database'};
167 $self->SUPER::BuildDSN( %args );
172 Returns the DSN for this handle. In order to get correct value you must
173 build DSN first, see L</BuildDSN>.
175 This is method can be called as class method, in this case creates
176 temporary handle object, L</BuildDSN builds DSN> and returns it.
182 return $self->SUPER::DSN if ref $self;
184 my $handle = $self->new;
191 Returns a DSN suitable for database creates and drops
192 and user creates and drops.
194 Gets RT's DSN first (see L<DSN>) and then change it according
195 to requirements of a database system RT's using.
202 my $db_name = RT->Config->Get('DatabaseName');
203 my $db_type = RT->Config->Get('DatabaseType');
205 my $dsn = $self->DSN;
206 if ( $db_type eq 'mysql' ) {
207 # with mysql, you want to connect sans database to funge things
208 $dsn =~ s/dbname=\Q$db_name//;
210 elsif ( $db_type eq 'Pg' ) {
211 # with postgres, you want to connect to template1 database
212 $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
214 elsif ( $db_type eq 'Informix' ) {
215 # with Informix, you want to connect sans database:
216 $dsn =~ s/Informix:\Q$db_name/Informix:/;
221 =head2 Database compatibility and integrity checks
230 my $dsn = $self->DSN;
231 my $user = RT->Config->Get('DatabaseUser');
232 my $pass = RT->Config->Get('DatabasePassword');
234 my $dbh = DBI->connect(
236 { RaiseError => 0, PrintError => 0 },
239 return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr);
242 RT::ConnectToDatabase();
245 require RT::CurrentUser;
246 my $test_user = new RT::CurrentUser;
247 $test_user->Load('RT_System');
248 unless ( $test_user->id ) {
249 return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'");
252 $test_user = new RT::CurrentUser;
253 $test_user->Load('Nobody');
254 unless ( $test_user->id ) {
255 return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'");
261 sub CheckCompatibility {
264 my $state = shift || 'post';
266 my $db_type = RT->Config->Get('DatabaseType');
267 if ( $db_type eq "mysql" ) {
268 # Check which version we're running
269 my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
270 return (0, "couldn't get version of the mysql server")
273 ($version) = $version =~ /^(\d+\.\d+)/;
274 return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
277 # MySQL must have InnoDB support
278 my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
279 if ( lc $innodb eq "no" ) {
280 return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
281 "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
282 } elsif ( lc $innodb eq "disabled" ) {
283 return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
284 "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
287 if ( $state eq 'post' ) {
288 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Tickets")->[1];
289 unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
290 return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
293 if ( $version >= 4.1 && $state eq 'post' ) {
294 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
295 unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
296 return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
297 ."Follow instructions in the UPGRADING.mysql file.");
304 =head2 Database maintanance
306 =head3 CreateDatabase $DBH
308 Creates a new database. This method can be used as class method.
310 Takes DBI handle. Many database systems require special handle to
311 allow you to create a new database, so you have to use L<SystemDSN>
312 method during connection.
314 Fetches type and name of the DB from the config.
320 my $dbh = shift or return (0, "No DBI handle provided");
321 my $db_type = RT->Config->Get('DatabaseType');
322 my $db_name = RT->Config->Get('DatabaseName');
325 if ( $db_type eq 'SQLite' ) {
326 return (1, 'Skipped as SQLite doesn\'t need any action');
328 elsif ( $db_type eq 'Oracle' ) {
329 my $db_user = RT->Config->Get('DatabaseUser');
330 my $db_pass = RT->Config->Get('DatabasePassword');
332 "CREATE USER $db_user IDENTIFIED BY $db_pass"
333 ." default tablespace USERS"
334 ." temporary tablespace TEMP"
335 ." quota unlimited on USERS"
338 return $status, "Couldn't create user $db_user identified by $db_pass."
339 ."\nError: ". $dbh->errstr;
341 $status = $dbh->do( "GRANT connect, resource TO $db_user" );
343 return $status, "Couldn't grant connect and resource to $db_user."
344 ."\nError: ". $dbh->errstr;
346 return (1, "Created user $db_user. All RT's objects should be in his schema.");
348 elsif ( $db_type eq 'Pg' ) {
349 # XXX: as we get external DBH we don't know if RaiseError or PrintError
350 # are enabled, so we have to setup it here and restore them back
351 $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0")
352 || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0");
354 elsif ( $db_type eq 'Informix' ) {
355 local $ENV{'DB_LOCALE'} = 'en_us.utf8';
356 $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG");
359 $status = $dbh->do("CREATE DATABASE $db_name");
361 return ($status, $DBI::errstr);
364 =head3 DropDatabase $DBH [Force => 0]
366 Drops RT's database. This method can be used as class method.
368 Takes DBI handle as first argument. Many database systems require
369 special handle to allow you to create a new database, so you have
370 to use L<SystemDSN> method during connection.
372 Fetches type and name of the DB from the config.
378 my $dbh = shift or return (0, "No DBI handle provided");
380 my $db_type = RT->Config->Get('DatabaseType');
381 my $db_name = RT->Config->Get('DatabaseName');
383 if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) {
384 my $db_user = RT->Config->Get('DatabaseUser');
385 my $status = $dbh->do( "DROP USER $db_user CASCADE" );
387 return 0, "Couldn't drop user $db_user."
388 ."\nError: ". $dbh->errstr;
390 return (1, "Successfully dropped user '$db_user' with his schema.");
392 elsif ( $db_type eq 'SQLite' ) {
394 $path = "$RT::VarPath/$path" unless substr($path, 0, 1) eq '/';
395 unlink $path or return (0, "Couldn't remove '$path': $!");
398 $dbh->do("DROP DATABASE ". $db_name)
399 or return (0, $DBI::errstr);
411 my $base_path = shift || $RT::EtcPath;
413 my $db_type = RT->Config->Get('DatabaseType');
414 return (1) if $db_type eq 'SQLite';
416 $dbh = $self->dbh if !$dbh && ref $self;
417 return (0, "No DBI handle provided") unless $dbh;
419 return (0, "'$base_path' doesn't exist") unless -e $base_path;
422 if ( -d $base_path ) {
423 $path = File::Spec->catfile( $base_path, "acl.$db_type");
424 $path = $self->GetVersionFile($dbh, $path);
426 $path = File::Spec->catfile( $base_path, "acl")
427 unless $path && -e $path;
428 return (0, "Couldn't find ACLs for $db_type")
435 do $path || return (0, "Couldn't load ACLs: " . $@);
437 foreach my $statement (@acl) {
438 my $sth = $dbh->prepare($statement)
439 or return (0, "Couldn't prepare SQL query:\n $statement\n\nERROR: ". $dbh->errstr);
440 unless ( $sth->execute ) {
441 return (0, "Couldn't run SQL query:\n $statement\n\nERROR: ". $sth->errstr);
454 my $base_path = (shift || $RT::EtcPath);
456 $dbh = $self->dbh if !$dbh && ref $self;
457 return (0, "No DBI handle provided") unless $dbh;
459 my $db_type = RT->Config->Get('DatabaseType');
462 if ( -d $base_path ) {
463 $file = $base_path . "/schema." . $db_type;
468 $file = $self->GetVersionFile( $dbh, $file );
470 return (0, "Couldn't find schema file(s) '$file*'");
472 unless ( -f $file && -r $file ) {
473 return (0, "File '$file' doesn't exist or couldn't be read");
478 open( my $fh_schema, '<', $file ) or die $!;
481 open( my $fh_schema_local, "<", $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type ))
485 foreach my $line ( <$fh_schema>, ($_ = ';;'), $has_local? <$fh_schema_local>: () ) {
489 if ( $line =~ /;(\s*)$/ ) {
490 $statement =~ s/;(\s*)$//g;
491 push @schema, $statement;
495 close $fh_schema; close $fh_schema_local;
497 if ( $db_type eq 'Oracle' ) {
498 my $db_user = RT->Config->Get('DatabaseUser');
499 my $status = $dbh->do( "ALTER SESSION SET CURRENT_SCHEMA=$db_user" );
501 return $status, "Couldn't set current schema to $db_user."
502 ."\nError: ". $dbh->errstr;
506 local $SIG{__WARN__} = sub {};
508 $dbh->begin_work or return (0, "Couldn't begin transaction: ". $dbh->errstr);
509 foreach my $statement (@schema) {
510 if ( $statement =~ /^\s*;$/ ) {
514 my $sth = $dbh->prepare($statement)
515 or return (0, "Couldn't prepare SQL query:\n$statement\n\nERROR: ". $dbh->errstr);
516 unless ( $sth->execute or $is_local ) {
517 return (0, "Couldn't run SQL query:\n$statement\n\nERROR: ". $sth->errstr);
520 $dbh->commit or return (0, "Couldn't commit transaction: ". $dbh->errstr);
524 =head1 GetVersionFile
526 Takes base name of the file as argument, scans for <base name>-<version> named
527 files and returns file name with closest version to the version of the RT DB.
534 my $base_name = shift;
536 my $db_version = ref $self
537 ? $self->DatabaseVersion
539 my $tmp = RT::Handle->new;
541 $tmp->DatabaseVersion;
545 my @files = File::Glob::bsd_glob("$base_name*");
546 return '' unless @files;
548 my %version = map { $_ =~ /\.\w+-([-\w\.]+)$/; ($1||0) => $_ } @files;
550 foreach ( reverse sort cmp_version keys %version ) {
551 if ( cmp_version( $db_version, $_ ) >= 0 ) {
557 return defined $version? $version{ $version } : undef;
560 sub cmp_version($$) {
563 my @a = split /[^0-9]+/, $a;
564 my @b = split /[^0-9]+/, $b;
565 for ( my $i = 0; $i < @a; $i++ ) {
566 return 1 unless defined $b[$i];
567 return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
569 return 0 if @a == @b;
574 =head2 InsertInitialData
576 Inserts system objects into RT's DB, like system user or 'nobody',
577 internal groups and other records required. However, this method
578 doesn't insert any real users like 'root' and you have to use
579 InsertData or another way to do that.
581 Takes no arguments. Returns status and message tuple.
583 It's safe to call this method even if those objects already exist.
587 sub InsertInitialData {
592 # create RT_System user and grant him rights
594 require RT::CurrentUser;
596 my $test_user = RT::User->new( new RT::CurrentUser );
597 $test_user->Load('RT_System');
598 if ( $test_user->id ) {
599 push @warns, "Found system user in the DB.";
602 my $user = RT::User->new( new RT::CurrentUser );
603 my ( $val, $msg ) = $user->_BootstrapCreate(
605 RealName => 'The RT System itself',
606 Comments => 'Do not delete or modify this user. '
607 . 'It is integral to RT\'s internal database structures',
609 LastUpdatedBy => '1',
611 return ($val, $msg) unless $val;
613 DBIx::SearchBuilder::Record::Cachable->FlushCache;
616 # init RT::SystemUser and RT::System objects
617 RT::InitSystemObjects();
618 unless ( $RT::SystemUser->id ) {
619 return (0, "Couldn't load system user");
622 # grant SuperUser right to system user
624 my $test_ace = RT::ACE->new( $RT::SystemUser );
625 $test_ace->LoadByCols(
626 PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
627 PrincipalType => 'Group',
628 RightName => 'SuperUser',
629 ObjectType => 'RT::System',
632 if ( $test_ace->id ) {
633 push @warns, "System user has global SuperUser right.";
635 my $ace = RT::ACE->new( $RT::SystemUser );
636 my ( $val, $msg ) = $ace->_BootstrapCreate(
637 PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
638 PrincipalType => 'Group',
639 RightName => 'SuperUser',
640 ObjectType => 'RT::System',
643 return ($val, $msg) unless $val;
645 DBIx::SearchBuilder::Record::Cachable->FlushCache;
649 # $self->loc('Everyone'); # For the string extractor to get a string to localize
650 # $self->loc('Privileged'); # For the string extractor to get a string to localize
651 # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
652 foreach my $name (qw(Everyone Privileged Unprivileged)) {
653 my $group = RT::Group->new( $RT::SystemUser );
654 $group->LoadSystemInternalGroup( $name );
656 push @warns, "System group '$name' already exists.";
660 $group = RT::Group->new( $RT::SystemUser );
661 my ( $val, $msg ) = $group->_Create(
663 Domain => 'SystemInternal',
664 Description => 'Pseudogroup for internal use', # loc
668 return ($val, $msg) unless $val;
673 my $user = RT::User->new( $RT::SystemUser );
674 $user->Load('Nobody');
676 push @warns, "Found 'Nobody' user in the DB.";
679 my ( $val, $msg ) = $user->Create(
681 RealName => 'Nobody in particular',
682 Comments => 'Do not delete or modify this user. It is integral '
683 .'to RT\'s internal data structures',
686 return ($val, $msg) unless $val;
689 if ( $user->HasRight( Right => 'OwnTicket', Object => $RT::System ) ) {
690 push @warns, "User 'Nobody' has global OwnTicket right.";
692 my ( $val, $msg ) = $user->PrincipalObj->GrantRight(
693 Right => 'OwnTicket',
694 Object => $RT::System,
696 return ($val, $msg) unless $val;
700 # rerun to get init Nobody as well
701 RT::InitSystemObjects();
704 foreach my $name (qw(Owner Requestor Cc AdminCc)) {
705 my $group = RT::Group->new( $RT::SystemUser );
706 $group->LoadSystemRoleGroup( $name );
708 push @warns, "System role '$name' already exists.";
712 $group = RT::Group->new( $RT::SystemUser );
713 my ( $val, $msg ) = $group->_Create(
715 Domain => 'RT::System-Role',
716 Description => 'SystemRolegroup for internal use', # loc
720 return ($val, $msg) unless $val;
723 push @warns, "You appear to have a functional RT database."
726 return (1, join "\n", @warns);
731 Load some sort of data into the database, takes path to a file.
737 my $datafile = shift;
739 # Slurp in stuff to insert from the datafile. Possible things to go in here:-
740 our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
741 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
742 local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
743 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
746 $RT::Logger->debug("Going to load '$datafile' data file");
747 eval { require $datafile }
748 or return (0, "Couldn't load data from '$datafile' for import:\n\nERROR:". $@);
751 $RT::Logger->debug("Running initial actions...");
752 foreach ( @Initial ) {
754 eval { $_->(); 1 } or return (0, "One of initial functions failed: $@");
756 $RT::Logger->debug("Done.");
759 $RT::Logger->debug("Creating groups...");
760 foreach my $item (@Groups) {
761 my $new_entry = RT::Group->new( $RT::SystemUser );
762 my $member_of = delete $item->{'MemberOf'};
763 my ( $return, $msg ) = $new_entry->_Create(%$item);
765 $RT::Logger->error( $msg );
768 $RT::Logger->debug($return .".");
771 $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
772 foreach( @$member_of ) {
773 my $parent = RT::Group->new($RT::SystemUser);
774 if ( ref $_ eq 'HASH' ) {
775 $parent->LoadByCols( %$_ );
778 $parent->LoadUserDefinedGroup( $_ );
782 "(Error: wrong format of MemberOf field."
783 ." Should be name of user defined group or"
784 ." hash reference with 'column => value' pairs."
785 ." Use array reference to add to multiple groups)"
789 unless ( $parent->Id ) {
790 $RT::Logger->error("(Error: couldn't load group to add member)");
793 my ( $return, $msg ) = $parent->AddMember( $new_entry->Id );
795 $RT::Logger->error( $msg );
797 $RT::Logger->debug( $return ."." );
802 $RT::Logger->debug("done.");
805 $RT::Logger->debug("Creating users...");
806 foreach my $item (@Users) {
807 my $new_entry = new RT::User( $RT::SystemUser );
808 my ( $return, $msg ) = $new_entry->Create(%$item);
810 $RT::Logger->error( $msg );
812 $RT::Logger->debug( $return ."." );
815 $RT::Logger->debug("done.");
818 $RT::Logger->debug("Creating queues...");
819 for my $item (@Queues) {
820 my $new_entry = new RT::Queue($RT::SystemUser);
821 my ( $return, $msg ) = $new_entry->Create(%$item);
823 $RT::Logger->error( $msg );
825 $RT::Logger->debug( $return ."." );
828 $RT::Logger->debug("done.");
830 if ( @CustomFields ) {
831 $RT::Logger->debug("Creating custom fields...");
832 for my $item ( @CustomFields ) {
833 my $new_entry = new RT::CustomField( $RT::SystemUser );
834 my $values = delete $item->{'Values'};
837 # if ref then it's list of queues, so we do things ourself
838 if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
839 $item->{'LookupType'} ||= 'RT::Queue-RT::Ticket';
840 @queues = @{ delete $item->{'Queue'} };
843 my ( $return, $msg ) = $new_entry->Create(%$item);
845 $RT::Logger->error( $msg );
849 if ( $item->{'BasedOn'} ) {
850 my $basedon = RT::CustomField->new($RT::SystemUser);
851 my ($ok, $msg ) = $basedon->LoadByCols( Name => $item->{'BasedOn'},
852 LookupType => $new_entry->LookupType );
854 ($ok, $msg) = $new_entry->SetBasedOn( $basedon );
856 $RT::Logger->debug("Added BasedOn $item->{BasedOn}: $msg");
858 $RT::Logger->error("Failed to add basedOn $item->{BasedOn}: $msg");
861 $RT::Logger->error("Unable to load $item->{BasedOn} as a $item->{LookupType} CF. Skipping BasedOn");
865 foreach my $value ( @{$values} ) {
866 my ( $return, $msg ) = $new_entry->AddValue(%$value);
867 $RT::Logger->error( $msg ) unless $return;
871 if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
872 my $ocf = RT::ObjectCustomField->new($RT::SystemUser);
873 $ocf->Create( CustomField => $new_entry->Id );
876 for my $q (@queues) {
877 my $q_obj = RT::Queue->new($RT::SystemUser);
879 unless ( $q_obj->Id ) {
880 $RT::Logger->error("Could not find queue ". $q );
883 my $OCF = RT::ObjectCustomField->new($RT::SystemUser);
884 ( $return, $msg ) = $OCF->Create(
885 CustomField => $new_entry->Id,
886 ObjectId => $q_obj->Id,
888 $RT::Logger->error( $msg ) unless $return and $OCF->Id;
892 $RT::Logger->debug("done.");
895 $RT::Logger->debug("Creating ACL...");
896 for my $item (@ACL) {
898 my ($princ, $object);
900 # Global rights or Queue rights?
901 if ( $item->{'CF'} ) {
902 $object = RT::CustomField->new( $RT::SystemUser );
903 my @columns = ( Name => $item->{'CF'} );
904 push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
905 $object->LoadByName( @columns );
906 } elsif ( $item->{'Queue'} ) {
907 $object = RT::Queue->new($RT::SystemUser);
908 $object->Load( $item->{'Queue'} );
910 $object = $RT::System;
913 $RT::Logger->error("Couldn't load object") and next unless $object and $object->Id;
915 # Group rights or user rights?
916 if ( $item->{'GroupDomain'} ) {
917 $princ = RT::Group->new($RT::SystemUser);
918 if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
919 $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
920 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
921 $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
922 } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
923 $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
924 } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
927 $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
928 Queue => $object->id);
930 $princ->Load( $item->{'GroupId'} );
933 $princ = RT::User->new($RT::SystemUser);
934 $princ->Load( $item->{'UserId'} );
938 my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
939 Right => $item->{'Right'},
943 $RT::Logger->error( $msg );
946 $RT::Logger->debug( $return ."." );
949 $RT::Logger->debug("done.");
952 if ( @ScripActions ) {
953 $RT::Logger->debug("Creating ScripActions...");
955 for my $item (@ScripActions) {
956 my $new_entry = RT::ScripAction->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.");
969 if ( @ScripConditions ) {
970 $RT::Logger->debug("Creating ScripConditions...");
972 for my $item (@ScripConditions) {
973 my $new_entry = RT::ScripCondition->new($RT::SystemUser);
974 my ( $return, $msg ) = $new_entry->Create(%$item);
976 $RT::Logger->error( $msg );
979 $RT::Logger->debug( $return ."." );
983 $RT::Logger->debug("done.");
987 $RT::Logger->debug("Creating templates...");
989 for my $item (@Templates) {
990 my $new_entry = new RT::Template($RT::SystemUser);
991 my ( $return, $msg ) = $new_entry->Create(%$item);
993 $RT::Logger->error( $msg );
996 $RT::Logger->debug( $return ."." );
999 $RT::Logger->debug("done.");
1002 $RT::Logger->debug("Creating scrips...");
1004 for my $item (@Scrips) {
1005 my $new_entry = new RT::Scrip($RT::SystemUser);
1007 my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
1008 push @queues, 0 unless @queues; # add global queue at least
1010 foreach my $q ( @queues ) {
1011 my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
1012 unless ( $return ) {
1013 $RT::Logger->error( $msg );
1016 $RT::Logger->debug( $return ."." );
1020 $RT::Logger->debug("done.");
1022 if ( @Attributes ) {
1023 $RT::Logger->debug("Creating predefined searches...");
1024 my $sys = RT::System->new($RT::SystemUser);
1026 for my $item (@Attributes) {
1027 my $obj = delete $item->{Object}; # XXX: make this something loadable
1029 my ( $return, $msg ) = $obj->AddAttribute (%$item);
1030 unless ( $return ) {
1031 $RT::Logger->error( $msg );
1034 $RT::Logger->debug( $return ."." );
1037 $RT::Logger->debug("done.");
1040 $RT::Logger->debug("Running final actions...");
1044 $RT::Logger->error( "Failed to run one of final actions: $@" )
1047 $RT::Logger->debug("done.");
1050 my $db_type = RT->Config->Get('DatabaseType');
1051 $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
1053 $RT::Logger->debug("Done setting up database content.");
1055 # TODO is it ok to return 1 here? If so, the previous codes in this sub
1056 # should return (0, $msg) if error happens instead of just warning.
1057 # anyway, we need to return something here to tell if everything is ok
1058 return( 1, 'Done inserting data' );
1061 =head2 ACLEquivGroupId
1063 Given a userid, return that user's acl equivalence group
1067 sub ACLEquivGroupId {
1070 my $cu = $RT::SystemUser;
1072 require RT::CurrentUser;
1073 $cu = new RT::CurrentUser;
1074 $cu->LoadByName('RT_System');
1075 warn "Couldn't load RT_System user" unless $cu->id;
1078 my $equiv_group = RT::Group->new( $cu );
1079 $equiv_group->LoadACLEquivalenceGroup( $id );
1080 return $equiv_group->Id;
1083 __PACKAGE__->FinalizeDatabaseType;
1085 RT::Base->_ImportOverlays();