diff options
author | Ivan Kohler <ivan@freeside.biz> | 2014-02-25 18:34:25 -0800 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2014-02-25 18:34:25 -0800 |
commit | 45d35d5739d05e602bc317739485693e0e9ff0b5 (patch) | |
tree | 61801368d96662baff145d3271fd887ca104391c /rt/sbin | |
parent | 662be3ece2ef8c7f05fcbfaa699d80a6a73ca110 (diff) |
RT 4.0.19
Diffstat (limited to 'rt/sbin')
-rw-r--r-- | rt/sbin/rt-attributes-viewer.in | 2 | ||||
-rw-r--r-- | rt/sbin/rt-clean-sessions.in | 2 | ||||
-rw-r--r-- | rt/sbin/rt-dump-metadata.in | 191 | ||||
-rw-r--r-- | rt/sbin/rt-email-dashboards.in | 2 | ||||
-rw-r--r-- | rt/sbin/rt-email-digest.in | 8 | ||||
-rwxr-xr-x | rt/sbin/rt-email-group-admin.in | 36 | ||||
-rwxr-xr-x | rt/sbin/rt-fulltext-indexer | 465 | ||||
-rw-r--r-- | rt/sbin/rt-fulltext-indexer.in | 6 | ||||
-rwxr-xr-x | rt/sbin/rt-message-catalog | 2 | ||||
-rw-r--r-- | rt/sbin/rt-preferences-viewer.in | 2 | ||||
-rw-r--r-- | rt/sbin/rt-server.fcgi.in | 4 | ||||
-rw-r--r-- | rt/sbin/rt-server.in | 4 | ||||
-rwxr-xr-x | rt/sbin/rt-session-viewer | 121 | ||||
-rw-r--r-- | rt/sbin/rt-session-viewer.in | 2 | ||||
-rw-r--r-- | rt/sbin/rt-setup-database.in | 44 | ||||
-rwxr-xr-x | rt/sbin/rt-setup-fulltext-index | 714 | ||||
-rw-r--r-- | rt/sbin/rt-setup-fulltext-index.in | 2 | ||||
-rwxr-xr-x | rt/sbin/rt-shredder.in | 8 | ||||
-rw-r--r-- | rt/sbin/rt-test-dependencies.in | 3 | ||||
-rw-r--r-- | rt/sbin/rt-validate-aliases.in | 2 | ||||
-rw-r--r-- | rt/sbin/rt-validator.in | 2 | ||||
-rwxr-xr-x | rt/sbin/standalone_httpd | 284 | ||||
-rw-r--r-- | rt/sbin/standalone_httpd.in | 4 |
23 files changed, 236 insertions, 1674 deletions
diff --git a/rt/sbin/rt-attributes-viewer.in b/rt/sbin/rt-attributes-viewer.in index 59eb42361..31bae00c7 100644 --- a/rt/sbin/rt-attributes-viewer.in +++ b/rt/sbin/rt-attributes-viewer.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-clean-sessions.in b/rt/sbin/rt-clean-sessions.in index 1d6e1cfb5..77730fe13 100644 --- a/rt/sbin/rt-clean-sessions.in +++ b/rt/sbin/rt-clean-sessions.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-dump-metadata.in b/rt/sbin/rt-dump-metadata.in index 20c28538b..5e0e5c3db 100644 --- a/rt/sbin/rt-dump-metadata.in +++ b/rt/sbin/rt-dump-metadata.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -79,7 +79,11 @@ BEGIN { use Getopt::Long; my %opt; -GetOptions( \%opt, "help|h" ); +GetOptions( \%opt, "help|h", + "limit-to-privileged|l", + "skip-disabled|s", + "all|a", +); if ( $opt{help} ) { require Pod::Usage; @@ -93,8 +97,6 @@ require XML::Simple; RT::LoadConfig(); RT::Init(); -my $LocalOnly = @ARGV ? shift(@ARGV) : 1; - my %RV; my %Ignore = ( All => [ @@ -117,8 +119,15 @@ my @classes = qw( foreach my $class (@classes) { require "RT/$class.pm"; my $objects = "RT::$class"->new( RT->SystemUser ); - $objects->{find_disabled_rows} = 1; + $objects->{find_disabled_rows} = 1 unless $opt{'skip-disabled'}; $objects->UnLimit; + $objects->LimitToPrivileged if $class eq 'Users' + && $opt{'limit-to-privileged'}; + $objects->Limit( + FIELD => 'Domain', + OPERATOR => '=', + VALUE => 'UserDefined' + ) if $class eq 'Groups'; if ( $class eq 'CustomFields' ) { $objects->OrderByCols( @@ -130,7 +139,7 @@ foreach my $class (@classes) { $objects->OrderBy( FIELD => 'Id' ); } - if ($LocalOnly) { + unless ($opt{all}) { next if $class eq 'ACL'; # XXX - would go into infinite loop - XXX $objects->Limit( FIELD => 'LastUpdatedBy', @@ -142,14 +151,10 @@ foreach my $class (@classes) { OPERATOR => '!=', VALUE => $SystemUserId ) if $class eq 'Users'; - $objects->Limit( - FIELD => 'Domain', - OPERATOR => '=', - VALUE => 'UserDefined' - ) if $class eq 'Groups'; } my %fields; +OBJECT: while ( my $obj = $objects->Next ) { next if $obj->can('LastUpdatedBy') @@ -163,36 +168,116 @@ foreach my $class (@classes) { my $rv; - # next if $obj-> # skip default names - foreach my $field ( sort keys %fields ) { - my $value = $obj->__Value($field); - $rv->{$field} = $value if ( defined($value) && length($value) ); - } - delete $rv->{Disabled} unless $rv->{Disabled}; - - foreach my $record ( map { /ACL/ ? 'ACE' : substr( $_, 0, -1 ) } - @classes ) - { - foreach my $key ( map "$record$_", ( '', 'Id' ) ) { - next unless exists $rv->{$key}; - my $id = $rv->{$key} or next; - my $obj = "RT::$record"->new( RT->SystemUser ); - $obj->LoadByCols( Id => $id ) or next; - $rv->{$key} = $obj->__Value('Name') || 0; + if ( $class ne 'ACL' ) { + # next if $obj-> # skip default names + foreach my $field ( sort keys %fields ) { + my $value = $obj->__Value($field); + $rv->{$field} = $value if ( defined($value) && length($value) ); + } + delete $rv->{Disabled} unless $rv->{Disabled}; + + foreach my $record ( map { /ACL/ ? 'ACE' : substr( $_, 0, -1 ) } + @classes ) + { + foreach my $key ( map "$record$_", ( '', 'Id' ) ) { + next unless exists $rv->{$key}; + my $id = $rv->{$key} or next; + my $obj = "RT::$record"->new( RT->SystemUser ); + $obj->LoadByCols( Id => $id ) or next; + $rv->{$key} = $obj->__Value('Name') || 0; + } + } + + if ( $class eq 'Users' and defined $obj->Privileged ) { + $rv->{Privileged} = int( $obj->Privileged ); + } elsif ( $class eq 'CustomFields' ) { + my $values = $obj->Values; + while ( my $value = $values->Next ) { + push @{ $rv->{Values} }, { + map { ( $_ => $value->__Value($_) ) } + qw( + Name Description SortOrder + ), + }; + } + if ( $obj->LookupType eq 'RT::Queue-RT::Ticket' ) { + # XXX-TODO: unused CF's turn into global CF when importing + # as the sub InsertData in RT::Handle creates a global CF + # when no queue is specified. + $rv->{Queue} = []; + my $applies = $obj->AppliedTo; + while ( my $queue = $applies->Next ) { + push @{ $rv->{Queue} }, $queue->Name; + } + } } } + else { + # 1) pick the right + $rv->{Right} = $obj->RightName; + + # 2) Pick a level: Granted on Queue, CF, CF+Queue, or Globally? + for ( $obj->ObjectType ) { + if ( /^RT::Queue$/ ) { + next OBJECT if $opt{'skip-disabled'} && $obj->Object->Disabled; + $rv->{Queue} = $obj->Object->Name; + } + elsif ( /^RT::CustomField$/ ) { + next OBJECT if $opt{'skip-disabled'} && $obj->Object->Disabled; + $rv->{CF} = $obj->Object->Name; + } + elsif ( /^RT::Group$/ ) { + # No support for RT::Group ACLs in RT::Handle yet. + next OBJECT; + } + elsif ( /^RT::System$/ ) { + # skip setting anything on $rv; + # "Specifying none of the above will get you a global right." + } + } - if ( $class eq 'Users' and defined $obj->Privileged ) { - $rv->{Privileged} = int( $obj->Privileged ); - } elsif ( $class eq 'CustomFields' ) { - my $values = $obj->Values; - while ( my $value = $values->Next ) { - push @{ $rv->{Values} }, { - map { ( $_ => $value->__Value($_) ) } - qw( - Name Description SortOrder - ), - }; + # 3) Pick a Principal; User or Group or Role + if ( $obj->PrincipalType eq 'Group' ) { + next OBJECT if $opt{'skip-disabled'} && $obj->PrincipalObj->Disabled; + my $group = $obj->PrincipalObj->Object; + for ( $group->Domain ) { + # An internal user group + if ( /^SystemInternal$/ ) { + $rv->{GroupDomain} = $group->Domain; + $rv->{GroupType} = $group->Type; + } + # An individual user + elsif ( /^ACLEquivalence$/ ) { + my $member = $group->MembersObj->Next->MemberObj; + next OBJECT if $opt{'skip-disabled'} && $member->Disabled; + $rv->{UserId} = $member->Object->Name; + } + # A group you created + elsif ( /^UserDefined$/ ) { + $rv->{GroupDomain} = 'UserDefined'; + $rv->{GroupId} = $group->Name; + } + } + } else { + $rv->{GroupType} = $obj->PrincipalType; + # A system-level role + if ( $obj->ObjectType eq 'RT::System' ) { + $rv->{GroupDomain} = 'RT::System-Role'; + } + # A queue-level role + elsif ( $obj->ObjectType eq 'RT::Queue' ) { + $rv->{GroupDomain} = 'RT::Queue-Role'; + } + } + if ( $obj->LookupType eq 'RT::Queue-RT::Ticket' ) { + # XXX-TODO: unused CF's turn into global CF when importing + # as the sub InsertData in RT::Handle creates a global CF + # when no queue is specified. + $rv->{Queue} = []; + my $applies = $obj->AppliedTo; + while ( my $queue = $applies->Next ) { + push @{ $rv->{Queue} }, $queue->Name; + } } } @@ -200,6 +285,9 @@ foreach my $class (@classes) { my $attributes = $obj->Attributes; while ( my $attribute = $attributes->Next ) { my $content = $attribute->Content; + if ( $class eq 'Users' and $attribute->Name eq 'Bookmarks' ) { + next; + } $rv->{Attributes}{ $attribute->Name } = $content if length($content); } @@ -232,7 +320,7 @@ rt-dump-metadata - dump configuration metadata from an RT database =head1 SYNOPSIS - rt-dump-metdata [ 0 ] + rt-dump-metdata [--all] =head1 DESCRIPTION @@ -242,11 +330,28 @@ C<rt-setup-database>. To dump and load a full RT database, you should generally use the native database tools instead, as well as performing any necessary steps from UPGRADING. -When run without arguments, the metadata dump will only include 'local' +This is NOT a tool for backing up an RT database. See also +L<docs/initialdata> for more straightforward means of importing data. + +=head1 OPTIONS + +=over + +=item C<--all> or C<-a> + +When run with C<--all>, the dump will include all configuration +metadata; otherwise, the metadata dump will only include 'local' configuration changes, i.e. those done manually in the web interface. -When run with the argument '0', the dump will include all configuration -metadata. +=item C<--limit-to-privileged> or C<-l> + +Causes the dumper to only dump privileged users. + +=item C<--skip-disabled> or C<-s> + +Ignores disabled rows in the database. + +=back -This is NOT a tool for backing up an RT database. +=cut diff --git a/rt/sbin/rt-email-dashboards.in b/rt/sbin/rt-email-dashboards.in index f77ae6117..0fcb1268e 100644 --- a/rt/sbin/rt-email-dashboards.in +++ b/rt/sbin/rt-email-dashboards.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-email-digest.in b/rt/sbin/rt-email-digest.in index 68f0b4c92..a535e3649 100644 --- a/rt/sbin/rt-email-digest.in +++ b/rt/sbin/rt-email-digest.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -95,6 +95,7 @@ sub usage { print "\t-p, --print\t" . loc("Print the resulting digest messages to STDOUT; don't mail them. Do not mark them as sent") . "\n"; + print "\t-v, --verbose\t" . loc("Give output even on messages successfully sent") . "\n"; print "\t-h, --help\t" . loc("Print this message") . "\n"; if ( $error eq 'help' ) { @@ -105,10 +106,11 @@ sub usage { } } -my ( $frequency, $print, $help ) = ( '', '', '' ); +my ( $frequency, $print, $verbose, $help ) = ( '', '', '', '' ); GetOptions( 'mode=s' => \$frequency, 'print' => \$print, + 'verbose' => \$verbose, 'help' => \$help, ); @@ -134,7 +136,7 @@ sub run { my ( $contents_list, $contents_body ) = build_digest_for_user( $user, $all_digest->{$user} ); # Now we have a content head and a content body. We can send a message. if ( send_digest( $user, $contents_list, $contents_body ) ) { - print "Sent message to $user\n"; + print "Sent message to $user\n" if $verbose; mark_transactions_sent( $frequency, $user, values %{$sent_transactions->{$user}} ) unless ($print); } else { print "Failed to send message to $user\n"; diff --git a/rt/sbin/rt-email-group-admin.in b/rt/sbin/rt-email-group-admin.in index f62662631..3306a3812 100755 --- a/rt/sbin/rt-email-group-admin.in +++ b/rt/sbin/rt-email-group-admin.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -245,10 +245,11 @@ sub _list { return; } -=head2 create NAME [--comment] [--group GNAME] [--user UNAME] +=head2 create NAME [--comment] [--group GNAME] [--user NAME-OR-EMAIL] Creates new action with NAME and adds users and/or groups to its -recipient list. Would be notify as comment if --comment specified. +recipient list. Would be notify as comment if --comment specified. The +user, if specified, will be autocreated if necessary. =cut @@ -295,8 +296,9 @@ sub __create_empty { sub _check_groups { - return grep { $_ ? 1: do { print STDERR "Group '$_' skipped, doesn't exist\n"; 0; } } - map { __check_group($_) } @_; + return map {$_->[1]} + grep { $_->[1] ? 1: do { print STDERR "Group '$_->[0]' skipped, doesn't exist\n"; 0; } } + map { [$_, __check_group($_)] } @_; } sub __check_group @@ -310,8 +312,9 @@ sub __check_group sub _check_users { - return grep { $_ ? 1: do { print STDERR "User '$_' skipped, doesn't exist\n"; 0; } } - map { __check_user($_) } @_; + return map {$_->[1]} + grep { $_->[1] ? 1: do { print STDERR "User '$_->[0]' skipped, doesn't exist and couldn't autocreate\n"; 0; } } + map { [$_, __check_user($_)] } @_; } sub __check_user @@ -320,12 +323,27 @@ sub __check_user require RT::User; my $obj = RT::User->new( RT->SystemUser ); $obj->Load( $instance ); + $obj->LoadByEmail( $instance ) + if not $obj->id and $instance =~ /@/; + + unless ($obj->id) { + my ($ok, $msg) = $obj->Create( + Name => $instance, + EmailAddress => $instance, + Privileged => 0, + Comments => 'Autocreated when added to notify action via rt-email-group-admin', + ); + print STDERR "Autocreate of user '$instance' failed: $msg\n" + unless $ok; + } + return $obj->id ? $obj : undef; } -=head2 add NAME [--group GNAME] [--user UNAME] +=head2 add NAME [--group GNAME] [--user NAME-OR-EMAIL] -Adds groups and/or users to recipients of the action NAME. +Adds groups and/or users to recipients of the action NAME. The user, if +specified, will be autocreated if necessary. =cut diff --git a/rt/sbin/rt-fulltext-indexer b/rt/sbin/rt-fulltext-indexer deleted file mode 100755 index 8ac0e9c21..000000000 --- a/rt/sbin/rt-fulltext-indexer +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/perl -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC -# <sales@bestpractical.com> -# -# (Except where explicitly superseded by other copyright notices) -# -# -# LICENSE: -# -# This work is made available to you under the terms of Version 2 of -# the GNU General Public License. A copy of that license should have -# been provided with this software, but in any event can be snarfed -# from www.gnu.org. -# -# This work is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 or visit their web page on the internet at -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# -# CONTRIBUTION SUBMISSION POLICY: -# -# (The following paragraph is not intended to limit the rights granted -# to you to modify and distribute this software under the terms of -# the GNU General Public License and is only of importance to you if -# you choose to contribute your changes and enhancements to the -# community by submitting them to Best Practical Solutions, LLC.) -# -# By intentionally submitting any modifications, corrections or -# derivatives to this work, or any other work intended for use with -# Request Tracker, to Best Practical Solutions, LLC, you confirm that -# you are the copyright holder for those contributions and you grant -# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -# royalty-free, perpetual, license to use, copy, create derivative -# works based on those contributions, and sublicense and distribute -# those contributions and any derivatives thereof. -# -# END BPS TAGGED BLOCK }}} -use strict; -use warnings; -no warnings 'once'; - -# fix lib paths, some may be relative -BEGIN { - require File::Spec; - my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib"); - my $bin_path; - - for my $lib (@libs) { - unless ( File::Spec->file_name_is_absolute($lib) ) { - unless ($bin_path) { - if ( File::Spec->file_name_is_absolute(__FILE__) ) { - $bin_path = ( File::Spec->splitpath(__FILE__) )[1]; - } - else { - require FindBin; - no warnings "once"; - $bin_path = $FindBin::Bin; - } - } - $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib ); - } - unshift @INC, $lib; - } -} - -BEGIN { - use RT; - RT::LoadConfig(); - RT::Init(); -}; -use RT::Interface::CLI (); - -my %OPT = ( - help => 0, - debug => 0, -); -my @OPT_LIST = qw(help|h! debug!); - -my $db_type = RT->Config->Get('DatabaseType'); -if ( $db_type eq 'Pg' ) { - %OPT = ( - %OPT, - limit => 0, - all => 0, - ); - push @OPT_LIST, 'limit=i', 'all!'; -} -elsif ( $db_type eq 'mysql' ) { - %OPT = ( - %OPT, - limit => 0, - all => 0, - xmlpipe2 => 0, - ); - push @OPT_LIST, 'limit=i', 'all!', 'xmlpipe2!'; -} -elsif ( $db_type eq 'Oracle' ) { - %OPT = ( - %OPT, - memory => '2M', - ); - push @OPT_LIST, qw(memory=s); -} - -use Getopt::Long qw(GetOptions); -GetOptions( \%OPT, @OPT_LIST ); - -if ( $OPT{'help'} ) { - RT::Interface::CLI->ShowHelp( - Sections => 'NAME|DESCRIPTION|'. uc($db_type), - ); -} - -my $fts_config = RT->Config->Get('FullTextSearch') || {}; -unless ( $fts_config->{'Enable'} ) { - print STDERR <<EOT; - -Full text search is disabled in your RT configuration. Run -/opt/rt3/sbin/rt-setup-fulltext-index to configure and enable it. - -EOT - exit 1; -} -unless ( $fts_config->{'Indexed'} ) { - print STDERR <<EOT; - -Full text search is enabled in your RT configuration, but not with any -full-text database indexing -- hence this tool is not required. Read -the documentation for %FullTextSearch in your RT_Config for more details. - -EOT - exit 1; -} - -if ( $db_type eq 'Oracle' ) { - my $index = $fts_config->{'IndexName'} || 'rt_fts_index'; - $RT::Handle->dbh->do( - "begin ctx_ddl.sync_index(?, ?); end;", undef, - $index, $OPT{'memory'} - ); - exit; -} elsif ( $db_type eq 'mysql' ) { - unless ($OPT{'xmlpipe2'}) { - print STDERR <<EOT; - -Updates to the external Sphinx index are done via running the sphinx -`indexer` tool: - - indexer rt - -EOT - exit 1; - } -} - -my @types = qw(text html); -foreach my $type ( @types ) { - REDO: - my $attachments = attachments($type); - $attachments->Limit( - FIELD => 'id', - OPERATOR => '>', - VALUE => last_indexed($type) - ); - $attachments->OrderBy( FIELD => 'id', ORDER => 'asc' ); - $attachments->RowsPerPage( $OPT{'limit'} || 100 ); - - my $found = 0; - while ( my $a = $attachments->Next ) { - next if filter( $type, $a ); - debug("Found attachment #". $a->id ); - my $txt = extract($type, $a) or next; - $found++; - process( $type, $a, $txt ); - debug("Processed attachment #". $a->id ); - } - finalize( $type, $attachments ) if $found; - clean( $type ); - goto REDO if $OPT{'all'} and $attachments->Count == ($OPT{'limit'} || 100) -} - -sub attachments { - my $type = shift; - my $res = RT::Attachments->new( RT->SystemUser ); - my $txn_alias = $res->Join( - ALIAS1 => 'main', - FIELD1 => 'TransactionId', - TABLE2 => 'Transactions', - FIELD2 => 'id', - ); - $res->Limit( - ALIAS => $txn_alias, - FIELD => 'ObjectType', - VALUE => 'RT::Ticket', - ); - my $ticket_alias = $res->Join( - ALIAS1 => $txn_alias, - FIELD1 => 'ObjectId', - TABLE2 => 'Tickets', - FIELD2 => 'id', - ); - $res->Limit( - ALIAS => $ticket_alias, - FIELD => 'Status', - OPERATOR => '!=', - VALUE => 'deleted' - ); - - # On newer DBIx::SearchBuilder's, indicate that making the query DISTINCT - # is unnecessary because the joins won't produce duplicates. This - # drastically improves performance when fetching attachments. - $res->{joins_are_distinct} = 1; - - return goto_specific( - suffix => $type, - error => "Don't know how to find $type attachments", - arguments => [$res], - ); -} - -sub last_indexed { - my ($type) = (@_); - return goto_specific( - suffix => $db_type, - error => "Don't know how to find last indexed $type attachment for $db_type DB", - arguments => \@_, - ); -} - -sub filter { - my $type = shift; - return goto_specific( - suffix => $type, - arguments => \@_, - ); -} - -sub extract { - my $type = shift; - return goto_specific( - suffix => $type, - error => "No way to convert $type attachment into text", - arguments => \@_, - ); -} - -sub process { - return goto_specific( - suffix => $db_type, - error => "No processer for $db_type DB", - arguments => \@_, - ); -} - -sub finalize { - return goto_specific( - suffix => $db_type, - arguments => \@_, - ); -} - -sub clean { - return goto_specific( - suffix => $db_type, - arguments => \@_, - ); -} - -{ -sub last_indexed_mysql { - my $type = shift; - my $attr = $RT::System->FirstAttribute('LastIndexedAttachments'); - return 0 unless $attr; - return 0 unless exists $attr->{ $type }; - return $attr->{ $type } || 0; -} - -sub process_mysql { - my ($type, $attachment, $text) = (@_); - - my $doc = sphinx_template(); - - my $element = $doc->createElement('sphinx:document'); - $element->setAttribute( id => $attachment->id ); - $element->appendTextChild( content => $$text ); - - $doc->documentElement->appendChild( $element ); -} - -my $doc = undef; -sub sphinx_template { - return $doc if $doc; - - require XML::LibXML; - $doc = XML::LibXML::Document->new('1.0', 'UTF-8'); - my $root = $doc->createElement('sphinx:docset'); - $doc->setDocumentElement( $root ); - - my $schema = $doc->createElement('sphinx:schema'); - $root->appendChild( $schema ); - foreach ( qw(content) ) { - my $field = $doc->createElement('sphinx:field'); - $field->setAttribute( name => $_ ); - $schema->appendChild( $field ); - } - - return $doc; -} - -sub finalize_mysql { - my ($type, $attachments) = @_; - sphinx_template()->toFH(*STDOUT, 1); -} - -sub clean_mysql { - $doc = undef; -} - -} - -sub last_indexed_pg { - my $type = shift; - my $attachments = attachments( $type ); - my $alias = 'main'; - if ( $fts_config->{'Table'} && $fts_config->{'Table'} ne 'Attachments' ) { - $alias = $attachments->Join( - TYPE => 'left', - FIELD1 => 'id', - TABLE2 => $fts_config->{'Table'}, - FIELD2 => 'id', - ); - } - $attachments->Limit( - ALIAS => $alias, - FIELD => $fts_config->{'Column'}, - OPERATOR => 'IS NOT', - VALUE => 'NULL', - ); - $attachments->OrderBy( FIELD => 'id', ORDER => 'desc' ); - $attachments->RowsPerPage( 1 ); - my $res = $attachments->First; - return 0 unless $res; - return $res->id; -} - -sub process_pg { - my ($type, $attachment, $text) = (@_); - - my $dbh = $RT::Handle->dbh; - my $table = $fts_config->{'Table'}; - my $column = $fts_config->{'Column'}; - - my $query; - if ( $table ) { - if ( my ($id) = $dbh->selectrow_array("SELECT id FROM $table WHERE id = ?", undef, $attachment->id) ) { - $query = "UPDATE $table SET $column = to_tsvector(?) WHERE id = ?"; - } else { - $query = "INSERT INTO $table($column, id) VALUES(to_tsvector(?), ?)"; - } - } else { - $query = "UPDATE Attachments SET $column = to_tsvector(?) WHERE id = ?"; - } - - my $status = eval { $dbh->do( $query, undef, $$text, $attachment->id ) }; - unless ( $status ) { - if ( $dbh->err == 7 && $dbh->state eq '54000' ) { - warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains too many unique words"; - } elsif ( $dbh->err == 7 && $dbh->state eq '22021' ) { - warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains invalid UTF8 bytes"; - } else { - die "error: ". $dbh->errstr; - } - - # Insert an empty tsvector, so we count this row as "indexed" - # for purposes of knowing where to pick up - eval { $dbh->do( $query, undef, "", $attachment->id ) } - or die "Failed to insert empty tsvector: " . $dbh->errstr; - } -} - -sub attachments_text { - my $res = shift; - $res->Limit( FIELD => 'ContentType', VALUE => 'text/plain' ); - return $res; -} - -sub extract_text { - my $attachment = shift; - my $text = $attachment->Content; - return undef unless defined $text && length($text); - return \$text; -} - -sub attachments_html { - my $res = shift; - $res->Limit( FIELD => 'ContentType', VALUE => 'text/html' ); - return $res; -} - -sub filter_html { - my $attachment = shift; - if ( my $parent = $attachment->ParentObj ) { -# skip html parts that are alternatives - return 1 if $parent->id - && $parent->ContentType eq 'mulitpart/alternative'; - } - return 0; -} - -sub extract_html { - my $attachment = shift; - my $text = $attachment->Content; - return undef unless defined $text && length($text); -# TODO: html -> text - return \$text; -} - -sub goto_specific { - my %args = (@_); - - my $func = (caller(1))[3]; - $func =~ s/.*:://; - my $call = $func ."_". lc $args{'suffix'}; - unless ( defined &$call ) { - return undef unless $args{'error'}; - require Carp; Carp::croak( $args{'error'} ); - } - @_ = @{ $args{'arguments'} }; - goto &$call; -} - - -# helper functions -sub debug { print @_, "\n" if $OPT{debug}; 1 } -sub error { $RT::Logger->error(_(@_)); 1 } -sub warning { $RT::Logger->warn(_(@_)); 1 } - -=head1 NAME - -rt-fulltext-indexer - Indexer for full text search - -=head1 DESCRIPTION - -This is a helper script to keep full text indexes in sync with data. -Read F<docs/full_text_indexing.pod> for complete details on how and when -to run it. - -=head1 AUTHOR - -Ruslan Zakirov E<lt>ruz@bestpractical.comE<gt>, -Alex Vandiver E<lt>alexmv@bestpractical.comE<gt> - -=cut - diff --git a/rt/sbin/rt-fulltext-indexer.in b/rt/sbin/rt-fulltext-indexer.in index 9ad6d26bd..b84ca9499 100644 --- a/rt/sbin/rt-fulltext-indexer.in +++ b/rt/sbin/rt-fulltext-indexer.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -375,9 +375,9 @@ sub process_pg { my $status = eval { $dbh->do( $query, undef, $$text, $attachment->id ) }; unless ( $status ) { if ( $dbh->err == 7 && $dbh->state eq '54000' ) { - warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains too many unique words"; + warn "Attachment @{[$attachment->id]} cannot be indexed. Most probably it contains too many unique words. Error: ". $dbh->errstr; } elsif ( $dbh->err == 7 && $dbh->state eq '22021' ) { - warn "Attachment @{[$attachment->id]} cannot be indexed, as it contains invalid UTF8 bytes"; + warn "Attachment @{[$attachment->id]} cannot be indexed. Most probably it contains invalid UTF8 bytes. Error: ". $dbh->errstr; } else { die "error: ". $dbh->errstr; } diff --git a/rt/sbin/rt-message-catalog b/rt/sbin/rt-message-catalog index b31b58de7..b4283694f 100755 --- a/rt/sbin/rt-message-catalog +++ b/rt/sbin/rt-message-catalog @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-preferences-viewer.in b/rt/sbin/rt-preferences-viewer.in index d32588eca..8eeb7c97d 100644 --- a/rt/sbin/rt-preferences-viewer.in +++ b/rt/sbin/rt-preferences-viewer.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-server.fcgi.in b/rt/sbin/rt-server.fcgi.in index 632ba0ba6..0d11f0124 100644 --- a/rt/sbin/rt-server.fcgi.in +++ b/rt/sbin/rt-server.fcgi.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -127,7 +127,7 @@ EOF RT->InstallMode(1); } else { - RT->Init(); + RT->Init( Heavy => 1 ); my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post'); unless ( $status ) { diff --git a/rt/sbin/rt-server.in b/rt/sbin/rt-server.in index 632ba0ba6..0d11f0124 100644 --- a/rt/sbin/rt-server.in +++ b/rt/sbin/rt-server.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -127,7 +127,7 @@ EOF RT->InstallMode(1); } else { - RT->Init(); + RT->Init( Heavy => 1 ); my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post'); unless ( $status ) { diff --git a/rt/sbin/rt-session-viewer b/rt/sbin/rt-session-viewer deleted file mode 100755 index 1a6334d94..000000000 --- a/rt/sbin/rt-session-viewer +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/perl -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC -# <jesse@bestpractical.com> -# -# (Except where explicitly superseded by other copyright notices) -# -# -# LICENSE: -# -# This work is made available to you under the terms of Version 2 of -# the GNU General Public License. A copy of that license should have -# been provided with this software, but in any event can be snarfed -# from www.gnu.org. -# -# This work is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 or visit their web page on the internet at -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# -# CONTRIBUTION SUBMISSION POLICY: -# -# (The following paragraph is not intended to limit the rights granted -# to you to modify and distribute this software under the terms of -# the GNU General Public License and is only of importance to you if -# you choose to contribute your changes and enhancements to the -# community by submitting them to Best Practical Solutions, LLC.) -# -# By intentionally submitting any modifications, corrections or -# derivatives to this work, or any other work intended for use with -# Request Tracker, to Best Practical Solutions, LLC, you confirm that -# you are the copyright holder for those contributions and you grant -# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -# royalty-free, perpetual, license to use, copy, create derivative -# works based on those contributions, and sublicense and distribute -# those contributions and any derivatives thereof. -# -# END BPS TAGGED BLOCK }}} -use strict; -use warnings; - -# fix lib paths, some may be relative -BEGIN { - require File::Spec; - my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib"); - my $bin_path; - - for my $lib (@libs) { - unless ( File::Spec->file_name_is_absolute($lib) ) { - unless ($bin_path) { - if ( File::Spec->file_name_is_absolute(__FILE__) ) { - $bin_path = ( File::Spec->splitpath(__FILE__) )[1]; - } - else { - require FindBin; - no warnings "once"; - $bin_path = $FindBin::Bin; - } - } - $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib ); - } - unshift @INC, $lib; - } -} - -use Getopt::Long; -my %opt; -GetOptions( \%opt, 'help|h', ); - -my $session_id = shift; - -if ( $opt{help} || !$session_id ) { - require Pod::Usage; - Pod::Usage::pod2usage({ verbose => 2 }); - exit; -} - -require RT; -RT::LoadConfig(); -RT::Init(); - -require RT::Interface::Web::Session; -my %session; - -tie %session, 'RT::Interface::Web::Session', $session_id; -unless ( $session{'_session_id'} eq $session_id ) { - print STDERR "Couldn't load session $session_id\n"; - exit 1; -} - -use Data::Dumper; -print "Content of session $session_id: ". Dumper( \%session); - -__END__ - -=head1 NAME - -rt-session-viewer - show the content of a user's session - -=head1 SYNOPSIS - - # show the content of a session - rt-session-viewer 2c21c8a2909c14eff12975dd2cc7b9a3 - -=head1 DESCRIPTION - -This script deserializes and print content of a session identified -by <session id>. May be useful for developers and for troubleshooting -problems. - -=cut diff --git a/rt/sbin/rt-session-viewer.in b/rt/sbin/rt-session-viewer.in index cdf7a77bc..5ace1aee2 100644 --- a/rt/sbin/rt-session-viewer.in +++ b/rt/sbin/rt-session-viewer.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <jesse@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-setup-database.in b/rt/sbin/rt-setup-database.in index f1e35f5cc..edf3d4636 100644 --- a/rt/sbin/rt-setup-database.in +++ b/rt/sbin/rt-setup-database.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -83,13 +83,15 @@ $| = 1; # unbuffer all output. my %args = ( dba => '@DB_DBA@', + package => 'RT', ); GetOptions( \%args, 'action=s', 'force', 'debug', - 'dba=s', 'dba-password=s', 'prompt-for-dba-password', + 'dba=s', 'dba-password=s', 'prompt-for-dba-password', 'package=s', 'datafile=s', 'datadir=s', 'skip-create', 'root-password-file=s', + 'upgrade-from=s', 'upgrade-to=s', 'help|h', ); @@ -164,6 +166,7 @@ foreach my $key(qw(Type Host Name User Password)) { my $db_type = RT->Config->Get('DatabaseType') || ''; my $db_host = RT->Config->Get('DatabaseHost') || ''; +my $db_port = RT->Config->Get('DatabasePort') || ''; my $db_name = RT->Config->Get('DatabaseName') || ''; my $db_user = RT->Config->Get('DatabaseUser') || ''; my $db_pass = RT->Config->Get('DatabasePassword') || ''; @@ -191,8 +194,11 @@ if ($args{'skip-create'}) { } } +my $version_word_regex = join '|', RT::Handle->version_words; +my $version_dir = qr/^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/; + print "Working with:\n" - ."Type:\t$db_type\nHost:\t$db_host\nName:\t$db_name\n" + ."Type:\t$db_type\nHost:\t$db_host\nPort:\t$db_port\nName:\t$db_name\n" ."User:\t$db_user\nDBA:\t$dba_user" . ($args{'skip-create'} ? ' (No DBA)' : '') . "\n"; foreach my $action ( @actions ) { @@ -220,7 +226,7 @@ sub action_drop { unless ( $args{'force'} ) { print <<END; -About to drop $db_type database $db_name on $db_host. +About to drop $db_type database $db_name on $db_host (port '$db_port'). WARNING: This will erase all data in $db_name. END @@ -308,18 +314,17 @@ sub action_upgrade { return (0, "Couldn't read dir '$base_dir' with upgrade data") unless -d $base_dir || -r _; - my $version_word_regex = join '|', RT::Handle->version_words; my $upgrading_from = undef; do { if ( defined $upgrading_from ) { print "Doesn't match #.#.#: "; } else { - print "Enter RT version you're upgrading from: "; + print "Enter $args{package} version you're upgrading from: "; } - $upgrading_from = scalar <STDIN>; + $upgrading_from = $args{'upgrade-from'} || scalar <STDIN>; chomp $upgrading_from; $upgrading_from =~ s/\s+//g; - } while $upgrading_from !~ /^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/; + } while $upgrading_from !~ /$version_dir/; my $upgrading_to = $RT::VERSION; return (0, "The current version $upgrading_to is lower than $upgrading_from") @@ -347,14 +352,14 @@ sub action_upgrade { if ( defined $custom_upgrading_to ) { print "Doesn't match #.#.#: "; } else { - print "\nEnter RT version if you want to stop upgrade at some point,\n"; + print "\nEnter $args{package} version if you want to stop upgrade at some point,\n"; print " or leave it blank if you want apply above upgrades: "; } - $custom_upgrading_to = scalar <STDIN>; + $custom_upgrading_to = $args{'upgrade-to'} || scalar <STDIN>; chomp $custom_upgrading_to; $custom_upgrading_to =~ s/\s+//g; last unless $custom_upgrading_to; - } while $custom_upgrading_to !~ /^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/; + } while $custom_upgrading_to !~ /$version_dir/; if ( $custom_upgrading_to ) { return ( @@ -410,9 +415,12 @@ sub get_versions_from_to { my ($base_dir, $from, $to) = @_; opendir( my $dh, $base_dir ) or die "couldn't open dir: $!"; - my @versions = grep -d "$base_dir/$_" && /\d+\.\d+\.\d+/, readdir $dh; + my @versions = grep -d "$base_dir/$_" && /$version_dir/, readdir $dh; closedir $dh; + die "\nERROR: No upgrade data found in '$base_dir'! Perhaps you specified the wrong --datadir?\n" + unless @versions; + return grep defined $to ? RT::Handle::cmp_version($_, $to) <= 0 : 1, grep RT::Handle::cmp_version($_, $from) > 0, @@ -429,7 +437,7 @@ sub error { sub get_dba_password { print "In order to create or update your RT database," . " this script needs to connect to your " - . " $db_type instance on $db_host as $dba_user\n"; + . " $db_type instance on $db_host (port '$db_port') as $dba_user\n"; print "Please specify that user's database password below. If the user has no database\n"; print "password, just press return.\n\n"; print "Password: "; @@ -588,4 +596,14 @@ administrator privileges for 'init' and 'insert': rather than using the default administrative password for RT's "root" user, use the password in this file. +=item upgrade-from + +for 'upgrade': specifies the version to upgrade from, and do not prompt +for it if it appears to be a valid version. + +=item upgrade-to + +for 'upgrade': specifies the version to upgrade to, and do not prompt +for it if it appears to be a valid version. + =back diff --git a/rt/sbin/rt-setup-fulltext-index b/rt/sbin/rt-setup-fulltext-index deleted file mode 100755 index ef9b7d543..000000000 --- a/rt/sbin/rt-setup-fulltext-index +++ /dev/null @@ -1,714 +0,0 @@ -#!/usr/bin/perl -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC -# <sales@bestpractical.com> -# -# (Except where explicitly superseded by other copyright notices) -# -# -# LICENSE: -# -# This work is made available to you under the terms of Version 2 of -# the GNU General Public License. A copy of that license should have -# been provided with this software, but in any event can be snarfed -# from www.gnu.org. -# -# This work is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 or visit their web page on the internet at -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# -# CONTRIBUTION SUBMISSION POLICY: -# -# (The following paragraph is not intended to limit the rights granted -# to you to modify and distribute this software under the terms of -# the GNU General Public License and is only of importance to you if -# you choose to contribute your changes and enhancements to the -# community by submitting them to Best Practical Solutions, LLC.) -# -# By intentionally submitting any modifications, corrections or -# derivatives to this work, or any other work intended for use with -# Request Tracker, to Best Practical Solutions, LLC, you confirm that -# you are the copyright holder for those contributions and you grant -# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -# royalty-free, perpetual, license to use, copy, create derivative -# works based on those contributions, and sublicense and distribute -# those contributions and any derivatives thereof. -# -# END BPS TAGGED BLOCK }}} -use strict; -use warnings; -no warnings 'once'; - -# fix lib paths, some may be relative -BEGIN { - require File::Spec; - my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib"); - my $bin_path; - - for my $lib (@libs) { - unless ( File::Spec->file_name_is_absolute($lib) ) { - unless ($bin_path) { - if ( File::Spec->file_name_is_absolute(__FILE__) ) { - $bin_path = ( File::Spec->splitpath(__FILE__) )[1]; - } - else { - require FindBin; - no warnings "once"; - $bin_path = $FindBin::Bin; - } - } - $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib ); - } - unshift @INC, $lib; - } -} - -BEGIN { - use RT; - RT::LoadConfig(); - RT::Init(); -}; -use RT::Interface::CLI (); - -my %DB = ( - type => scalar RT->Config->Get('DatabaseType'), - user => scalar RT->Config->Get('DatabaseUser'), - admin => 'freeside', - admin_password => undef, -); - -my %OPT = ( - help => 0, - ask => 1, - dryrun => 0, - attachments => 1, -); - -my %DEFAULT; -if ( $DB{'type'} eq 'Pg' ) { - %DEFAULT = ( - table => 'Attachments', - column => 'ContentIndex', - ); -} -elsif ( $DB{'type'} eq 'mysql' ) { - %DEFAULT = ( - table => 'AttachmentsIndex', - ); -} -elsif ( $DB{'type'} eq 'Oracle' ) { - %DEFAULT = ( - prefix => 'rt_fts_', - ); -} - -use Getopt::Long qw(GetOptions); -GetOptions( - 'h|help!' => \$OPT{'help'}, - 'ask!' => \$OPT{'ask'}, - 'dry-run!' => \$OPT{'dryrun'}, - 'attachments!' => \$OPT{'attachments'}, - - 'table=s' => \$OPT{'table'}, - 'column=s' => \$OPT{'column'}, - 'url=s' => \$OPT{'url'}, - 'maxmatches=i' => \$OPT{'maxmatches'}, - 'index-type=s' => \$OPT{'index-type'}, - - 'dba=s' => \$DB{'admin'}, - 'dba-password=s' => \$DB{'admin_password'}, -) or show_help(); - -if ( $OPT{'help'} || (!$DB{'admin'} && $DB{'type'} eq 'Oracle' ) ) { - show_help( !$OPT{'help'} ); -} - -my $dbh = $RT::Handle->dbh; -$dbh->{'RaiseError'} = 1; -$dbh->{'PrintError'} = 1; - -if ( $DB{'type'} eq 'mysql' ) { - check_sphinx(); - my $table = $OPT{'table'} || prompt( - message => "Enter name of a new MySQL table that will be used to connect to the\n" - . "Sphinx server:", - default => $DEFAULT{'table'}, - silent => !$OPT{'ask'}, - ); - my $url = $OPT{'url'} || prompt( - message => "Enter URL of the sphinx search server; this should be of the form\n" - . "sphinx://<server>:<port>/<index name>", - default => 'sphinx://localhost:3312/rt', - silent => !$OPT{'ask'}, - ); - my $maxmatches = $OPT{'maxmatches'} || prompt( - message => "Maximum number of matches to return; this is the maximum number of\n" - . "attachment records returned by the search, not the maximum number\n" - . "of tickets. Both your RT_SiteConfig.pm and your sphinx.conf must\n" - . "agree on this value. Larger values cause your Sphinx server to\n" - . "consume more memory and CPU time per query.", - default => 10000, - silent => !$OPT{'ask'}, - ); - - my $schema = <<END; -CREATE TABLE $table ( - id INTEGER UNSIGNED NOT NULL, - weight INTEGER NOT NULL, - query VARCHAR(3072) NOT NULL, - INDEX(query) -) ENGINE=SPHINX CONNECTION="$url" CHARACTER SET utf8 -END - - do_error_is_ok( dba_handle() => "DROP TABLE $table" ) - unless $OPT{'dryrun'}; - insert_schema( $schema ); - - print_rt_config( Table => $table, MaxMatches => $maxmatches ); - - require URI; - my $urlo = URI->new( $url ); - my ($host, $port) = split /:/, $urlo->authority; - my $index = $urlo->path; - $index =~ s{^/+}{}; - - my $var_path = $RT::VarPath; - - my %sphinx_conf = (); - $sphinx_conf{'host'} = RT->Config->Get('DatabaseHost'); - $sphinx_conf{'db'} = RT->Config->Get('DatabaseName'); - $sphinx_conf{'user'} = RT->Config->Get('DatabaseUser'); - $sphinx_conf{'pass'} = RT->Config->Get('DatabasePassword'); - - print <<END - -Below is a simple Sphinx configuration which can be used to index all -text/plain attachments in your database. This configuration is not -ideal; you should read the Sphinx documentation to understand how to -configure it to better suit your needs. - -source rt { - type = mysql - - sql_host = $sphinx_conf{'host'} - sql_db = $sphinx_conf{'db'} - sql_user = $sphinx_conf{'user'} - sql_pass = $sphinx_conf{'pass'} - - sql_query_pre = SET NAMES utf8 - sql_query = \\ - SELECT a.id, a.content FROM Attachments a \\ - JOIN Transactions txn ON a.TransactionId = txn.id AND txn.ObjectType = 'RT::Ticket' \\ - JOIN Tickets t ON txn.ObjectId = t.id \\ - WHERE a.ContentType = 'text/plain' AND t.Status != 'deleted' - - sql_query_info = SELECT * FROM Attachments WHERE id=\$id -} - -index $index { - source = rt - path = $var_path/sphinx/index - docinfo = extern - charset_type = utf-8 -} - -indexer { - mem_limit = 32M -} - -searchd { - port = $port - log = $var_path/sphinx/searchd.log - query_log = $var_path/sphinx/query.log - read_timeout = 5 - max_children = 30 - pid_file = $var_path/sphinx/searchd.pid - max_matches = $maxmatches - seamless_rotate = 1 - preopen_indexes = 0 - unlink_old = 1 -} - -END - -} -elsif ( $DB{'type'} eq 'Pg' ) { - check_tsvalue(); - my $table = $OPT{'table'} || prompt( - message => "Enter the name of a DB table that will be used to store the Pg tsvector.\n" - . "You may either use the existing Attachments table, or create a new\n" - . "table.", - default => $DEFAULT{'table'}, - silent => !$OPT{'ask'}, - ); - my $column = $OPT{'column'} || prompt( - message => 'Enter the name of a column that will be used to store the Pg tsvector:', - default => $DEFAULT{'column'}, - silent => !$OPT{'ask'}, - ); - - my $schema; - my $drop; - if ( lc($table) eq 'attachments' ) { - $drop = "ALTER TABLE $table DROP COLUMN $column"; - $schema = "ALTER TABLE $table ADD COLUMN $column tsvector"; - } else { - $drop = "DROP TABLE $table"; - $schema = "CREATE TABLE $table ( " - ."id INTEGER NOT NULL," - ."$column tsvector )"; - } - - my $index_type = lc($OPT{'index-type'} || ''); - while ( $index_type ne 'gist' and $index_type ne 'gin' ) { - $index_type = lc prompt( - message => "You may choose between GiST or GIN indexes; the former is several times\n" - . "slower to search, but takes less space on disk and is faster to update.", - default => 'GiST', - silent => !$OPT{'ask'}, - ); - } - - do_error_is_ok( dba_handle() => $drop ) - unless $OPT{'dryrun'}; - insert_schema( $schema ); - insert_schema("CREATE INDEX ${column}_idx ON $table USING $index_type($column)"); - - print_rt_config( Table => $table, Column => $column ); -} -elsif ( $DB{'type'} eq 'Oracle' ) { - { - my $dbah = dba_handle(); - do_print_error( $dbah => 'GRANT CTXAPP TO '. $DB{'user'} ); - do_print_error( $dbah => 'GRANT EXECUTE ON CTXSYS.CTX_DDL TO '. $DB{'user'} ); - } - - my %PREFERENCES = ( - datastore => { - type => 'DIRECT_DATASTORE', - }, - filter => { - type => 'AUTO_FILTER', -# attributes => { -# timeout => 120, # seconds -# timeout_type => 'HEURISTIC', # or 'FIXED' -# }, - }, - lexer => { - type => 'WORLD_LEXER', - }, - word_list => { - type => 'BASIC_WORDLIST', - attributes => { - stemmer => 'AUTO', - fuzzy_match => 'AUTO', -# fuzzy_score => undef, -# fuzzy_numresults => undef, -# substring_index => undef, -# prefix_index => undef, -# prefix_length_min => undef, -# prefix_length_max => undef, -# wlidcard_maxterms => undef, - }, - }, - 'section_group' => { - type => 'NULL_SECTION_GROUP', - }, - - storage => { - type => 'BASIC_STORAGE', - attributes => { - R_TABLE_CLAUSE => 'lob (data) store as (cache)', - I_INDEX_CLAUSE => 'compress 2', - }, - }, - ); - - my @params = (); - push @params, ora_create_datastore( %{ $PREFERENCES{'datastore'} } ); - push @params, ora_create_filter( %{ $PREFERENCES{'filter'} } ); - push @params, ora_create_lexer( %{ $PREFERENCES{'lexer'} } ); - push @params, ora_create_word_list( %{ $PREFERENCES{'word_list'} } ); - push @params, ora_create_stop_list(); - push @params, ora_create_section_group( %{ $PREFERENCES{'section_group'} } ); - push @params, ora_create_storage( %{ $PREFERENCES{'storage'} } ); - - my $index_params = join "\n", @params; - my $index_name = $DEFAULT{prefix} .'index'; - do_error_is_ok( $dbh => "DROP INDEX $index_name" ) - unless $OPT{'dryrun'}; - $dbh->do( - "CREATE INDEX $index_name ON Attachments(Content) - indextype is ctxsys.context parameters(' - $index_params - ')", - ) unless $OPT{'dryrun'}; - - print_rt_config( IndexName => $index_name ); -} -else { - die "Full-text indexes on $DB{type} are not yet supported"; -} - -sub check_tsvalue { - my $dbh = $RT::Handle->dbh; - my $fts = ($dbh->selectrow_array(<<EOQ))[0]; -SELECT 1 FROM information_schema.routines WHERE routine_name = 'plainto_tsquery' -EOQ - unless ($fts) { - print STDERR <<EOT; - -Your PostgreSQL server does not include full-text support. You will -need to upgrade to PostgreSQL version 8.3 or higher to use full-text -indexing. - -EOT - exit 1; - } -} - -sub check_sphinx { - return if $RT::Handle->CheckSphinxSE; - - print STDERR <<EOT; - -Your MySQL server has not been compiled with the Sphinx storage engine -(sphinxse). You will need to recompile MySQL according to the -instructions in Sphinx's documentation at -http://sphinxsearch.com/docs/current.html#sphinxse-installing - -EOT - exit 1; -} - -sub ora_create_datastore { - return sprintf 'datastore %s', ora_create_preference( - @_, - name => 'datastore', - ); -} - -sub ora_create_filter { - my $res = ''; - $res .= sprintf "format column %s\n", ora_create_format_column(); - $res .= sprintf 'filter %s', ora_create_preference( - @_, - name => 'filter', - ); - return $res; -} - -sub ora_create_lexer { - return sprintf 'lexer %s', ora_create_preference( - @_, - name => 'lexer', - ); -} - -sub ora_create_word_list { - return sprintf 'wordlist %s', ora_create_preference( - @_, - name => 'word_list', - ); -} - -sub ora_create_stop_list { - my $file = shift || 'etc/stopwords/en.txt'; - return '' unless -e $file; - - my $name = $DEFAULT{'prefix'} .'stop_list'; - unless ($OPT{'dryrun'}) { - do_error_is_ok( $dbh => 'begin ctx_ddl.drop_stoplist(?); end;', $name ); - - $dbh->do( - 'begin ctx_ddl.create_stoplist(?, ?); end;', - undef, $name, 'BASIC_STOPLIST' - ); - - open( my $fh, '<:utf8', $file ) - or die "couldn't open file '$file': $!"; - while ( my $word = <$fh> ) { - chomp $word; - $dbh->do( - 'begin ctx_ddl.add_stopword(?, ?); end;', - undef, $name, $word - ); - } - close $fh; - } - return sprintf 'stoplist %s', $name; -} - -sub ora_create_section_group { - my %args = @_; - my $name = $DEFAULT{'prefix'} .'section_group'; - unless ($OPT{'dryrun'}) { - do_error_is_ok( $dbh => 'begin ctx_ddl.drop_section_group(?); end;', $name ); - $dbh->do( - 'begin ctx_ddl.create_section_group(?, ?); end;', - undef, $name, $args{'type'} - ); - } - return sprintf 'section group %s', $name; -} - -sub ora_create_storage { - return sprintf 'storage %s', ora_create_preference( - @_, - name => 'storage', - ); -} - -sub ora_create_format_column { - my $column_name = 'ContentOracleFormat'; - return $column_name if $OPT{'dryrun'}; - unless ( - $dbh->column_info( - undef, undef, uc('Attachments'), uc( $column_name ) - )->fetchrow_array - ) { - $dbh->do(qq{ - ALTER TABLE Attachments ADD $column_name VARCHAR2(10) - }); - } - - my $detect_format = qq{ - CREATE OR REPLACE FUNCTION $DEFAULT{prefix}detect_format_simple( - parent IN NUMBER, - type IN VARCHAR2, - encoding IN VARCHAR2, - fname IN VARCHAR2 - ) - RETURN VARCHAR2 - AS - format VARCHAR2(10); - BEGIN - format := CASE - }; - unless ( $OPT{'attachments'} ) { - $detect_format .= qq{ - WHEN fname IS NOT NULL THEN 'ignore' - }; - } - $detect_format .= qq{ - WHEN type = 'text' THEN 'text' - WHEN type = 'text/rtf' THEN 'ignore' - WHEN type LIKE 'text/%' THEN 'text' - WHEN type LIKE 'message/%' THEN 'text' - ELSE 'ignore' - END; - RETURN format; - END; - }; - ora_create_procedure( $detect_format ); - - $dbh->do(qq{ - UPDATE Attachments - SET $column_name = $DEFAULT{prefix}detect_format_simple( - Parent, - ContentType, ContentEncoding, - Filename - ) - WHERE $column_name IS NULL - }); - $dbh->do(qq{ - CREATE OR REPLACE TRIGGER $DEFAULT{prefix}set_format - BEFORE INSERT - ON Attachments - FOR EACH ROW - BEGIN - :new.$column_name := $DEFAULT{prefix}detect_format_simple( - :new.Parent, - :new.ContentType, :new.ContentEncoding, - :new.Filename - ); - END; - }); - return $column_name; -} - -sub ora_create_preference { - my %info = @_; - my $name = $DEFAULT{'prefix'} . $info{'name'}; - return $name if $OPT{'dryrun'}; - do_error_is_ok( $dbh => 'begin ctx_ddl.drop_preference(?); end;', $name ); - $dbh->do( - 'begin ctx_ddl.create_preference(?, ?); end;', - undef, $name, $info{'type'} - ); - return $name unless $info{'attributes'}; - - while ( my ($attr, $value) = each %{ $info{'attributes'} } ) { - $dbh->do( - 'begin ctx_ddl.set_attribute(?, ?, ?); end;', - undef, $name, $attr, $value - ); - } - - return $name; -} - -sub ora_create_procedure { - my $text = shift; - - return if $OPT{'dryrun'}; - my $status = $dbh->do($text, { RaiseError => 0 }); - - # Statement succeeded - return if $status; - - if ( 6550 != $dbh->err ) { - # Utter failure - die $dbh->errstr; - } - else { - my $msg = $dbh->func( 'plsql_errstr' ); - die $dbh->errstr if !defined $msg; - die $msg if $msg; - } -} - -sub dba_handle { - if ( $DB{'type'} eq 'Oracle' ) { - $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8"; - $ENV{'NLS_NCHAR'} = "AL32UTF8"; - } - my $dsn = do { my $h = new RT::Handle; $h->BuildDSN; $h->DSN }; - my $dbh = DBI->connect( - $dsn, $DB{admin}, $DB{admin_password}, - { RaiseError => 1, PrintError => 1 }, - ); - unless ( $dbh ) { - die "Failed to connect to $dsn as user '$DB{admin}': ". $DBI::errstr; - } - return $dbh; -} - -sub do_error_is_ok { - my $dbh = shift; - local $dbh->{'RaiseError'} = 0; - local $dbh->{'PrintError'} = 0; - return $dbh->do(shift, undef, @_); -} - -sub do_print_error { - my $dbh = shift; - local $dbh->{'RaiseError'} = 0; - local $dbh->{'PrintError'} = 1; - return $dbh->do(shift, undef, @_); -} - -sub prompt { - my %args = ( @_ ); - return $args{'default'} if $args{'silent'}; - - local $| = 1; - print $args{'message'}; - if ( $args{'default'} ) { - print "\n[". $args{'default'} .']: '; - } else { - print ":\n"; - } - - my $res = <STDIN>; - chomp $res; - print "\n"; - return $args{'default'} if !$res && $args{'default'}; - return $res; -} - -sub verbose { print @_, "\n" if $OPT{verbose} || $OPT{verbose}; 1 } -sub debug { print @_, "\n" if $OPT{debug}; 1 } -sub error { $RT::Logger->error( @_ ); verbose(@_); 1 } -sub warning { $RT::Logger->warning( @_ ); verbose(@_); 1 } - -sub show_help { - my $error = shift; - RT::Interface::CLI->ShowHelp( - ExitValue => $error, - Sections => 'NAME|DESCRIPTION', - ); -} - -sub print_rt_config { - my %args = @_; - my $config = <<END; - -You can now configure RT to use the newly-created full-text index by -adding the following to your RT_SiteConfig.pm: - -Set( %FullTextSearch, - Enable => 1, - Indexed => 1, -END - - $config .= sprintf(" %-10s => '$args{$_}',\n",$_) - foreach grep defined $args{$_}, keys %args; - $config .= ");\n"; - - print $config; -} - -sub insert_schema { - my $dbh = dba_handle(); - my $message = "Going to run the following in the DB:"; - my $schema = shift; - print "$message\n"; - my $disp = $schema; - $disp =~ s/^/ /mg; - print "$disp\n\n"; - return if $OPT{'dryrun'}; - - my $res = $dbh->do( $schema ); - unless ( $res ) { - die "Couldn't run DDL query: ". $dbh->errstr; - } -} - -=head1 NAME - -rt-setup-fulltext-index - Create indexes for full text search - -=head1 DESCRIPTION - -This script creates the appropriate tables, columns, functions, and / or -views necessary for full-text searching for your database type. It will -drop any existing indexes in the process. - -Please read F<docs/full_text_indexing.pod> for complete documentation on -full-text indexing for your database type. - -If you have a non-standard database administrator user or password, you -may use the C<--dba> and C<--dba-password> parameters to set them -explicitly: - - rt-setup-fulltext-index --dba sysdba --dba-password 'secret' - -To test what will happen without running any DDL, pass the C<--dryrun> -flag. - -The Oracle index determines which content-types it will index at -creation time. By default, textual message bodies and textual uploaded -attachments (attachments with filenames) are indexed; to ignore textual -attachments, pass the C<--no-attachments> flag when the index is -created. - - -=head1 AUTHOR - -Ruslan Zakirov E<lt>ruz@bestpractical.comE<gt>, -Alex Vandiver E<lt>alexmv@bestpractical.comE<gt> - -=cut - diff --git a/rt/sbin/rt-setup-fulltext-index.in b/rt/sbin/rt-setup-fulltext-index.in index eab720352..ade728f8f 100644 --- a/rt/sbin/rt-setup-fulltext-index.in +++ b/rt/sbin/rt-setup-fulltext-index.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-shredder.in b/rt/sbin/rt-shredder.in index e7910e523..a903728ce 100755 --- a/rt/sbin/rt-shredder.in +++ b/rt/sbin/rt-shredder.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -135,7 +135,10 @@ BEGIN { } -use RT::Shredder (); +use RT -init; + +require RT::Shredder; + use Getopt::Long qw(GetOptions); use File::Spec (); @@ -146,7 +149,6 @@ our %plugins = RT::Shredder::Plugin->List; our %opt; parse_args(); -RT::Shredder::Init( %opt ); my $shredder = RT::Shredder->new; { diff --git a/rt/sbin/rt-test-dependencies.in b/rt/sbin/rt-test-dependencies.in index df1ef135f..868105431 100644 --- a/rt/sbin/rt-test-dependencies.in +++ b/rt/sbin/rt-test-dependencies.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -295,6 +295,7 @@ Test::WWW::Mechanize::PSGI Plack::Middleware::Test::StashWarnings 0.06 Test::LongString Test::NoWarnings +Locale::PO . $deps{'FASTCGI'} = [ text_to_hash( << '.') ]; diff --git a/rt/sbin/rt-validate-aliases.in b/rt/sbin/rt-validate-aliases.in index 5dbfaa459..97252ae19 100644 --- a/rt/sbin/rt-validate-aliases.in +++ b/rt/sbin/rt-validate-aliases.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/rt-validator.in b/rt/sbin/rt-validator.in index d90280169..128e60af0 100644 --- a/rt/sbin/rt-validator.in +++ b/rt/sbin/rt-validator.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) diff --git a/rt/sbin/standalone_httpd b/rt/sbin/standalone_httpd deleted file mode 100755 index 5bc8d0c8b..000000000 --- a/rt/sbin/standalone_httpd +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/perl -w -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC -# <sales@bestpractical.com> -# -# (Except where explicitly superseded by other copyright notices) -# -# -# LICENSE: -# -# This work is made available to you under the terms of Version 2 of -# the GNU General Public License. A copy of that license should have -# been provided with this software, but in any event can be snarfed -# from www.gnu.org. -# -# This work is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 or visit their web page on the internet at -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# -# CONTRIBUTION SUBMISSION POLICY: -# -# (The following paragraph is not intended to limit the rights granted -# to you to modify and distribute this software under the terms of -# the GNU General Public License and is only of importance to you if -# you choose to contribute your changes and enhancements to the -# community by submitting them to Best Practical Solutions, LLC.) -# -# By intentionally submitting any modifications, corrections or -# derivatives to this work, or any other work intended for use with -# Request Tracker, to Best Practical Solutions, LLC, you confirm that -# you are the copyright holder for those contributions and you grant -# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -# royalty-free, perpetual, license to use, copy, create derivative -# works based on those contributions, and sublicense and distribute -# those contributions and any derivatives thereof. -# -# END BPS TAGGED BLOCK }}} -use warnings; -use strict; - -# fix lib paths, some may be relative -BEGIN { - die <<EOT if ${^TAINT}; -RT does not run under Perl's "taint mode". Remove -T from the command -line, or remove the PerlTaintCheck parameter from your mod_perl -configuration. -EOT - - require File::Spec; - my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib"); - my $bin_path; - - for my $lib (@libs) { - unless ( File::Spec->file_name_is_absolute($lib) ) { - unless ($bin_path) { - if ( File::Spec->file_name_is_absolute(__FILE__) ) { - $bin_path = ( File::Spec->splitpath(__FILE__) )[1]; - } - else { - require FindBin; - no warnings "once"; - $bin_path = $FindBin::Bin; - } - } - $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib ); - } - unshift @INC, $lib; - } - -} - -use Getopt::Long; -no warnings 'once'; - -if (grep { m/help/ } @ARGV) { - require Pod::Usage; - print Pod::Usage::pod2usage( { verbose => 2 } ); - exit; -} - -require RT; -RT->LoadConfig(); -RT->InitPluginPaths(); -RT->InitLogging(); -require Module::Refresh if RT->Config->Get('DevelMode'); - -require RT::Handle; -my ($integrity, $state, $msg) = RT::Handle->CheckIntegrity; - -unless ( $integrity ) { - print STDERR <<EOF; - -RT couldn't connect to the database where tickets are stored. -If this is a new installation of RT, you should visit the URL below -to configure RT and initialize your database. - -If this is an existing RT installation, this may indicate a database -connectivity problem. - -The error RT got back when trying to connect to your database was: - -$msg - -EOF - - require RT::Installer; - # don't enter install mode if the file exists but is unwritable - if (-e RT::Installer->ConfigFile && !-w _) { - die 'Since your configuration exists (' - . RT::Installer->ConfigFile - . ") but is not writable, I'm refusing to do anything.\n"; - } - - RT->Config->Set( 'LexiconLanguages' => '*' ); - RT::I18N->Init; - - RT->InstallMode(1); -} else { - RT->Init(); - - my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post'); - unless ( $status ) { - print STDERR $msg, "\n\n"; - exit -1; - } -} - -# we must disconnect DB before fork -if ($RT::Handle) { - $RT::Handle->dbh(undef); - undef $RT::Handle; -} - -require RT::Interface::Web::Handler; -my $app = RT::Interface::Web::Handler->PSGIApp; - -if ($ENV{RT_TESTING}) { - my $screen_logger = $RT::Logger->remove('screen'); - require Log::Dispatch::Perl; - $RT::Logger->add( - Log::Dispatch::Perl->new( - name => 'rttest', - min_level => $screen_logger->min_level, - action => { - error => 'warn', - critical => 'warn' - } - ) - ); - require Plack::Middleware::Test::StashWarnings; - $app = Plack::Middleware::Test::StashWarnings->wrap($app); -} - -# when used as a psgi file -if (caller) { - return $app; -} - - -# load appropriate server - -require Plack::Runner; - -my $is_fastcgi = $0 =~ m/fcgi$/; -my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) : - $is_fastcgi ? ( server => 'FCGI' ) - : (), - env => 'deployment' ); - -# figure out the port -my $port; - -# handle "rt-server 8888" for back-compat, but complain about it -if ($ARGV[0] && $ARGV[0] =~ m/^\d+$/) { - warn "Deprecated: please run $0 --port $ARGV[0] instead\n"; - unshift @ARGV, '--port'; -} - -my @args = @ARGV; - -use List::MoreUtils 'last_index'; -my $last_index = last_index { $_ eq '--port' } @args; - -my $explicit_port; - -if ( $last_index != -1 && $args[$last_index+1] =~ /^\d+$/ ) { - $explicit_port = $args[$last_index+1]; - $port = $explicit_port; - - # inform the rest of the system what port we manually chose - my $old_app = $app; - $app = sub { - my $env = shift; - - $env->{'rt.explicit_port'} = $port; - - $old_app->($env, @_); - }; -} -else { - # default to the configured WebPort and inform Plack::Runner - $port = RT->Config->Get('WebPort') || '8080'; - push @args, '--port', $port; -} - -push @args, '--server', 'Standalone' if RT->InstallMode; -push @args, '--server', 'Starlet' unless $r->{server} || grep { m/--server/ } @args; - -$r->parse_options(@args); - -delete $r->{options} if $is_fastcgi; ### mangle_host_port_socket ruins everything - -unless ($r->{env} eq 'development') { - push @{$r->{options}}, server_ready => sub { - my($args) = @_; - my $name = $args->{server_software} || ref($args); # $args is $server - my $host = $args->{host} || 0; - my $proto = $args->{proto} || 'http'; - print STDERR "$name: Accepting connections at $proto://$host:$args->{port}/\n"; - }; -} -eval { $r->run($app) }; -if (my $err = $@) { - handle_startup_error($err); -} - -exit 0; - -sub handle_startup_error { - my $err = shift; - if ( $err =~ /listen/ ) { - handle_bind_error(); - } else { - die - "Something went wrong while trying to run RT's standalone web server:\n\t" - . $err; - } -} - - -sub handle_bind_error { - - print STDERR <<EOF; -WARNING: RT couldn't start up a web server on port @{[$port]}. -This is often the case if the port is already in use or you're running @{[$0]} -as someone other than your system's "root" user. You may also specify a -temporary port with: $0 --port <port> -EOF - - if ($explicit_port) { - print STDERR - "Please check your system configuration or choose another port\n\n"; - } -} - -__END__ - -=head1 NAME - -rt-server - RT standalone server - -=head1 SYNOPSIS - - # runs prefork server listening on port 8080, requires Starlet - rt-server --port 8080 - - # runs server listening on port 8080 - rt-server --server Standalone --port 8080 - # or - standalone_httpd --port 8080 - - # runs other PSGI server on port 8080 - rt-server --server Starman --port 8080 diff --git a/rt/sbin/standalone_httpd.in b/rt/sbin/standalone_httpd.in index 632ba0ba6..0d11f0124 100644 --- a/rt/sbin/standalone_httpd.in +++ b/rt/sbin/standalone_httpd.in @@ -3,7 +3,7 @@ # # COPYRIGHT: # -# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC # <sales@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -127,7 +127,7 @@ EOF RT->InstallMode(1); } else { - RT->Init(); + RT->Init( Heavy => 1 ); my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'post'); unless ( $status ) { |