1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2012 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
78 =head2 FinalizeDatabaseType
80 Sets RT::Handle's superclass to the correct subclass of
81 L<DBIx::SearchBuilder::Handle>, using the C<DatabaseType> configuration.
85 sub FinalizeDatabaseType {
87 use base "DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType');
91 die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
92 "Perhaps you've picked an invalid database type or spelled it incorrectly.\n".
99 Connects to RT's database using credentials and options from the RT config.
108 my $db_type = RT->Config->Get('DatabaseType');
109 if ( $db_type eq 'Oracle' ) {
110 $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
111 $ENV{'NLS_NCHAR'} = "AL32UTF8";
114 $self->SUPER::Connect(
115 User => RT->Config->Get('DatabaseUser'),
116 Password => RT->Config->Get('DatabasePassword'),
120 if ( $db_type eq 'mysql' ) {
121 my $version = $self->DatabaseVersion;
122 ($version) = $version =~ /^(\d+\.\d+)/;
123 $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
127 if ( $db_type eq 'Pg' ) {
128 my $version = $self->DatabaseVersion;
129 ($version) = $version =~ /^(\d+\.\d+)/;
130 $self->dbh->do("SET bytea_output = 'escape'") if $version >= 9.0;
135 $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
140 Build the DSN for the RT database. Doesn't take any parameters, draws all that
148 # Unless the database port is a positive integer, we really don't want to pass it.
149 my $db_port = RT->Config->Get('DatabasePort');
150 $db_port = undef unless (defined $db_port && $db_port =~ /^(\d+)$/);
151 my $db_host = RT->Config->Get('DatabaseHost');
152 $db_host = undef unless $db_host;
153 my $db_name = RT->Config->Get('DatabaseName');
154 my $db_type = RT->Config->Get('DatabaseType');
155 $db_name = File::Spec->catfile($RT::VarPath, $db_name)
156 if $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name);
160 Database => $db_name,
163 RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
164 DisconnectHandleOnDestroy => 1,
166 if ( $db_type eq 'Oracle' && $db_host ) {
167 $args{'SID'} = delete $args{'Database'};
169 $self->SUPER::BuildDSN( %args );
174 Returns the DSN for this handle. In order to get correct value you must
175 build DSN first, see L</BuildDSN>.
177 This is method can be called as class method, in this case creates
178 temporary handle object, L</BuildDSN builds DSN> and returns it.
184 return $self->SUPER::DSN if ref $self;
186 my $handle = $self->new;
193 Returns a DSN suitable for database creates and drops
194 and user creates and drops.
196 Gets RT's DSN first (see L<DSN>) and then change it according
197 to requirements of a database system RT's using.
204 my $db_name = RT->Config->Get('DatabaseName');
205 my $db_type = RT->Config->Get('DatabaseType');
207 my $dsn = $self->DSN;
208 if ( $db_type eq 'mysql' ) {
209 # with mysql, you want to connect sans database to funge things
210 $dsn =~ s/dbname=\Q$db_name//;
212 elsif ( $db_type eq 'Pg' ) {
213 # with postgres, you want to connect to template1 database
214 $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
219 =head2 Database compatibility and integrity checks
227 $self = new $self unless ref $self;
231 unless ( eval { RT::ConnectToDatabase(); 1 } ) {
232 return (0, 'no connection', "$@");
238 require RT::CurrentUser;
239 my $test_user = RT::CurrentUser->new;
240 $test_user->Load('RT_System');
241 unless ( $test_user->id ) {
242 return (0, 'no system user', "Couldn't find RT_System user in the DB '". $self->DSN ."'");
245 $test_user = RT::CurrentUser->new;
246 $test_user->Load('Nobody');
247 unless ( $test_user->id ) {
248 return (0, 'no nobody user', "Couldn't find Nobody user in the DB '". $self->DSN ."'");
251 return $RT::Handle->dbh;
254 sub CheckCompatibility {
257 my $state = shift || 'post';
259 my $db_type = RT->Config->Get('DatabaseType');
260 if ( $db_type eq "mysql" ) {
261 # Check which version we're running
262 my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
263 return (0, "couldn't get version of the mysql server")
266 ($version) = $version =~ /^(\d+\.\d+)/;
267 return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
270 # MySQL must have InnoDB support
271 my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
272 if ( lc $innodb eq "no" ) {
273 return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
274 "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
275 } elsif ( lc $innodb eq "disabled" ) {
276 return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
277 "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
280 if ( $state eq 'post' ) {
281 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Tickets")->[1];
282 unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
283 return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
286 if ( $version >= 4.1 && $state eq 'post' ) {
287 my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
288 unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
289 return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
290 ."Follow instructions in the UPGRADING.mysql file.");
300 my $dbh = $RT::Handle->dbh;
301 local $dbh->{'RaiseError'} = 0;
302 local $dbh->{'PrintError'} = 0;
303 my $has = ($dbh->selectrow_array("show variables like 'have_sphinx'"))[1];
304 $has ||= ($dbh->selectrow_array(
305 "select 'yes' from INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME = 'sphinx' AND PLUGIN_STATUS='active'"
308 return 0 unless lc($has||'') eq "yes";
312 =head2 Database maintanance
314 =head3 CreateDatabase $DBH
316 Creates a new database. This method can be used as class method.
318 Takes DBI handle. Many database systems require special handle to
319 allow you to create a new database, so you have to use L<SystemDSN>
320 method during connection.
322 Fetches type and name of the DB from the config.
328 my $dbh = shift or return (0, "No DBI handle provided");
329 my $db_type = RT->Config->Get('DatabaseType');
330 my $db_name = RT->Config->Get('DatabaseName');
333 if ( $db_type eq 'SQLite' ) {
334 return (1, 'Skipped as SQLite doesn\'t need any action');
336 elsif ( $db_type eq 'Oracle' ) {
337 my $db_user = RT->Config->Get('DatabaseUser');
338 my $db_pass = RT->Config->Get('DatabasePassword');
340 "CREATE USER $db_user IDENTIFIED BY $db_pass"
341 ." default tablespace USERS"
342 ." temporary tablespace TEMP"
343 ." quota unlimited on USERS"
346 return $status, "Couldn't create user $db_user identified by $db_pass."
347 ."\nError: ". $dbh->errstr;
349 $status = $dbh->do( "GRANT connect, resource TO $db_user" );
351 return $status, "Couldn't grant connect and resource to $db_user."
352 ."\nError: ". $dbh->errstr;
354 return (1, "Created user $db_user. All RT's objects should be in his schema.");
356 elsif ( $db_type eq 'Pg' ) {
357 $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0");
360 $status = $dbh->do("CREATE DATABASE $db_name");
362 return ($status, $DBI::errstr);
365 =head3 DropDatabase $DBH
367 Drops RT's database. This method can be used as class method.
369 Takes DBI handle as first argument. Many database systems require
370 a special handle to allow you to drop a database, so you may have
371 to use L<SystemDSN> when acquiring the DBI handle.
373 Fetches the type and name of the database from the config.
379 my $dbh = shift or return (0, "No DBI handle provided");
381 my $db_type = RT->Config->Get('DatabaseType');
382 my $db_name = RT->Config->Get('DatabaseName');
384 if ( $db_type eq 'Oracle' ) {
385 my $db_user = RT->Config->Get('DatabaseUser');
386 my $status = $dbh->do( "DROP USER $db_user CASCADE" );
388 return 0, "Couldn't drop user $db_user."
389 ."\nError: ". $dbh->errstr;
391 return (1, "Successfully dropped user '$db_user' with his schema.");
393 elsif ( $db_type eq 'SQLite' ) {
395 $path = "$RT::VarPath/$path" unless substr($path, 0, 1) eq '/';
396 unlink $path or return (0, "Couldn't remove '$path': $!");
399 $dbh->do("DROP DATABASE ". $db_name)
400 or return (0, $DBI::errstr);
412 my $base_path = shift || $RT::EtcPath;
414 my $db_type = RT->Config->Get('DatabaseType');
415 return (1) if $db_type eq 'SQLite';
417 $dbh = $self->dbh if !$dbh && ref $self;
418 return (0, "No DBI handle provided") unless $dbh;
420 return (0, "'$base_path' doesn't exist") unless -e $base_path;
423 if ( -d $base_path ) {
424 $path = File::Spec->catfile( $base_path, "acl.$db_type");
425 $path = $self->GetVersionFile($dbh, $path);
427 $path = File::Spec->catfile( $base_path, "acl")
428 unless $path && -e $path;
429 return (0, "Couldn't find ACLs for $db_type")
436 do $path || return (0, "Couldn't load ACLs: " . $@);
438 foreach my $statement (@acl) {
439 my $sth = $dbh->prepare($statement)
440 or return (0, "Couldn't prepare SQL query:\n $statement\n\nERROR: ". $dbh->errstr);
441 unless ( $sth->execute ) {
442 return (0, "Couldn't run SQL query:\n $statement\n\nERROR: ". $sth->errstr);
455 my $base_path = (shift || $RT::EtcPath);
457 $dbh = $self->dbh if !$dbh && ref $self;
458 return (0, "No DBI handle provided") unless $dbh;
460 my $db_type = RT->Config->Get('DatabaseType');
463 if ( -d $base_path ) {
464 $file = $base_path . "/schema." . $db_type;
469 $file = $self->GetVersionFile( $dbh, $file );
471 return (0, "Couldn't find schema file(s) '$file*'");
473 unless ( -f $file && -r $file ) {
474 return (0, "File '$file' doesn't exist or couldn't be read");
479 open( my $fh_schema, '<', $file ) or die $!;
482 open( my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type ))
486 foreach my $line ( <$fh_schema>, ($_ = ';;'), $has_local? <$fh_schema_local>: () ) {
490 if ( $line =~ /;(\s*)$/ ) {
491 $statement =~ s/;(\s*)$//g;
492 push @schema, $statement;
496 close $fh_schema; close $fh_schema_local;
498 if ( $db_type eq 'Oracle' ) {
499 my $db_user = RT->Config->Get('DatabaseUser');
500 my $status = $dbh->do( "ALTER SESSION SET CURRENT_SCHEMA=$db_user" );
502 return $status, "Couldn't set current schema to $db_user."
503 ."\nError: ". $dbh->errstr;
507 local $SIG{__WARN__} = sub {};
509 $dbh->begin_work or return (0, "Couldn't begin transaction: ". $dbh->errstr);
510 foreach my $statement (@schema) {
511 if ( $statement =~ /^\s*;$/ ) {
515 my $sth = $dbh->prepare($statement)
516 or return (0, "Couldn't prepare SQL query:\n$statement\n\nERROR: ". $dbh->errstr);
517 unless ( $sth->execute or $is_local ) {
518 return (0, "Couldn't run SQL query:\n$statement\n\nERROR: ". $sth->errstr);
521 $dbh->commit or return (0, "Couldn't commit transaction: ". $dbh->errstr);
525 =head1 GetVersionFile
527 Takes base name of the file as argument, scans for <base name>-<version> named
528 files and returns file name with closest version to the version of the RT DB.
535 my $base_name = shift;
537 my $db_version = ref $self
538 ? $self->DatabaseVersion
540 my $tmp = RT::Handle->new;
542 $tmp->DatabaseVersion;
546 my @files = File::Glob::bsd_glob("$base_name*");
547 return '' unless @files;
549 my %version = map { $_ =~ /\.\w+-([-\w\.]+)$/; ($1||0) => $_ } @files;
551 foreach ( reverse sort cmp_version keys %version ) {
552 if ( cmp_version( $db_version, $_ ) >= 0 ) {
558 return defined $version? $version{ $version } : undef;
570 sub cmp_version($$) {
572 my @a = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef }
573 split /([^0-9]+)/, $a;
574 my @b = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef }
575 split /([^0-9]+)/, $b;
577 ? push @b, (0) x (@a-@b)
578 : push @a, (0) x (@b-@a);
579 for ( my $i = 0; $i < @a; $i++ ) {
580 return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
586 =head2 InsertInitialData
588 Inserts system objects into RT's DB, like system user or 'nobody',
589 internal groups and other records required. However, this method
590 doesn't insert any real users like 'root' and you have to use
591 InsertData or another way to do that.
593 Takes no arguments. Returns status and message tuple.
595 It's safe to call this method even if those objects already exist.
599 sub InsertInitialData {
604 # create RT_System user and grant him rights
606 require RT::CurrentUser;
608 my $test_user = RT::User->new( RT::CurrentUser->new() );
609 $test_user->Load('RT_System');
610 if ( $test_user->id ) {
611 push @warns, "Found system user in the DB.";
614 my $user = RT::User->new( RT::CurrentUser->new() );
615 my ( $val, $msg ) = $user->_BootstrapCreate(
617 RealName => 'The RT System itself',
618 Comments => 'Do not delete or modify this user. '
619 . 'It is integral to RT\'s internal database structures',
621 LastUpdatedBy => '1',
623 return ($val, $msg) unless $val;
625 DBIx::SearchBuilder::Record::Cachable->FlushCache;
628 # init RT::SystemUser and RT::System objects
629 RT::InitSystemObjects();
630 unless ( RT->SystemUser->id ) {
631 return (0, "Couldn't load system user");
634 # grant SuperUser right to system user
636 my $test_ace = RT::ACE->new( RT->SystemUser );
637 $test_ace->LoadByCols(
638 PrincipalId => ACLEquivGroupId( RT->SystemUser->Id ),
639 PrincipalType => 'Group',
640 RightName => 'SuperUser',
641 ObjectType => 'RT::System',
644 if ( $test_ace->id ) {
645 push @warns, "System user has global SuperUser right.";
647 my $ace = RT::ACE->new( RT->SystemUser );
648 my ( $val, $msg ) = $ace->_BootstrapCreate(
649 PrincipalId => ACLEquivGroupId( RT->SystemUser->Id ),
650 PrincipalType => 'Group',
651 RightName => 'SuperUser',
652 ObjectType => 'RT::System',
655 return ($val, $msg) unless $val;
657 DBIx::SearchBuilder::Record::Cachable->FlushCache;
661 # $self->loc('Everyone'); # For the string extractor to get a string to localize
662 # $self->loc('Privileged'); # For the string extractor to get a string to localize
663 # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
664 foreach my $name (qw(Everyone Privileged Unprivileged)) {
665 my $group = RT::Group->new( RT->SystemUser );
666 $group->LoadSystemInternalGroup( $name );
668 push @warns, "System group '$name' already exists.";
672 $group = RT::Group->new( RT->SystemUser );
673 my ( $val, $msg ) = $group->_Create(
675 Domain => 'SystemInternal',
676 Description => 'Pseudogroup for internal use', # loc
680 return ($val, $msg) unless $val;
685 my $user = RT::User->new( RT->SystemUser );
686 $user->Load('Nobody');
688 push @warns, "Found 'Nobody' user in the DB.";
691 my ( $val, $msg ) = $user->Create(
693 RealName => 'Nobody in particular',
694 Comments => 'Do not delete or modify this user. It is integral '
695 .'to RT\'s internal data structures',
698 return ($val, $msg) unless $val;
701 if ( $user->HasRight( Right => 'OwnTicket', Object => $RT::System ) ) {
702 push @warns, "User 'Nobody' has global OwnTicket right.";
704 my ( $val, $msg ) = $user->PrincipalObj->GrantRight(
705 Right => 'OwnTicket',
706 Object => $RT::System,
708 return ($val, $msg) unless $val;
712 # rerun to get init Nobody as well
713 RT::InitSystemObjects();
716 foreach my $name (qw(Owner Requestor Cc AdminCc)) {
717 my $group = RT::Group->new( RT->SystemUser );
718 $group->LoadSystemRoleGroup( $name );
720 push @warns, "System role '$name' already exists.";
724 $group = RT::Group->new( RT->SystemUser );
725 my ( $val, $msg ) = $group->_Create(
727 Domain => 'RT::System-Role',
728 Description => 'SystemRolegroup for internal use', # loc
732 return ($val, $msg) unless $val;
735 push @warns, "You appear to have a functional RT database."
738 return (1, join "\n", @warns);
743 Load some sort of data into the database, takes path to a file.
749 my $datafile = shift;
750 my $root_password = shift;
752 # Slurp in stuff to insert from the datafile. Possible things to go in here:-
753 our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
754 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
755 local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
756 @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
759 $RT::Logger->debug("Going to load '$datafile' data file");
760 eval { require $datafile }
761 or return (0, "Couldn't load data from '$datafile' for import:\n\nERROR:". $@);
764 $RT::Logger->debug("Running initial actions...");
765 foreach ( @Initial ) {
767 eval { $_->(); 1 } or return (0, "One of initial functions failed: $@");
769 $RT::Logger->debug("Done.");
772 $RT::Logger->debug("Creating groups...");
773 foreach my $item (@Groups) {
774 my $new_entry = RT::Group->new( RT->SystemUser );
775 my $member_of = delete $item->{'MemberOf'};
776 my ( $return, $msg ) = $new_entry->_Create(%$item);
778 $RT::Logger->error( $msg );
781 $RT::Logger->debug($return .".");
784 $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
785 foreach( @$member_of ) {
786 my $parent = RT::Group->new(RT->SystemUser);
787 if ( ref $_ eq 'HASH' ) {
788 $parent->LoadByCols( %$_ );
791 $parent->LoadUserDefinedGroup( $_ );
795 "(Error: wrong format of MemberOf field."
796 ." Should be name of user defined group or"
797 ." hash reference with 'column => value' pairs."
798 ." Use array reference to add to multiple groups)"
802 unless ( $parent->Id ) {
803 $RT::Logger->error("(Error: couldn't load group to add member)");
806 my ( $return, $msg ) = $parent->AddMember( $new_entry->Id );
808 $RT::Logger->error( $msg );
810 $RT::Logger->debug( $return ."." );
815 $RT::Logger->debug("done.");
818 $RT::Logger->debug("Creating users...");
819 foreach my $item (@Users) {
820 if ( $item->{'Name'} eq 'root' && $root_password ) {
821 $item->{'Password'} = $root_password;
823 my $new_entry = RT::User->new( RT->SystemUser );
824 my ( $return, $msg ) = $new_entry->Create(%$item);
826 $RT::Logger->error( $msg );
828 $RT::Logger->debug( $return ."." );
831 $RT::Logger->debug("done.");
834 $RT::Logger->debug("Creating queues...");
835 for my $item (@Queues) {
836 my $new_entry = RT::Queue->new(RT->SystemUser);
837 my ( $return, $msg ) = $new_entry->Create(%$item);
839 $RT::Logger->error( $msg );
841 $RT::Logger->debug( $return ."." );
844 $RT::Logger->debug("done.");
846 if ( @CustomFields ) {
847 $RT::Logger->debug("Creating custom fields...");
848 for my $item ( @CustomFields ) {
849 my $new_entry = RT::CustomField->new( RT->SystemUser );
850 my $values = delete $item->{'Values'};
853 # if ref then it's list of queues, so we do things ourself
854 if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
855 $item->{'LookupType'} ||= 'RT::Queue-RT::Ticket';
856 @queues = @{ delete $item->{'Queue'} };
859 my ( $return, $msg ) = $new_entry->Create(%$item);
861 $RT::Logger->error( $msg );
865 if ( $item->{'BasedOn'} ) {
866 my $basedon = RT::CustomField->new($RT::SystemUser);
867 my ($ok, $msg ) = $basedon->LoadByCols( Name => $item->{'BasedOn'},
868 LookupType => $new_entry->LookupType );
870 ($ok, $msg) = $new_entry->SetBasedOn( $basedon );
872 $RT::Logger->debug("Added BasedOn $item->{BasedOn}: $msg");
874 $RT::Logger->error("Failed to add basedOn $item->{BasedOn}: $msg");
877 $RT::Logger->error("Unable to load $item->{BasedOn} as a $item->{LookupType} CF. Skipping BasedOn");
881 foreach my $value ( @{$values} ) {
882 my ( $return, $msg ) = $new_entry->AddValue(%$value);
883 $RT::Logger->error( $msg ) unless $return;
887 if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
888 my $ocf = RT::ObjectCustomField->new(RT->SystemUser);
889 $ocf->Create( CustomField => $new_entry->Id );
892 for my $q (@queues) {
893 my $q_obj = RT::Queue->new(RT->SystemUser);
895 unless ( $q_obj->Id ) {
896 $RT::Logger->error("Could not find queue ". $q );
899 my $OCF = RT::ObjectCustomField->new(RT->SystemUser);
900 ( $return, $msg ) = $OCF->Create(
901 CustomField => $new_entry->Id,
902 ObjectId => $q_obj->Id,
904 $RT::Logger->error( $msg ) unless $return and $OCF->Id;
908 $RT::Logger->debug("done.");
911 $RT::Logger->debug("Creating ACL...");
912 for my $item (@ACL) {
914 my ($princ, $object);
916 # Global rights or Queue rights?
917 if ( $item->{'CF'} ) {
918 $object = RT::CustomField->new( RT->SystemUser );
919 my @columns = ( Name => $item->{'CF'} );
920 push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
921 $object->LoadByName( @columns );
922 } elsif ( $item->{'Queue'} ) {
923 $object = RT::Queue->new(RT->SystemUser);
924 $object->Load( $item->{'Queue'} );
926 $object = $RT::System;
929 $RT::Logger->error("Couldn't load object") and next unless $object and $object->Id;
931 # Group rights or user rights?
932 if ( $item->{'GroupDomain'} ) {
933 $princ = RT::Group->new(RT->SystemUser);
934 if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
935 $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
936 } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
937 $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
938 } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
939 $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
940 } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
943 $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
944 Queue => $object->id);
946 $princ->Load( $item->{'GroupId'} );
948 unless ( $princ->Id ) {
949 RT->Logger->error("Unable to load Group: GroupDomain => $item->{GroupDomain}, GroupId => $item->{GroupId}, Queue => $item->{Queue}");
953 $princ = RT::User->new(RT->SystemUser);
954 my ($ok, $msg) = $princ->Load( $item->{'UserId'} );
956 RT->Logger->error("Unable to load user: $item->{UserId} : $msg");
962 my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
963 Right => $item->{'Right'},
967 $RT::Logger->error( $msg );
970 $RT::Logger->debug( $return ."." );
973 $RT::Logger->debug("done.");
976 if ( @ScripActions ) {
977 $RT::Logger->debug("Creating ScripActions...");
979 for my $item (@ScripActions) {
980 my $new_entry = RT::ScripAction->new(RT->SystemUser);
981 my ( $return, $msg ) = $new_entry->Create(%$item);
983 $RT::Logger->error( $msg );
986 $RT::Logger->debug( $return ."." );
990 $RT::Logger->debug("done.");
993 if ( @ScripConditions ) {
994 $RT::Logger->debug("Creating ScripConditions...");
996 for my $item (@ScripConditions) {
997 my $new_entry = RT::ScripCondition->new(RT->SystemUser);
998 my ( $return, $msg ) = $new_entry->Create(%$item);
1000 $RT::Logger->error( $msg );
1003 $RT::Logger->debug( $return ."." );
1007 $RT::Logger->debug("done.");
1011 $RT::Logger->debug("Creating templates...");
1013 for my $item (@Templates) {
1014 my $new_entry = RT::Template->new(RT->SystemUser);
1015 my ( $return, $msg ) = $new_entry->Create(%$item);
1016 unless ( $return ) {
1017 $RT::Logger->error( $msg );
1020 $RT::Logger->debug( $return ."." );
1023 $RT::Logger->debug("done.");
1026 $RT::Logger->debug("Creating scrips...");
1028 for my $item (@Scrips) {
1029 my $new_entry = RT::Scrip->new(RT->SystemUser);
1031 my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
1032 push @queues, 0 unless @queues; # add global queue at least
1034 foreach my $q ( @queues ) {
1035 my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
1036 unless ( $return ) {
1037 $RT::Logger->error( $msg );
1040 $RT::Logger->debug( $return ."." );
1044 $RT::Logger->debug("done.");
1046 if ( @Attributes ) {
1047 $RT::Logger->debug("Creating attributes...");
1048 my $sys = RT::System->new(RT->SystemUser);
1050 for my $item (@Attributes) {
1051 my $obj = delete $item->{Object}; # XXX: make this something loadable
1053 my ( $return, $msg ) = $obj->AddAttribute (%$item);
1054 unless ( $return ) {
1055 $RT::Logger->error( $msg );
1058 $RT::Logger->debug( $return ."." );
1061 $RT::Logger->debug("done.");
1064 $RT::Logger->debug("Running final actions...");
1068 $RT::Logger->error( "Failed to run one of final actions: $@" )
1071 $RT::Logger->debug("done.");
1074 my $db_type = RT->Config->Get('DatabaseType');
1075 $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
1077 $RT::Logger->debug("Done setting up database content.");
1079 # TODO is it ok to return 1 here? If so, the previous codes in this sub
1080 # should return (0, $msg) if error happens instead of just warning.
1081 # anyway, we need to return something here to tell if everything is ok
1082 return( 1, 'Done inserting data' );
1085 =head2 ACLEquivGroupId
1087 Given a userid, return that user's acl equivalence group
1091 sub ACLEquivGroupId {
1094 my $cu = RT->SystemUser;
1096 require RT::CurrentUser;
1097 $cu = RT::CurrentUser->new;
1098 $cu->LoadByName('RT_System');
1099 warn "Couldn't load RT_System user" unless $cu->id;
1102 my $equiv_group = RT::Group->new( $cu );
1103 $equiv_group->LoadACLEquivalenceGroup( $id );
1104 return $equiv_group->Id;
1109 Returns the SQL query history associated with this handle. The top level array
1110 represents a lists of request. Each request is a hash with metadata about the
1111 request (such as the URL) and a list of queries. You'll probably not be using this.
1118 return $self->{QueryHistory};
1121 =head2 AddRequestToHistory
1123 Adds a web request to the query history. It must be a hash with keys Path (a
1124 string) and Queries (an array reference of arrays, where elements are time,
1125 sql, bind parameters, and duration).
1129 sub AddRequestToHistory {
1131 my $request = shift;
1133 push @{ $self->{QueryHistory} }, $request;
1138 Returns the parameter quoted by DBI. B<You almost certainly do not need this.>
1139 Use bind parameters (C<?>) instead. This is used only outside the scope of interacting
1148 return $self->dbh->quote($value);
1153 Takes a SQL query and an array reference of bind parameters and fills in the
1154 query's C<?> parameters.
1165 # is this regex sufficient?
1166 $sql =~ s{\?}{$self->Quote($bind->[$b++])}eg;
1171 # log a mason stack trace instead of a Carp::longmess because it's less painful
1172 # and uses mason component paths properly
1173 sub _LogSQLStatement {
1175 my $statement = shift;
1176 my $duration = shift;
1179 require HTML::Mason::Exceptions;
1180 push @{$self->{'StatementLog'}} , ([Time::HiRes::time(), $statement, [@bind], $duration, HTML::Mason::Exception->new->as_string]);
1183 __PACKAGE__->FinalizeDatabaseType;
1185 RT::Base->_ImportOverlays();