summaryrefslogtreecommitdiff
path: root/rt/lib
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib')
-rw-r--r--rt/lib/RT.pm619
-rwxr-xr-xrt/lib/RT/ACE.pm99
-rwxr-xr-xrt/lib/RT/ACL.pm50
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm107
-rwxr-xr-xrt/lib/RT/Action/Generic.pm201
-rwxr-xr-xrt/lib/RT/Action/Notify.pm125
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm46
-rw-r--r--rt/lib/RT/Action/ResolveMembers.pm45
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm1222
-rwxr-xr-xrt/lib/RT/Attachment.pm113
-rwxr-xr-xrt/lib/RT/Attachments.pm45
-rw-r--r--rt/lib/RT/Condition/AnyTransaction.pm45
-rwxr-xr-xrt/lib/RT/Condition/Generic.pm217
-rw-r--r--rt/lib/RT/Condition/StatusChange.pm47
-rwxr-xr-xrt/lib/RT/CurrentUser.pm425
-rw-r--r--rt/lib/RT/Date.pm1069
-rw-r--r--rt/lib/RT/Extension/ActivityReports.pm3
-rwxr-xr-xrt/lib/RT/Group.pm87
-rwxr-xr-xrt/lib/RT/GroupMember.pm63
-rwxr-xr-xrt/lib/RT/GroupMembers.pm45
-rwxr-xr-xrt/lib/RT/Groups.pm47
-rw-r--r--rt/lib/RT/Groups_Overlay.pm166
-rw-r--r--rt/lib/RT/Handle.pm1047
-rw-r--r--rt/lib/RT/I18N/en_malkovich.po3973
-rw-r--r--rt/lib/RT/Interface/CLI.pm60
-rwxr-xr-xrt/lib/RT/Interface/Email.pm1924
-rw-r--r--rt/lib/RT/Interface/Web.pm2250
-rw-r--r--rt/lib/RT/Interface/Web_Vendor.pm201
-rw-r--r--rt/lib/RT/Link.pm97
-rw-r--r--rt/lib/RT/Links.pm45
-rwxr-xr-xrt/lib/RT/Queue.pm115
-rwxr-xr-xrt/lib/RT/Queues.pm45
-rwxr-xr-xrt/lib/RT/Record.pm576
-rwxr-xr-xrt/lib/RT/Scrip.pm141
-rwxr-xr-xrt/lib/RT/ScripAction.pm91
-rwxr-xr-xrt/lib/RT/ScripActions.pm45
-rwxr-xr-xrt/lib/RT/ScripCondition.pm97
-rwxr-xr-xrt/lib/RT/ScripConditions.pm45
-rwxr-xr-xrt/lib/RT/Scrips.pm45
-rw-r--r--rt/lib/RT/SearchBuilder.pm96
-rwxr-xr-xrt/lib/RT/Template.pm111
-rwxr-xr-xrt/lib/RT/Templates.pm45
-rwxr-xr-xrt/lib/RT/Ticket.pm201
-rw-r--r--rt/lib/RT/TicketCustomFieldValue.pm308
-rw-r--r--rt/lib/RT/TicketCustomFieldValue_Overlay.pm74
-rw-r--r--rt/lib/RT/TicketCustomFieldValues.pm137
-rw-r--r--rt/lib/RT/TicketCustomFieldValues_Overlay.pm108
-rw-r--r--rt/lib/RT/Ticket_Overlay.pm1503
-rwxr-xr-xrt/lib/RT/Tickets.pm45
-rwxr-xr-xrt/lib/RT/Transaction.pm229
-rw-r--r--rt/lib/RT/Transaction_Overlay.pm487
-rwxr-xr-xrt/lib/RT/Transactions.pm45
-rw-r--r--rt/lib/RT/URI/freeside.pm285
-rw-r--r--rt/lib/RT/URI/freeside/Internal.pm145
-rw-r--r--rt/lib/RT/URI/freeside/XMLRPC.pm122
-rwxr-xr-xrt/lib/RT/User.pm241
-rw-r--r--rt/lib/RT/User_Overlay.pm1311
-rwxr-xr-xrt/lib/RT/Users.pm45
-rw-r--r--rt/lib/RT/Users_Overlay.pm85
-rwxr-xr-xrt/lib/RTx/Statistics.pm239
-rw-r--r--rt/lib/RTx/WebCronTool.pm41
-rw-r--r--rt/lib/t/00smoke.t.in14
-rw-r--r--rt/lib/t/01harness.t.in12
-rw-r--r--rt/lib/t/02regression.t7
-rw-r--r--rt/lib/t/02regression.t.in47
-rw-r--r--rt/lib/t/03web.pl78
-rw-r--r--rt/lib/t/03web.pl.in170
-rw-r--r--rt/lib/t/04_send_email.pl25
-rw-r--r--rt/lib/t/04_send_email.pl.in506
-rw-r--r--rt/lib/t/05cronsupport.pl.in84
-rw-r--r--rt/lib/t/regression/00placeholder1
71 files changed, 7345 insertions, 15185 deletions
diff --git a/rt/lib/RT.pm b/rt/lib/RT.pm
index 3ed85afc0..033e5e607 100644
--- a/rt/lib/RT.pm
+++ b/rt/lib/RT.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,79 +45,61 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
-use strict;
-use warnings;
-
package RT;
-
-
-use File::Spec ();
-use Cwd ();
-
-use vars qw($Config $System $SystemUser $Nobody $Handle $Logger $_INSTALL_MODE);
-
-our $VERSION = '3.8.7';
-
-
-
-our $BasePath = '/opt/rt3';
-our $EtcPath = 'etc';
-our $BinPath = 'bin';
-our $SbinPath = 'sbin';
-our $VarPath = 'var';
-our $PluginPath = 'plugins';
-our $LocalPath = 'local';
-our $LocalEtcPath = 'local/etc';
-our $LocalLibPath = 'local/lib';
-our $LocalLexiconPath = 'local/po';
-our $LocalPluginPath = $LocalPath."/plugins";
-
+use strict;
+use RT::I18N;
+use RT::CurrentUser;
+use RT::System;
+
+use vars qw($VERSION $System $SystemUser $Nobody $Handle $Logger
+ $CORE_CONFIG_FILE
+ $SITE_CONFIG_FILE
+ $BasePath
+ $EtcPath
+ $VarPath
+ $LocalPath
+ $LocalEtcPath
+ $LocalLexiconPath
+ $LogDir
+ $BinPath
+ $MasonComponentRoot
+ $MasonLocalComponentRoot
+ $MasonDataDir
+ $MasonSessionDir
+);
+
+$VERSION = '3.6.10';
+$CORE_CONFIG_FILE = "/opt/rt3/etc/RT_Config.pm";
+$SITE_CONFIG_FILE = "/opt/rt3/etc/RT_SiteConfig.pm";
+
+
+
+$BasePath = '/opt/rt3';
+
+$EtcPath = '/opt/rt3/etc';
+$BinPath = '/opt/rt3/bin';
+$VarPath = '/opt/rt3/var';
+$LocalPath = '/opt/rt3/local';
+$LocalEtcPath = '/opt/rt3/local/etc';
+$LocalLexiconPath = '/opt/rt3/local/po';
# $MasonComponentRoot is where your rt instance keeps its mason html files
-our $MasonComponentRoot = 'share/html';
+$MasonComponentRoot = '/var/www/freeside/rt';
# $MasonLocalComponentRoot is where your rt instance keeps its site-local
# mason html files.
-our $MasonLocalComponentRoot = 'local/html';
+$MasonLocalComponentRoot = '/opt/rt3/local/html';
# $MasonDataDir Where mason keeps its datafiles
-our $MasonDataDir = 'var/mason_data';
+$MasonDataDir = '/usr/local/etc/freeside/masondata';
# RT needs to put session data (for preserving state between connections
# via the web interface)
-our $MasonSessionDir = 'var/session_data';
-
-unless ( File::Spec->file_name_is_absolute($EtcPath) ) {
-
-# if BasePath exists and is absolute, we won't infer it from $INC{'RT.pm'}.
-# otherwise RT.pm will make src dir(where we configure RT) be the BasePath
-# instead of the --prefix one
- unless ( -d $BasePath && File::Spec->file_name_is_absolute($BasePath) ) {
- my $pm_path = ( File::Spec->splitpath( $INC{'RT.pm'} ) )[1];
+$MasonSessionDir = '/opt/rt3/var/session_data';
- # need rel2abs here is to make sure path is absolute, since $INC{'RT.pm'}
- # is not always absolute
- $BasePath =
- File::Spec->rel2abs(
- File::Spec->catdir( $pm_path, File::Spec->updir ) );
- }
-
- $BasePath = Cwd::realpath( $BasePath );
-
- for my $path ( qw/EtcPath BinPath SbinPath VarPath LocalPath LocalEtcPath
- LocalLibPath LocalLexiconPath PluginPath LocalPluginPath
- MasonComponentRoot MasonLocalComponentRoot MasonDataDir
- MasonSessionDir/ ) {
- no strict 'refs';
- # just change relative ones
- $$path = File::Spec->catfile( $BasePath, $$path )
- unless File::Spec->file_name_is_absolute( $$path );
- }
-}
=head1 NAME
@@ -130,14 +112,12 @@ A fully featured request tracker package
=head1 DESCRIPTION
-=head2 INITIALIZATION
-
=head2 LoadConfig
Load RT's config file. First, the site configuration file
-(F<RT_SiteConfig.pm>) is loaded, in order to establish overall site
+(C<RT_SiteConfig.pm>) is loaded, in order to establish overall site
settings like hostname and name of RT instance. Then, the core
-configuration file (F<RT_Config.pm>) is loaded to set fallback values
+configuration file (C<RT_Config.pm>) is loaded to set fallback values
for all settings; it bases some values on settings from the site
configuration file.
@@ -148,33 +128,59 @@ have not been set already.
=cut
sub LoadConfig {
- require RT::Config;
- $Config = new RT::Config;
- $Config->LoadConfigs;
- require RT::I18N;
+ local *Set = sub { $_[0] = $_[1] unless defined $_[0] };
+
+ my $username = getpwuid($>);
+ my $group = getgrgid($();
+ my $message = <<EOF;
+
+RT couldn't load RT config file %s as:
+ user: $username
+ group: $group
+
+The file is owned by user %s and group %s.
+
+This usually means that the user/group your webserver is running
+as cannot read the file. Be careful not to make the permissions
+on this file too liberal, because it contains database passwords.
+You may need to put the webserver user in the appropriate group
+(%s) or change permissions be able to run succesfully.
+EOF
+
+
+ if ( -f "$SITE_CONFIG_FILE" ) {
+ eval { require $SITE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $SITE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@");
+ }
+ }
+ eval { require $CORE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($CORE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $CORE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@")
+ }
# RT::Essentials mistakenly recommends that WebPath be set to '/'.
# If the user does that, do what they mean.
$RT::WebPath = '' if ($RT::WebPath eq '/');
- # fix relative LogDir and GnuPG homedir
- unless ( File::Spec->file_name_is_absolute( $Config->Get('LogDir') ) ) {
- $Config->Set( LogDir =>
- File::Spec->catfile( $BasePath, $Config->Get('LogDir') ) );
- }
+ $ENV{'TZ'} = $RT::Timezone if ($RT::Timezone);
- my $gpgopts = $Config->Get('GnuPGOptions');
- unless ( File::Spec->file_name_is_absolute( $gpgopts->{homedir} ) ) {
- $gpgopts->{homedir} = File::Spec->catfile( $BasePath, $gpgopts->{homedir} );
- }
-
RT::I18N->Init;
}
=head2 Init
-L<Connect to the database /ConnectToDatabase>, L<initilizes system objects /InitSystemObjects>,
-L<preloads classes /InitClasses> and L<set up logging /InitLogging>.
+Conenct to the database, set up logging.
=cut
@@ -182,34 +188,41 @@ sub Init {
CheckPerlRequirements();
- InitPluginPaths();
-
#Get a database connection
ConnectToDatabase();
- InitSystemObjects();
+
+ #RT's system user is a genuine database user. its id lives here
+ $SystemUser = new RT::CurrentUser();
+ $SystemUser->LoadByName('RT_System');
+
+ #RT's "nobody user" is a genuine database user. its ID lives here.
+ $Nobody = new RT::CurrentUser();
+ $Nobody->LoadByName('Nobody');
+
+ $System = RT::System->new();
+
InitClasses();
InitLogging();
- InitPlugins();
- RT->Config->PostLoadCheck;
-
}
+
=head2 ConnectToDatabase
-Get a database connection. See also </Handle>.
+Get a database connection
=cut
sub ConnectToDatabase {
require RT::Handle;
- $Handle = new RT::Handle unless $Handle;
- $Handle->Connect;
- return $Handle;
+ unless ($Handle && $Handle->dbh && $Handle->dbh->ping) {
+ $Handle = RT::Handle->new();
+ }
+ $Handle->Connect();
}
=head2 InitLogging
-Create the Logger object and set up signal handlers.
+Create the RT::Logger object.
=cut
@@ -218,141 +231,112 @@ sub InitLogging {
# We have to set the record separator ($, man perlvar)
# or Log::Dispatch starts getting
# really pissy, as some other module we use unsets it.
+
$, = '';
use Log::Dispatch 1.6;
- my %level_to_num = (
- map( { $_ => } 0..7 ),
- debug => 0,
- info => 1,
- notice => 2,
- warning => 3,
- error => 4, 'err' => 4,
- critical => 5, crit => 5,
- alert => 6,
- emergency => 7, emerg => 7,
- );
+ unless ($RT::Logger) {
- unless ( $RT::Logger ) {
+ $RT::Logger = Log::Dispatch->new();
- $RT::Logger = Log::Dispatch->new;
+ my $simple_cb = sub {
+ # if this code throw any warning we can get segfault
+ no warnings;
- my $stack_from_level;
- if ( $stack_from_level = RT->Config->Get('LogStackTraces') ) {
- # if option has old style '\d'(true) value
- $stack_from_level = 0 if $stack_from_level =~ /^\d+$/;
- $stack_from_level = $level_to_num{ $stack_from_level } || 0;
- } else {
- $stack_from_level = 99; # don't log
- }
+ my %p = @_;
- my $simple_cb = sub {
- # if this code throw any warning we can get segfault
- no warnings;
- my %p = @_;
-
- # skip Log::* stack frames
- my $frame = 0;
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- my ($package, $filename, $line) = caller($frame);
-
- $p{'message'} =~ s/(?:\r*\n)+$//;
- return "[". gmtime(time) ."] [". $p{'level'} ."]: "
- . $p{'message'} ." ($filename:$line)\n";
- };
-
- my $syslog_cb = sub {
- # if this code throw any warning we can get segfault
- no warnings;
- my %p = @_;
-
- my $frame = 0; # stack frame index
- # skip Log::* stack frames
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- my ($package, $filename, $line) = caller($frame);
-
- # syswrite() cannot take utf8; turn it off here.
- Encode::_utf8_off($p{message});
-
- $p{message} =~ s/(?:\r*\n)+$//;
- if ($p{level} eq 'debug') {
- return "$p{message}\n";
- } else {
- return "$p{message} ($filename:$line)\n";
- }
- };
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
- my $stack_cb = sub {
- no warnings;
- my %p = @_;
- return $p{'message'} unless $level_to_num{ $p{'level'} } >= $stack_from_level;
-
- require Devel::StackTrace;
- my $trace = Devel::StackTrace->new( ignore_class => [ 'Log::Dispatch', 'Log::Dispatch::Base' ] );
- return $p{'message'} . $trace->as_string;
+ my ($package, $filename, $line) = caller($frame);
+ $p{message} =~ s/(?:\r*\n)+$//;
+ my $str = "[".gmtime(time)."] [".$p{level}."]: $p{message} ($filename:$line)\n";
+ if( $RT::LogStackTraces ) {
+ $str .= "\nStack trace:\n";
# skip calling of the Log::* subroutins
- my $frame = 0;
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- $frame++ while caller($frame) && (caller($frame))[3] =~ /^Log::/;
-
- $p{'message'} .= "\nStack trace:\n";
+ $frame++ while( caller($frame) && (caller($frame))[3] =~ /^Log::/ );
while( my ($package, $filename, $line, $sub) = caller($frame++) ) {
- $p{'message'} .= "\t$sub(...) called at $filename:$line\n";
- }
- return $p{'message'};
- };
-
- if ( $Config->Get('LogToFile') ) {
- my ($filename, $logdir) = (
- $Config->Get('LogToFileNamed') || 'rt.log',
- $Config->Get('LogDir') || File::Spec->catdir( $VarPath, 'log' ),
- );
- if ( $filename =~ m![/\\]! ) { # looks like an absolute path.
- ($logdir) = $filename =~ m{^(.*[/\\])};
- }
- else {
- $filename = File::Spec->catfile( $logdir, $filename );
+ $str .= "\t". $sub ."() called at $filename:$line\n";
}
+ }
+ return $str;
+ };
- unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
- # localizing here would be hard when we don't have a current user yet
- die "Log file '$filename' couldn't be written or created.\n RT can't run.";
- }
+ my $syslog_cb = sub {
+ my %p = @_;
+
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
+ my ($package, $filename, $line) = caller($frame);
+
+ # syswrite() cannot take utf8; turn it off here.
+ Encode::_utf8_off($p{message});
- require Log::Dispatch::File;
- $RT::Logger->add( Log::Dispatch::File->new
- ( name=>'file',
- min_level=> $Config->Get('LogToFile'),
- filename=> $filename,
- mode=>'append',
- callbacks => [ $simple_cb, $stack_cb ],
- ));
+ $p{message} =~ s/(?:\r*\n)+$//;
+ if ($p{level} eq 'debug') {
+ return "$p{message}\n"
+ } else {
+ return "$p{message} ($filename:$line)\n"
+ }
+ };
+
+ if ($RT::LogToFile) {
+ my ($filename, $logdir);
+ if ($RT::LogToFileNamed =~ m![/\\]!) {
+ # looks like an absolute path.
+ $filename = $RT::LogToFileNamed;
+ ($logdir) = $RT::LogToFileNamed =~ m!^(.*[/\\])!;
}
- if ( $Config->Get('LogToScreen') ) {
- require Log::Dispatch::Screen;
- $RT::Logger->add( Log::Dispatch::Screen->new
- ( name => 'screen',
- min_level => $Config->Get('LogToScreen'),
- callbacks => [ $simple_cb, $stack_cb ],
- stderr => 1,
- ));
+ else {
+ $filename = "$RT::LogDir/$RT::LogToFileNamed";
+ $logdir = $RT::LogDir;
}
- if ( $Config->Get('LogToSyslog') ) {
- require Log::Dispatch::Syslog;
- $RT::Logger->add(Log::Dispatch::Syslog->new
- ( name => 'syslog',
- ident => 'RT',
- min_level => $Config->Get('LogToSyslog'),
- callbacks => [ $syslog_cb, $stack_cb ],
- stderr => 1,
- $Config->Get('LogToSyslogConf'),
- ));
+
+ unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
+ # localizing here would be hard when we don't have a current user yet
+ die "Log file $filename couldn't be written or created.\n RT can't run.";
}
+
+ package Log::Dispatch::File;
+ require Log::Dispatch::File;
+ $RT::Logger->add(Log::Dispatch::File->new
+ ( name=>'rtlog',
+ min_level=> $RT::LogToFile,
+ filename=> $filename,
+ mode=>'append',
+ callbacks => $simple_cb,
+ ));
+ }
+ if ($RT::LogToScreen) {
+ package Log::Dispatch::Screen;
+ require Log::Dispatch::Screen;
+ $RT::Logger->add(Log::Dispatch::Screen->new
+ ( name => 'screen',
+ min_level => $RT::LogToScreen,
+ callbacks => $simple_cb,
+ stderr => 1,
+ ));
+ }
+ if ($RT::LogToSyslog) {
+ package Log::Dispatch::Syslog;
+ require Log::Dispatch::Syslog;
+ $RT::Logger->add(Log::Dispatch::Syslog->new
+ ( name => 'syslog',
+ ident => 'RT',
+ min_level => $RT::LogToSyslog,
+ callbacks => $syslog_cb,
+ stderr => 1,
+ @RT::LogToSyslogConf
+ ));
+ }
+
}
+# {{{ Signal handlers
-# Signal handlers
## This is the default handling of warnings and die'ings in the code
## (including other used modules - maybe except for errors catched by
## Mason). It will log all problems through the standard logging
@@ -371,15 +355,16 @@ sub InitLogging {
#When we call die, trap it and log->crit with the value of the die.
- $SIG{__DIE__} = sub {
- # if we are not in eval and perl is not parsing code
- # then rollback transactions and log RT error
- unless ($^S || !defined $^S ) {
- $RT::Handle->Rollback(1) if $RT::Handle;
- $RT::Logger->crit("$_[0]") if $RT::Logger;
- }
- die $_[0];
- };
+$SIG{__DIE__} = sub {
+ unless ($^S || !defined $^S ) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("$_[0]");
+ }
+ die $_[0];
+};
+
+# }}}
+
}
@@ -415,9 +400,10 @@ EOF
}
}
+
=head2 InitClasses
-Load all modules that define base classes.
+Load all modules that define base classes
=cut
@@ -440,8 +426,6 @@ sub InitClasses {
require RT::ObjectCustomFields;
require RT::ObjectCustomFieldValues;
require RT::Attributes;
- require RT::Dashboard;
- require RT::Approval;
# on a cold server (just after restart) people could have an object
# in the session, as we deserialize it so we never call constructor
@@ -468,196 +452,21 @@ sub InitClasses {
);
}
-=head2 InitSystemObjects
-
-Initializes system objects: C<$RT::System>, C<$RT::SystemUser>
-and C<$RT::Nobody>.
-
-=cut
-
-sub InitSystemObjects {
-
- #RT's system user is a genuine database user. its id lives here
- require RT::CurrentUser;
- $SystemUser = new RT::CurrentUser;
- $SystemUser->LoadByName('RT_System');
-
- #RT's "nobody user" is a genuine database user. its ID lives here.
- $Nobody = new RT::CurrentUser;
- $Nobody->LoadByName('Nobody');
-
- require RT::System;
- $System = RT::System->new( $SystemUser );
-}
-
-=head1 CLASS METHODS
-
-=head2 Config
-
-Returns the current L<config object RT::Config>, but note that
-you must L<load config /LoadConfig> first otherwise this method
-returns undef.
-
-Method can be called as class method.
-
-=cut
-
-sub Config { return $Config }
-
-=head2 DatabaseHandle
-
-Returns the current L<database handle object RT::Handle>.
-
-See also L</ConnectToDatabase>.
-
-=cut
-
-sub DatabaseHandle { return $Handle }
-
-=head2 Logger
-
-Returns the logger. See also L</InitLogging>.
-
-=cut
-
-sub Logger { return $Logger }
-
-=head2 System
-
-Returns the current L<system object RT::System>. See also
-L</InitSystemObjects>.
-
-=cut
-
-sub System { return $System }
-
-=head2 SystemUser
-
-Returns the system user's object, it's object of
-L<RT::CurrentUser> class that represents the system. See also
-L</InitSystemObjects>.
-
-=cut
-
-sub SystemUser { return $SystemUser }
-
-=head2 Nobody
-
-Returns object of Nobody. It's object of L<RT::CurrentUser> class
-that represents a user who can own ticket and nothing else. See
-also L</InitSystemObjects>.
-
-=cut
-
-sub Nobody { return $Nobody }
-
-=head2 Plugins
-
-Returns a listref of all Plugins currently configured for this RT instance.
-You can define plugins by adding them to the @Plugins list in your RT_SiteConfig
-
-=cut
-
-our @PLUGINS = ();
-sub Plugins {
- my $self = shift;
- unless (@PLUGINS) {
- $self->InitPluginPaths;
- @PLUGINS = $self->InitPlugins;
- }
- return \@PLUGINS;
-}
-
-=head2 PluginDirs
-
-Takes optional subdir (e.g. po, lib, etc.) and return plugins' dirs that exist.
-
-This code chacke plugins names or anything else and required when main config
-is loaded to load plugins' configs.
-
-=cut
-
-sub PluginDirs {
- my $self = shift;
- my $subdir = shift;
-
- require RT::Plugin;
-
- my @res;
- foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
- my $path = RT::Plugin->new( name => $plugin )->Path( $subdir );
- next unless -d $path;
- push @res, $path;
- }
- return @res;
-}
-
-=head2 InitPluginPaths
-
-Push plugins' lib paths into @INC right after F<local/lib>.
-In case F<local/lib> isn't in @INC, append them to @INC
-
-=cut
-
-sub InitPluginPaths {
- my $self = shift || __PACKAGE__;
-
- my @lib_dirs = $self->PluginDirs('lib');
-
- my @tmp_inc;
- my $added;
- for (@INC) {
- if ( Cwd::realpath($_) eq $RT::LocalLibPath) {
- push @tmp_inc, $_, @lib_dirs;
- $added = 1;
- } else {
- push @tmp_inc, $_;
- }
- }
-
- # append @lib_dirs in case $RT::LocalLibPath isn't in @INC
- push @tmp_inc, @lib_dirs unless $added;
-
- my %seen;
- @INC = grep !$seen{$_}++, @tmp_inc;
-}
-
-=head2 InitPlugins
-
-Initialze all Plugins found in the RT configuration file, setting up their lib and HTML::Mason component roots.
+# }}}
-=cut
-
-sub InitPlugins {
- my $self = shift;
- my @plugins;
- require RT::Plugin;
- foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
- $plugin->require;
- die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
- push @plugins, RT::Plugin->new(name =>$plugin);
- }
- return @plugins;
-}
+sub SystemUser {
+ return($SystemUser);
+}
-sub InstallMode {
- my $self = shift;
- if (@_) {
- $_INSTALL_MODE = shift;
- if($_INSTALL_MODE) {
- require RT::CurrentUser;
- $SystemUser = RT::CurrentUser->new();
- }
- }
- return $_INSTALL_MODE;
+sub Nobody {
+ return ($Nobody);
}
-
=head1 BUGS
-Please report them to rt-bugs@bestpractical.com, if you know what's
-broken and have at least some idea of what needs to be fixed.
+Please report them to rt-bugs@fsck.com, if you know what's broken and have at least
+some idea of what needs to be fixed.
If you're not sure what's going on, report them rt-devel@lists.bestpractical.com.
@@ -666,6 +475,14 @@ If you're not sure what's going on, report them rt-devel@lists.bestpractical.com
L<RT::StyleGuide>
L<DBIx::SearchBuilder>
+=begin testing
+
+ok ($RT::Nobody->Name() eq 'Nobody', "Nobody is nobody");
+ok ($RT::Nobody->Name() ne 'root', "Nobody isn't named root");
+ok ($RT::SystemUser->Name() eq 'RT_System', "The system user is RT_System");
+ok ($RT::SystemUser->Name() ne 'noname', "The system user isn't noname");
+
+=end testing
=cut
diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm
index 7f21ba05e..1501a125e 100755
--- a/rt/lib/RT/ACE.pm
+++ b/rt/lib/RT/ACE.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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:
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# (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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -69,7 +44,11 @@ RT::ACE
=cut
package RT::ACE;
-use base 'RT::Record';
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
sub _Init {
my $self = shift;
@@ -82,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -125,7 +104,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -134,14 +113,14 @@ Returns the current value of id.
=cut
-=head2 PrincipalType
+=item PrincipalType
Returns the current value of PrincipalType.
(In the database, PrincipalType is stored as varchar(25).)
-=head2 SetPrincipalType VALUE
+=item SetPrincipalType VALUE
Set PrincipalType to VALUE.
@@ -152,14 +131,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 PrincipalId
+=item PrincipalId
Returns the current value of PrincipalId.
(In the database, PrincipalId is stored as int(11).)
-=head2 SetPrincipalId VALUE
+=item SetPrincipalId VALUE
Set PrincipalId to VALUE.
@@ -170,14 +149,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 RightName
+=item RightName
Returns the current value of RightName.
(In the database, RightName is stored as varchar(25).)
-=head2 SetRightName VALUE
+=item SetRightName VALUE
Set RightName to VALUE.
@@ -188,14 +167,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ObjectType
+=item ObjectType
Returns the current value of ObjectType.
(In the database, ObjectType is stored as varchar(25).)
-=head2 SetObjectType VALUE
+=item SetObjectType VALUE
Set ObjectType to VALUE.
@@ -206,14 +185,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ObjectId
+=item ObjectId
Returns the current value of ObjectId.
(In the database, ObjectId is stored as int(11).)
-=head2 SetObjectId VALUE
+=item SetObjectId VALUE
Set ObjectId to VALUE.
@@ -224,14 +203,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 DelegatedBy
+=item DelegatedBy
Returns the current value of DelegatedBy.
(In the database, DelegatedBy is stored as int(11).)
-=head2 SetDelegatedBy VALUE
+=item SetDelegatedBy VALUE
Set DelegatedBy to VALUE.
@@ -242,14 +221,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 DelegatedFrom
+=item DelegatedFrom
Returns the current value of DelegatedFrom.
(In the database, DelegatedFrom is stored as int(11).)
-=head2 SetDelegatedFrom VALUE
+=item SetDelegatedFrom VALUE
Set DelegatedFrom to VALUE.
@@ -261,25 +240,25 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
PrincipalType =>
- {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ {read => 1, write => 1, type => 'varchar(25)', default => ''},
PrincipalId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
RightName =>
- {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ {read => 1, write => 1, type => 'varchar(25)', default => ''},
ObjectType =>
- {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ {read => 1, write => 1, type => 'varchar(25)', default => ''},
ObjectId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
DelegatedBy =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
DelegatedFrom =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
}
};
@@ -311,7 +290,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/ACL.pm b/rt/lib/RT/ACL.pm
index 1dc66e8b6..81f59c6d0 100755
--- a/rt/lib/RT/ACL.pm
+++ b/rt/lib/RT/ACL.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -72,9 +47,12 @@ use strict;
package RT::ACL;
-use base 'RT::SearchBuilder';
+use RT::SearchBuilder;
use RT::ACE;
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
sub _Init {
my $self = shift;
@@ -86,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::ACE item
@@ -123,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
index 3734d819a..81f7bddfa 100755
--- a/rt/lib/RT/Action/Autoreply.pm
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,51 +14,20 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
package RT::Action::Autoreply;
+require RT::Action::SendEmail;
use strict;
-use warnings;
-
-use base qw(RT::Action::SendEmail);
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::SendEmail);
-=head2 Prepare
-
-Set up the relevant recipients, then call our parent.
-
-=cut
-
-
-sub Prepare {
- my $self = shift;
- $self->SetRecipients();
- $self->SUPER::Prepare();
-}
# {{{ sub SetRecipients
@@ -96,37 +59,35 @@ Set this message\'s return address to the apropriate queue address
sub SetReturnAddress {
my $self = shift;
+ my %args = ( is_comment => 0,
+ @_
+ );
- my $friendly_name;
-
- if (RT->Config->Get('UseFriendlyFromLine')) {
- $friendly_name = $self->TicketObj->QueueObj->Description ||
- $self->TicketObj->QueueObj->Name;
- }
-
- $self->SUPER::SetReturnAddress( @_, friendly_name => $friendly_name );
+ my $replyto;
+ if ($args{'is_comment'}) {
+ $replyto = $self->TicketObj->QueueObj->CommentAddress ||
+ $RT::CommentAddress;
+ }
+ else {
+ $replyto = $self->TicketObj->QueueObj->CorrespondAddress ||
+ $RT::CorrespondAddress;
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('From')) {
+ my $friendly_name = $self->TicketObj->QueueObj->Description ||
+ $self->TicketObj->QueueObj->Name;
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader('From', "\"$friendly_name\" <$replyto>");
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
+ $self->SetHeader('Reply-To', "$replyto");
+ }
}
# }}}
-# {{{{ sub SetRTSpecialHeaders
-
-=head2 SetRTSpecialHeaders
-
-Set the C<Auto-Generated> header to C<auto-replied>, in accordance
-with RFC3834.
-
-=cut
-
-sub SetRTSpecialHeaders {
- my $self = shift;
- $self->SUPER::SetRTSpecialHeaders(@_);
- $self->SetHeader( 'Auto-Submitted', 'auto-replied' );
-}
-
-# }}}
-
eval "require RT::Action::Autoreply_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Autoreply_Vendor.pm});
eval "require RT::Action::Autoreply_Local";
diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm
index 5e8ef32ce..007d299c7 100755
--- a/rt/lib/RT/Action/Generic.pm
+++ b/rt/lib/RT/Action/Generic.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,35 +14,16 @@
# 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.)
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
=head1 NAME
- RT::Action::Generic - deprecated, see RT::Action
+ RT::Action::Generic - a generic baseclass for RT Actions
=head1 SYNOPSIS
@@ -56,25 +31,165 @@
=head1 DESCRIPTION
-This module is provided only for backwards compatibility.
-
=head1 METHODS
+=begin testing
+
+ok (require RT::Action::Generic);
+
+=end testing
=cut
-use strict;
-use warnings;
package RT::Action::Generic;
-use base 'RT::Action';
+
+use strict;
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub new
+sub loc {
+ my $self = shift;
+ return $self->{'ScripObj'}->loc(@_);
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ ScripObj => undef,
+ TemplateObj => undef,
+ Argument => undef,
+ Type => undef,
+ @_ );
+
+
+ $self->{'Argument'} = $args{'Argument'};
+ $self->{'ScripObj'} = $args{'ScripObj'};
+ $self->{'TicketObj'} = $args{'TicketObj'};
+ $self->{'TransactionObj'} = $args{'TransactionObj'};
+ $self->{'TemplateObj'} = $args{'TemplateObj'};
+ $self->{'Type'} = $args{'Type'};
+}
+# }}}
+
+# Access Scripwide data
+
+# {{{ sub Argument
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+# {{{ sub TicketObj
+sub TicketObj {
+ my $self = shift;
+ return($self->{'TicketObj'});
+}
+# }}}
+
+# {{{ sub TransactionObj
+sub TransactionObj {
+ my $self = shift;
+ return($self->{'TransactionObj'});
+}
+# }}}
+
+# {{{ sub TemplateObj
+sub TemplateObj {
+ my $self = shift;
+ return($self->{'TemplateObj'});
+}
+# }}}
+
+# {{{ sub ScripObj
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+
+# {{{ sub Type
+sub Type {
+ my $self = shift;
+ return($self->{'Type'});
+}
+# }}}
+
+
+# Scrip methods
+
+#Do what we need to do and send it out.
+
+# {{{ sub Commit
+sub Commit {
+ my $self = shift;
+ return(0, $self->loc("Commit Stubbed"));
+}
+# }}}
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return $self->loc("No description for [_1]", ref $self);
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+ return (0, $self->loc("Prepare Stubbed"));
+}
+# }}}
+
+
+#If this rule applies to this transaction, return true.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ return(undef);
+}
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self = shift;
+
+ # We need to clean up all the references that might maybe get
+ # oddly circular
+ $self->{'TemplateObj'} =undef
+ $self->{'TicketObj'} = undef;
+ $self->{'TransactionObj'} = undef;
+ $self->{'ScripObj'} = undef;
+
+
+
+}
+
+# }}}
eval "require RT::Action::Generic_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Vendor.pm});
-warn "RT::Action::Generic has become RT::Action. Please adjust your deprecated RT::Action::Generic_Vendor file at " . $INC{"RT/Action/Generic_Vendor.pm"} if !$@;
-
eval "require RT::Action::Generic_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Local.pm});
-warn "RT::Action::Generic has become RT::Action. Please adjust your deprecated RT::Action::Generic_Local file at " . $INC{"RT/Action/Generic_Local.pm"} if !$@;
1;
-
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
index 30238fd61..1e4e4c073 100755
--- a/rt/lib/RT/Action/Notify.pm
+++ b/rt/lib/RT/Action/Notify.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,54 +14,21 @@
# 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.)
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
-#
+# END LICENSE BLOCK
package RT::Action::Notify;
+require RT::Action::SendEmail;
use strict;
-use warnings;
-
-use base qw(RT::Action::SendEmail);
-
-use Email::Address;
-
-=head2 Prepare
-
-Set up the relevant recipients, then call our parent.
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::SendEmail);
-=cut
-
-
-sub Prepare {
- my $self = shift;
- $self->SetRecipients();
- $self->SUPER::Prepare();
-}
+# {{{ sub SetRecipients
=head2 SetRecipients
@@ -79,63 +40,65 @@ Explicitly B<does not> notify the creator of the transaction by default
sub SetRecipients {
my $self = shift;
- my $ticket = $self->TicketObj;
-
my $arg = $self->Argument;
+
$arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
my ( @To, @PseudoTo, @Cc, @Bcc );
- if ( $arg =~ /\bOtherRecipients\b/ ) {
- if ( my $attachment = $self->TransactionObj->Attachments->First ) {
- push @Cc, map { $_->address } Email::Address->parse(
- $attachment->GetHeader('RT-Send-Cc')
- );
- push @Bcc, map { $_->address } Email::Address->parse(
- $attachment->GetHeader('RT-Send-Bcc')
- );
+ if ($arg =~ /\bOtherRecipients\b/) {
+ if ($self->TransactionObj->Attachments->First) {
+ push (@Cc, $self->TransactionObj->Attachments->First->GetHeader('RT-Send-Cc'));
+ push (@Bcc, $self->TransactionObj->Attachments->First->GetHeader('RT-Send-Bcc'));
}
}
if ( $arg =~ /\bRequestor\b/ ) {
- push @To, $ticket->Requestors->MemberEmailAddresses;
+ push ( @To, $self->TicketObj->Requestors->MemberEmailAddresses );
}
+
+
if ( $arg =~ /\bCc\b/ ) {
#If we have a To, make the Ccs, Ccs, otherwise, promote them to To
if (@To) {
- push ( @Cc, $ticket->Cc->MemberEmailAddresses );
- push ( @Cc, $ticket->QueueObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
}
else {
- push ( @Cc, $ticket->Cc->MemberEmailAddresses );
- push ( @To, $ticket->QueueObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @To, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
}
}
- if ( $arg =~ /\bOwner\b/ && $ticket->OwnerObj->id != $RT::Nobody->id ) {
- # If we're not sending to Ccs or requestors,
+ if ( ( $arg =~ /\bOwner\b/ )
+ && ( $self->TicketObj->OwnerObj->id != $RT::Nobody->id ) )
+ {
+
+ # If we're not sending to Ccs or requestors,
# then the Owner can be the To.
if (@To) {
- push ( @Bcc, $ticket->OwnerObj->EmailAddress );
+ push ( @Bcc, $self->TicketObj->OwnerObj->EmailAddress );
}
else {
- push ( @To, $ticket->OwnerObj->EmailAddress );
+ push ( @To, $self->TicketObj->OwnerObj->EmailAddress );
}
}
if ( $arg =~ /\bAdminCc\b/ ) {
- push ( @Bcc, $ticket->AdminCc->MemberEmailAddresses );
- push ( @Bcc, $ticket->QueueObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $self->TicketObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $self->TicketObj->QueueObj->AdminCc->MemberEmailAddresses );
}
- if ( RT->Config->Get('UseFriendlyToLine') ) {
+ if ($RT::UseFriendlyToLine) {
unless (@To) {
- push @PseudoTo,
- sprintf RT->Config->Get('FriendlyToLineFormat'), $arg, $ticket->id;
+ push (
+ @PseudoTo,
+ sprintf($RT::FriendlyToLineFormat, $arg, $self->TicketObj->id),
+ );
}
}
@@ -144,21 +107,23 @@ sub SetRecipients {
#Strip the sender out of the To, Cc and AdminCc and set the
# recipients fields used to build the message by the superclass.
# unless a flag is set
- if (RT->Config->Get('NotifyActor')) {
+ if ($RT::NotifyActor) {
@{ $self->{'To'} } = @To;
@{ $self->{'Cc'} } = @Cc;
@{ $self->{'Bcc'} } = @Bcc;
}
else {
- @{ $self->{'To'} } = grep ( lc $_ ne lc $creator, @To );
- @{ $self->{'Cc'} } = grep ( lc $_ ne lc $creator, @Cc );
- @{ $self->{'Bcc'} } = grep ( lc $_ ne lc $creator, @Bcc );
+ @{ $self->{'To'} } = grep ( !/^$creator$/, @To );
+ @{ $self->{'Cc'} } = grep ( !/^$creator$/, @Cc );
+ @{ $self->{'Bcc'} } = grep ( !/^$creator$/, @Bcc );
}
@{ $self->{'PseudoTo'} } = @PseudoTo;
-
+ return (1);
}
+# }}}
+
eval "require RT::Action::Notify_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Vendor.pm});
eval "require RT::Action::Notify_Local";
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
index b2eb5acd8..210e4ab15 100755
--- a/rt/lib/RT/Action/NotifyAsComment.pm
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,38 +14,20 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
package RT::Action::NotifyAsComment;
require RT::Action::Notify;
use strict;
-use warnings;
-use base qw(RT::Action::Notify);
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Notify);
+
=head2 SetReturnAddress
diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm
index ff826ccc1..02ff3a58c 100644
--- a/rt/lib/RT/Action/ResolveMembers.pm
+++ b/rt/lib/RT/Action/ResolveMembers.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,39 +14,22 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# This Action will resolve all members of a resolved group ticket
package RT::Action::ResolveMembers;
-use base 'RT::Action';
+require RT::Action::Generic;
require RT::Links;
use strict;
+use vars qw/@ISA/;
+@ISA=qw(RT::Action::Generic);
#Do what we need to do and send it out.
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
index a09bd3e56..dac8fc8e7 100755
--- a/rt/lib/RT/Action/SendEmail.pm
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,46 +14,25 @@
# 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:
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# (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 }}}
-
+# END LICENSE BLOCK
# Portions Copyright 2000 Tobias Brox <tobix@cpan.org>
package RT::Action::SendEmail;
+require RT::Action::Generic;
use strict;
-use warnings;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
-use base qw(RT::Action);
+use MIME::Words qw(encode_mimeword);
use RT::EmailParser;
-use RT::Interface::Email;
-use Email::Address;
-our @EMAIL_RECIPIENT_HEADERS = qw(To Cc Bcc);
-
=head1 NAME
@@ -69,493 +42,273 @@ RT::Action::AutoReply is a good example subclass.
=head1 SYNOPSIS
- use base 'RT::Action::SendEmail';
+ require RT::Action::SendEmail;
+ @ISA = qw(RT::Action::SendEmail);
+
=head1 DESCRIPTION
Basically, you create another module RT::Action::YourAction which ISA
RT::Action::SendEmail.
-=head1 METHODS
+If you want to set the recipients of the mail to something other than
+the addresses mentioned in the To, Cc, Bcc and headers in
+the template, you should subclass RT::Action::SendEmail and override
+either the SetRecipients method or the SetTo, SetCc, etc methods (see
+the comments for the SetRecipients sub).
-=head2 CleanSlate
-Cleans class-wide options, like L</SquelchMailTo> or L</AttachTickets>.
+=begin testing
-=cut
+ok (require RT::Action::SendEmail);
-sub CleanSlate {
- my $self = shift;
- $self->SquelchMailTo(undef);
- $self->AttachTickets(undef);
-}
+=end testing
-=head2 Commit
-Sends the prepared message and writes outgoing record into DB if the feature is
-activated in the config.
+=head1 AUTHOR
-=cut
+Jesse Vincent <jesse@bestpractical.com> and Tobias Brox <tobix@cpan.org>
-sub Commit {
- my $self = shift;
+=head1 SEE ALSO
- $self->DeferDigestRecipients() if RT->Config->Get('RecordOutgoingEmail');
- my $message = $self->TemplateObj->MIMEObj;
-
- my $orig_message;
- if ( RT->Config->Get('RecordOutgoingEmail')
- && RT->Config->Get('GnuPG')->{'Enable'} )
- {
-
- # it's hacky, but we should know if we're going to crypt things
- my $attachment = $self->TransactionObj->Attachments->First;
-
- my %crypt;
- foreach my $argument (qw(Sign Encrypt)) {
- if ( $attachment
- && defined $attachment->GetHeader("X-RT-$argument") )
- {
- $crypt{$argument} = $attachment->GetHeader("X-RT-$argument");
- } else {
- $crypt{$argument} = $self->TicketObj->QueueObj->$argument();
- }
- }
- if ( $crypt{'Sign'} || $crypt{'Encrypt'} ) {
- $orig_message = $message->dup;
- }
- }
-
- my ($ret) = $self->SendMessage($message);
- if ( $ret > 0 && RT->Config->Get('RecordOutgoingEmail') ) {
- if ($orig_message) {
- $message->attach(
- Type => 'application/x-rt-original-message',
- Disposition => 'inline',
- Data => $orig_message->as_string,
- );
- }
- $self->RecordOutgoingMailTransaction($message);
- $self->RecordDeferredRecipients();
- }
+perl(1).
+=cut
- return ( abs $ret );
-}
+# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
-=head2 Prepare
+# {{{ sub _Init
+# We use _Init from RT::Action
+# }}}
-Builds an outgoing email we're going to send using scrip's template.
+# {{{ sub Commit
+#Do what we need to do and send it out.
+sub Commit {
+ my $self = shift;
-=cut
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+ my $msgid = $MIMEObj->head->get('Message-Id');
+ chomp $msgid;
+ $RT::Logger->info($msgid." #".$self->TicketObj->id."/".$self->TransactionObj->id." - Scrip ". $self->ScripObj->id ." ".$self->ScripObj->Description);
+ #send the email
-sub Prepare {
- my $self = shift;
+ # Weed out any RT addresses. We really don't want to talk to ourselves!
+ @{$self->{'To'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'To'}});
+ @{$self->{'Cc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Cc'}});
+ @{$self->{'Bcc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Bcc'}});
+ # If there are no recipients, don't try to send the message.
+ # If the transaction has content and has the header RT-Squelch-Replies-To
- my ( $result, $message ) = $self->TemplateObj->Parse(
- Argument => $self->Argument,
- TicketObj => $self->TicketObj,
- TransactionObj => $self->TransactionObj
- );
- if ( !$result ) {
- return (undef);
- }
+ if ( defined $self->TransactionObj->Attachments->First() ) {
- my $MIMEObj = $self->TemplateObj->MIMEObj;
+ my $squelch = $self->TransactionObj->Attachments->First->GetHeader( 'RT-Squelch-Replies-To');
- # Header
- $self->SetRTSpecialHeaders();
+ if ($squelch) {
+ my @blacklist = split ( /,/, $squelch );
- $self->RemoveInappropriateRecipients();
+ # Cycle through the people we're sending to and pull out anyone on the
+ # system blacklist
- my %seen;
- foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
- @{ $self->{$type} }
- = grep defined && length && !$seen{ lc $_ }++,
- @{ $self->{$type} };
+ foreach my $person_to_yank (@blacklist) {
+ $person_to_yank =~ s/\s//g;
+ @{ $self->{'To'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'To'} } );
+ @{ $self->{'Cc'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'Cc'} } );
+ @{ $self->{'Bcc'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'Bcc'} } );
+ }
+ }
}
# Go add all the Tos, Ccs and Bccs that we need to to the message to
# make it happy, but only if we actually have values in those arrays.
-# TODO: We should be pulling the recipients out of the template and shove them into To, Cc and Bcc
+ $self->SetHeader( 'To', join ( ',', @{ $self->{'To'} } ) )
+ if ( $self->{'To'} && @{ $self->{'To'} } );
+ $self->SetHeader( 'Cc', join ( ',', @{ $self->{'Cc'} } ) )
+ if ( $self->{'Cc'} && @{ $self->{'Cc'} } );
+ $self->SetHeader( 'Bcc', join ( ',', @{ $self->{'Bcc'} } ) )
+ if ( $self->{'Cc'} && @{ $self->{'Bcc'} } );
- for my $header (@EMAIL_RECIPIENT_HEADERS) {
- $self->SetHeader( $header, join( ', ', @{ $self->{$header} } ) )
- if ( !$MIMEObj->head->get($header)
- && $self->{$header}
- && @{ $self->{$header} } );
-}
- # PseudoTo (fake to headers) shouldn't get matched for message recipients.
- # If we don't have any 'To' header (but do have other recipients), drop in
- # the pseudo-to header.
- $self->SetHeader( 'To', join( ', ', @{ $self->{'PseudoTo'} } ) )
- if $self->{'PseudoTo'}
- && @{ $self->{'PseudoTo'} }
- && !$MIMEObj->head->get('To')
- && ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc') );
-
- # We should never have to set the MIME-Version header
- $self->SetHeader( 'MIME-Version', '1.0' );
-
- # fsck.com #5959: Since RT sends 8bit mail, we should say so.
- $self->SetHeader( 'Content-Transfer-Encoding', '8bit' );
-
- # For security reasons, we only send out textual mails.
- foreach my $part ( grep !$_->is_multipart, $MIMEObj->parts_DFS ) {
- my $type = $part->mime_type || 'text/plain';
- $type = 'text/plain' unless RT::I18N::IsTextualContentType($type);
- $part->head->mime_attr( "Content-Type" => $type );
- $part->head->mime_attr( "Content-Type.charset" => 'utf-8' );
- }
+ $self->SetHeader('MIME-Version', '1.0');
- RT::I18N::SetMIMEEntityToEncoding( $MIMEObj,
- RT->Config->Get('EmailOutputEncoding'),
- 'mime_words_ok', );
+ # try to convert message body from utf-8 to $RT::EmailOutputEncoding
+ $self->SetHeader( 'Content-Type', 'text/plain; charset="utf-8"' );
- # Build up a MIME::Entity that looks like the original message.
- $self->AddAttachments if ( $MIMEObj->head->get('RT-Attach-Message')
- && ( $MIMEObj->head->get('RT-Attach-Message') !~ /^(n|no|0|off|false)$/i ) );
-
- $self->AddTickets;
-
- my $attachment = $self->TransactionObj->Attachments->First;
- if ($attachment
- && !(
- $attachment->GetHeader('X-RT-Encrypt')
- || $self->TicketObj->QueueObj->Encrypt
- )
- )
- {
- $attachment->SetHeader( 'X-RT-Encrypt' => 1 )
- if ( $attachment->GetHeader("X-RT-Incoming-Encryption") || '' ) eq
- 'Success';
- }
+ RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, $RT::EmailOutputEncoding, 'mime_words_ok' );
+ $self->SetHeader( 'Content-Type', 'text/plain; charset="' . $RT::EmailOutputEncoding . '"' );
- return $result;
-}
-=head2 To
+ # Build up a MIME::Entity that looks like the original message.
-Returns an array of L<Email::Address> objects containing all the To: recipients for this notification
+ my $do_attach = $self->TemplateObj->MIMEObj->head->get('RT-Attach-Message');
-=cut
+ if ($do_attach) {
+ $self->TemplateObj->MIMEObj->head->delete('RT-Attach-Message');
-sub To {
- my $self = shift;
- return ( $self->AddressesFromHeader('To') );
-}
+ my $attachments = RT::Attachments->new($RT::SystemUser);
+ $attachments->Limit( FIELD => 'TransactionId',
+ VALUE => $self->TransactionObj->Id );
+ $attachments->OrderBy('id');
-=head2 Cc
+ my $transaction_content_obj = $self->TransactionObj->ContentObj;
-Returns an array of L<Email::Address> objects containing all the Cc: recipients for this notification
+ # attach any of this transaction's attachments
+ while ( my $attach = $attachments->Next ) {
-=cut
+ # Don't attach anything blank
+ next unless ( $attach->ContentLength );
-sub Cc {
- my $self = shift;
- return ( $self->AddressesFromHeader('Cc') );
-}
-
-=head2 Bcc
-
-Returns an array of L<Email::Address> objects containing all the Bcc: recipients for this notification
+ # We want to make sure that we don't include the attachment that's being sued as the "Content" of this message"
+ next
+ if ( $transaction_content_obj
+ && $transaction_content_obj->Id == $attach->Id
+ && $transaction_content_obj->ContentType =~ qr{text/plain}i
+ );
+ $MIMEObj->make_multipart('mixed');
+ $MIMEObj->attach( Type => $attach->ContentType,
+ Charset => $attach->OriginalEncoding,
+ Data => $attach->OriginalContent,
+ Filename => $self->MIMEEncodeString( $attach->Filename, $RT::EmailOutputEncoding ),
+ Encoding => '-SUGGEST');
+ }
-=cut
+ }
-sub Bcc {
- my $self = shift;
- return ( $self->AddressesFromHeader('Bcc') );
-}
+ my $retval = $self->SendMessage($MIMEObj);
-sub AddressesFromHeader {
- my $self = shift;
- my $field = shift;
- my $header = $self->TemplateObj->MIMEObj->head->get($field);
- my @addresses = Email::Address->parse($header);
- return (@addresses);
+ return ($retval);
}
-=head2 SendMessage MIMEObj
-
-sends the message using RT's preferred API.
-TODO: Break this out to a separate module
-
-=cut
-
-sub SendMessage {
-
- # DO NOT SHIFT @_ in this subroutine. It breaks Hook::LexWrap's
- # ability to pass @_ to a 'post' routine.
- my ( $self, $MIMEObj ) = @_;
-
- my $msgid = $MIMEObj->head->get('Message-ID');
- chomp $msgid;
+# }}}
- $self->ScripActionObj->{_Message_ID}++;
+# {{{ sub Prepare
- $RT::Logger->info( $msgid . " #"
- . $self->TicketObj->id . "/"
- . $self->TransactionObj->id
- . " - Scrip "
- . ($self->ScripObj->id || '#rule'). " "
- . ( $self->ScripObj->Description || '' ) );
-
- my $status = RT::Interface::Email::SendEmail(
- Entity => $MIMEObj,
- Ticket => $self->TicketObj,
- Transaction => $self->TransactionObj,
- );
-
-
- return $status unless ($status > 0 || exists $self->{'Deferred'});
+sub Prepare {
+ my $self = shift;
- my $success = $msgid . " sent ";
- foreach (@EMAIL_RECIPIENT_HEADERS) {
- my $recipients = $MIMEObj->head->get($_);
- $success .= " $_: " . $recipients if $recipients;
- }
+ # This actually populates the MIME::Entity fields in the Template Object
- if( exists $self->{'Deferred'} ) {
- for (qw(daily weekly susp)) {
- $success .= "\nBatched email $_ for: ". join(", ", keys %{ $self->{'Deferred'}{ $_ } } )
- if exists $self->{'Deferred'}{ $_ };
- }
+ unless ( $self->TemplateObj ) {
+ $RT::Logger->warning("No template object handed to $self\n");
}
- $success =~ s/\n//g;
-
- $RT::Logger->info($success);
-
- return (1);
-}
+ unless ( $self->TransactionObj ) {
+ $RT::Logger->warning("No transaction object handed to $self\n");
-=head2 AddAttachments
-
-Takes any attachments to this transaction and attaches them to the message
-we're building.
-
-=cut
-
-sub AddAttachments {
- my $self = shift;
-
- my $MIMEObj = $self->TemplateObj->MIMEObj;
-
- $MIMEObj->head->delete('RT-Attach-Message');
+ }
- my $attachments = RT::Attachments->new($RT::SystemUser);
- $attachments->Limit(
- FIELD => 'TransactionId',
- VALUE => $self->TransactionObj->Id
- );
+ unless ( $self->TicketObj ) {
+ $RT::Logger->warning("No ticket object handed to $self\n");
- # Don't attach anything blank
- $attachments->LimitNotEmpty;
- $attachments->OrderBy( FIELD => 'id' );
-
- # We want to make sure that we don't include the attachment that's
- # being used as the "Content" of this message" unless that attachment's
- # content type is not like text/...
- my $transaction_content_obj = $self->TransactionObj->ContentObj;
-
- if ( $transaction_content_obj
- && $transaction_content_obj->ContentType =~ m{text/}i )
- {
- # If this was part of a multipart/alternative, skip all of the kids
- my $parent = $transaction_content_obj->ParentObj;
- if ($parent and $parent->Id and $parent->ContentType eq "multipart/alternative") {
- $attachments->Limit(
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'parent',
- OPERATOR => '!=',
- VALUE => $parent->Id,
- );
- } else {
- $attachments->Limit(
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'id',
- OPERATOR => '!=',
- VALUE => $transaction_content_obj->Id,
- );
- }
}
- # attach any of this transaction's attachments
- my $seen_attachment = 0;
- while ( my $attach = $attachments->Next ) {
- if ( !$seen_attachment ) {
- $MIMEObj->make_multipart( 'mixed', Force => 1 );
- $seen_attachment = 1;
+ my ( $result, $message ) = $self->TemplateObj->Parse(
+ Argument => $self->Argument,
+ TicketObj => $self->TicketObj,
+ TransactionObj => $self->TransactionObj
+ );
+ if ($result) {
+
+ # Header
+ $self->SetSubject();
+ $self->SetSubjectToken();
+ $self->SetRecipients();
+ $self->SetReturnAddress();
+ $self->SetRTSpecialHeaders();
+ if ($RT::EmailOutputEncoding) {
+
+ # l10n related header
+ $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding );
}
- $self->AddAttachment($attach);
}
-}
-
-=head2 AddAttachment $attachment
-
-Takes one attachment object of L<RT::Attachmment> class and attaches it to the message
-we're building.
-=cut
+ return $result;
-sub AddAttachment {
- my $self = shift;
- my $attach = shift;
- my $MIMEObj = shift || $self->TemplateObj->MIMEObj;
-
- $MIMEObj->attach(
- Type => $attach->ContentType,
- Charset => $attach->OriginalEncoding,
- Data => $attach->OriginalContent,
- Filename => $self->MIMEEncodeString( $attach->Filename ),
- 'RT-Attachment:' => $self->TicketObj->Id . "/"
- . $self->TransactionObj->Id . "/"
- . $attach->id,
- Encoding => '-SUGGEST',
- );
}
-=head2 AttachTickets [@IDs]
+# }}}
-Returns or set list of ticket's IDs that should be attached to an outgoing message.
+# }}}
-B<Note> this method works as a class method and setup things global, so you have to
-clean list by passing undef as argument.
-
-=cut
-
-{
- my $list = [];
-
- sub AttachTickets {
- my $self = shift;
- $list = [ grep defined, @_ ] if @_;
- return @$list;
- }
-}
-
-=head2 AddTickets
+# {{{ SendMessage
+=head2 SendMessage MIMEObj
-Attaches tickets to the current message, list of tickets' ids get from
-L</AttachTickets> method.
+sends the message using RT's preferred API.
+TODO: Break this out to a seperate module
=cut
-sub AddTickets {
+sub SendMessage {
my $self = shift;
- $self->AddTicket($_) foreach $self->AttachTickets;
- return;
-}
-
-=head2 AddTicket $ID
+ my $MIMEObj = shift;
-Attaches a ticket with ID to the message.
+ my $msgid = $MIMEObj->head->get('Message-Id');
-Each ticket is attached as multipart entity and all its messages and attachments
-are attached as sub entities in order of creation, but only if transaction type
-is Create or Correspond.
-=cut
-
-sub AddTicket {
- my $self = shift;
- my $tid = shift;
-
- # XXX: we need a current user here, but who is current user?
- my $attachs = RT::Attachments->new($RT::SystemUser);
- my $txn_alias = $attachs->TransactionAlias;
- $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' );
- $attachs->Limit(
- ALIAS => $txn_alias,
- FIELD => 'Type',
- VALUE => 'Correspond'
- );
- $attachs->LimitByTicket($tid);
- $attachs->LimitNotEmpty;
- $attachs->OrderBy( FIELD => 'Created' );
-
- my $ticket_mime = MIME::Entity->build(
- Type => 'multipart/mixed',
- Top => 0,
- Description => "ticket #$tid",
- );
- while ( my $attachment = $attachs->Next ) {
- $self->AddAttachment( $attachment, $ticket_mime );
+ #If we don't have any recipients to send to, don't send a message;
+ unless ( $MIMEObj->head->get('To')
+ || $MIMEObj->head->get('Cc')
+ || $MIMEObj->head->get('Bcc') ) {
+ $RT::Logger->info($msgid. " No recipients found. Not sending.\n");
+ return (1);
}
- if ( $ticket_mime->parts ) {
- my $email_mime = $self->TemplateObj->MIMEObj;
- $email_mime->make_multipart;
- $email_mime->add_part($ticket_mime);
- }
- return;
-}
-=head2 RecordOutgoingMailTransaction MIMEObj
+ # PseudoTo (fake to headers) shouldn't get matched for message recipients.
+ # If we don't have any 'To' header, drop in the pseudo-to header.
-Record a transaction in RT with this outgoing message for future record-keeping purposes
-
-=cut
+ $self->SetHeader( 'To', join ( ',', @{ $self->{'PseudoTo'} } ) )
+ if ( $self->{'PseudoTo'} && ( @{ $self->{'PseudoTo'} } )
+ and ( !$MIMEObj->head->get('To') ) );
+ if ( $RT::MailCommand eq 'sendmailpipe' ) {
+ eval {
+ open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" );
+ print MAIL $MIMEObj->as_string;
+ close(MAIL);
+ };
+ if ($@) {
+ $RT::Logger->crit($msgid. "Could not send mail. -".$@ );
+ }
+ }
+ else {
+ my @mailer_args = ($RT::MailCommand);
+ local $ENV{MAILADDRESS};
-sub RecordOutgoingMailTransaction {
- my $self = shift;
- my $MIMEObj = shift;
+ if ( $RT::MailCommand eq 'sendmail' ) {
+ push @mailer_args, $RT::SendmailArguments;
+ }
+ elsif ( $RT::MailCommand eq 'smtp' ) {
+ $ENV{MAILADDRESS} = $RT::SMTPFrom || $MIMEObj->head->get('From');
+ push @mailer_args, (Server => $RT::SMTPServer);
+ push @mailer_args, (Debug => $RT::SMTPDebug);
+ }
+ else {
+ push @mailer_args, $RT::MailParams;
+ }
- my @parts = $MIMEObj->parts;
- my @attachments;
- my @keep;
- foreach my $part (@parts) {
- my $attach = $part->head->get('RT-Attachment');
- if ($attach) {
- $RT::Logger->debug(
- "We found an attachment. we want to not record it.");
- push @attachments, $attach;
- } else {
- $RT::Logger->debug("We found a part. we want to record it.");
- push @keep, $part;
+ unless ( $MIMEObj->send( @mailer_args ) ) {
+ $RT::Logger->crit($msgid. "Could not send mail." );
+ return (0);
}
}
- $MIMEObj->parts( \@keep );
- foreach my $attachment (@attachments) {
- $MIMEObj->head->add( 'RT-Attachment', $attachment );
- }
- RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, 'utf-8', 'mime_words_ok' );
- my $transaction
- = RT::Transaction->new( $self->TransactionObj->CurrentUser );
-
-# XXX: TODO -> Record attachments as references to things in the attachments table, maybe.
+ my $success = ($msgid. " sent To: ".$MIMEObj->head->get('To') . " Cc: ".$MIMEObj->head->get('Cc') . " Bcc: ".$MIMEObj->head->get('Bcc'));
+ $success =~ s/\n//gi;
+ $RT::Logger->info($success);
- my $type;
- if ( $self->TransactionObj->Type eq 'Comment' ) {
- $type = 'CommentEmailRecord';
- } else {
- $type = 'EmailRecord';
- }
+ return (1);
+}
- my $msgid = $MIMEObj->head->get('Message-ID');
- chomp $msgid;
+# }}}
- my ( $id, $msg ) = $transaction->Create(
- Ticket => $self->TicketObj->Id,
- Type => $type,
- Data => $msgid,
- MIMEObj => $MIMEObj,
- ActivateScrips => 0
- );
+# {{{ Deal with message headers (Set* subs, designed for easy overriding)
- if ($id) {
- $self->{'OutgoingMailTransaction'} = $id;
- } else {
- $RT::Logger->warning(
- "Could not record outgoing message transaction: $msg");
- }
- return $id;
-}
+# {{{ sub SetRTSpecialHeaders
=head2 SetRTSpecialHeaders
@@ -567,267 +320,85 @@ that don't matter much to anybody else.
sub SetRTSpecialHeaders {
my $self = shift;
- $self->SetSubject();
- $self->SetSubjectToken();
- $self->SetHeaderAsEncoding( 'Subject',
- RT->Config->Get('EmailOutputEncoding') )
- if ( RT->Config->Get('EmailOutputEncoding') );
- $self->SetReturnAddress();
- $self->SetReferencesHeaders();
-
- unless ( $self->TemplateObj->MIMEObj->head->get('Message-ID') ) {
-
- # Get Message-ID for this txn
- my $msgid = "";
- if ( my $msg = $self->TransactionObj->Message->First ) {
- $msgid = $msg->GetHeader("RT-Message-ID")
- || $msg->GetHeader("Message-ID");
- }
+ $self->SetReferences();
- # If there is one, and we can parse it, then base our Message-ID on it
- if ( $msgid
- and $msgid
- =~ s/<(rt-.*?-\d+-\d+)\.(\d+)-\d+-\d+\@\QRT->Config->Get('Organization')\E>$/
- "<$1." . $self->TicketObj->id
- . "-" . $self->ScripObj->id
- . "-" . $self->ScripActionObj->{_Message_ID}
- . "@" . RT->Config->Get('Organization') . ">"/eg
- and $2 == $self->TicketObj->id
- )
- {
- $self->SetHeader( "Message-ID" => $msgid );
- } else {
- $self->SetHeader(
- 'Message-ID' => RT::Interface::Email::GenMessageId(
- Ticket => $self->TicketObj,
- Scrip => $self->ScripObj,
- ScripAction => $self->ScripActionObj
- ),
- );
- }
- }
+ $self->SetMessageID();
- $self->SetHeader( 'Precedence', "bulk" )
- unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") );
+ $self->SetPrecedence();
- $self->SetHeader( 'X-RT-Loop-Prevention', RT->Config->Get('rtname') );
+ $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname );
$self->SetHeader( 'RT-Ticket',
- RT->Config->Get('rtname') . " #" . $self->TicketObj->id() );
+ $RT::rtname . " #" . $self->TicketObj->id() );
$self->SetHeader( 'Managed-by',
- "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
+ "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
-# XXX, TODO: use /ShowUser/ShowUserEntry(or something like that) when it would be
-# refactored into user's method.
- if ( my $email = $self->TransactionObj->CreatorObj->EmailAddress ) {
- $self->SetHeader( 'RT-Originator', $email );
- }
+ $self->SetHeader( 'RT-Originator',
+ $self->TransactionObj->CreatorObj->EmailAddress );
+ return ();
}
+# {{{ sub SetReferences
-sub DeferDigestRecipients {
- my $self = shift;
- $RT::Logger->debug( "Calling SetRecipientDigests for transaction " . $self->TransactionObj . ", id " . $self->TransactionObj->id );
-
- # The digest attribute will be an array of notifications that need to
- # be sent for this transaction. The array will have the following
- # format for its objects.
- # $digest_hash -> {daily|weekly|susp} -> address -> {To|Cc|Bcc}
- # -> sent -> {true|false}
- # The "sent" flag will be used by the cron job to indicate that it has
- # run on this transaction.
- # In a perfect world we might move this hash construction to the
- # extension module itself.
- my $digest_hash = {};
-
- foreach my $mailfield (@EMAIL_RECIPIENT_HEADERS) {
- # If we have a "PseudoTo", the "To" contains it, so we don't need to access it
- next if ( ( $self->{'PseudoTo'} && @{ $self->{'PseudoTo'} } ) && ( $mailfield eq 'To' ) );
- $RT::Logger->debug( "Working on mailfield $mailfield; recipients are " . join( ',', @{ $self->{$mailfield} } ) );
-
- # Store the 'daily digest' folk in an array.
- my ( @send_now, @daily_digest, @weekly_digest, @suspended );
-
- # Have to get the list of addresses directly from the MIME header
- # at this point.
- $RT::Logger->debug( $self->TemplateObj->MIMEObj->head->as_string );
- foreach my $rcpt ( map { $_->address } $self->AddressesFromHeader($mailfield) ) {
- next unless $rcpt;
- my $user_obj = RT::User->new($RT::SystemUser);
- $user_obj->LoadByEmail($rcpt);
- if ( ! $user_obj->id ) {
- # If there's an email address in here without an associated
- # RT user, pass it on through.
- $RT::Logger->debug( "User $rcpt is not associated with an RT user object. Send mail.");
- push( @send_now, $rcpt );
- next;
- }
-
- my $mailpref = RT->Config->Get( 'EmailFrequency', $user_obj ) || '';
- $RT::Logger->debug( "Got user mail preference '$mailpref' for user $rcpt");
-
- if ( $mailpref =~ /daily/i ) { push( @daily_digest, $rcpt ) }
- elsif ( $mailpref =~ /weekly/i ) { push( @weekly_digest, $rcpt ) }
- elsif ( $mailpref =~ /suspend/i ) { push( @suspended, $rcpt ) }
- else { push( @send_now, $rcpt ) }
- }
-
- # Reset the relevant mail field.
- $RT::Logger->debug( "Removing deferred recipients from $mailfield: line");
- if (@send_now) {
- $self->SetHeader( $mailfield, join( ', ', @send_now ) );
- } else { # No recipients! Remove the header.
- $self->TemplateObj->MIMEObj->head->delete($mailfield);
- }
-
- # Push the deferred addresses into the appropriate field in
- # our attribute hash, with the appropriate mail header.
- $RT::Logger->debug(
- "Setting deferred recipients for attribute creation");
- $digest_hash->{'daily'}->{$_} = {'header' => $mailfield , _sent => 0} for (@daily_digest);
- $digest_hash->{'weekly'}->{$_} ={'header' => $mailfield, _sent => 0} for (@weekly_digest);
- $digest_hash->{'susp'}->{$_} = {'header' => $mailfield, _sent =>0 } for (@suspended);
- }
-
- if ( scalar keys %$digest_hash ) {
-
- # Save the hash so that we can add it as an attribute to the
- # outgoing email transaction.
- $self->{'Deferred'} = $digest_hash;
- } else {
- $RT::Logger->debug( "No recipients found for deferred delivery on "
- . "transaction #"
- . $self->TransactionObj->id );
- }
-}
+=head2 SetReferences
+
+ # This routine will set the References: and In-Reply-To headers,
+# autopopulating it with all the correspondence on this ticket so
+# far. This should make RT responses threadable.
+=cut
-
-sub RecordDeferredRecipients {
+sub SetReferences {
my $self = shift;
- return unless exists $self->{'Deferred'};
- my $txn_id = $self->{'OutgoingMailTransaction'};
- return unless $txn_id;
+ # TODO: this one is broken. What is this email really a reply to?
+ # If it's a reply to an incoming message, we'll need to use the
+ # actual message-id from the appropriate Attachment object. For
+ # incoming mails, we would like to preserve the In-Reply-To and/or
+ # References.
- my $txn_obj = RT::Transaction->new( $self->CurrentUser );
- $txn_obj->Load( $txn_id );
- my( $ret, $msg ) = $txn_obj->AddAttribute(
- Name => 'DeferredRecipients',
- Content => $self->{'Deferred'}
- );
- $RT::Logger->warning( "Unable to add deferred recipients to outgoing transaction: $msg" )
- unless $ret;
+ $self->SetHeader( 'In-Reply-To',
+ "<rt-" . $self->TicketObj->id() . "\@" . $RT::rtname . ">" );
- return ($ret,$msg);
+ # TODO We should always add References headers for all message-ids
+ # of previous messages related to this ticket.
}
-=head2 SquelchMailTo [@ADDRESSES]
-
-Mark ADDRESSES to be removed from list of the recipients. Returns list of the addresses.
-To empty list pass undefined argument.
-
-B<Note> that this method can be called as class method and works globaly. Don't forget to
-clean this list when blocking is not required anymore, pass undef to do this.
-
-=cut
-
-{
- my $squelch = [];
+# }}}
- sub SquelchMailTo {
- my $self = shift;
- if (@_) {
- $squelch = [ grep defined, @_ ];
- }
- return @$squelch;
- }
-}
+# {{{ sub SetMessageID
-=head2 RemoveInappropriateRecipients
+=head2 SetMessageID
-Remove addresses that are RT addresses or that are on this transaction's blacklist
+Without this one, threading won't work very nice in email agents.
+Anyway, I'm not really sure it's that healthy if we need to send
+several separate/different emails about the same transaction.
=cut
-sub RemoveInappropriateRecipients {
+sub SetMessageID {
my $self = shift;
- my @blacklist = ();
-
- # If there are no recipients, don't try to send the message.
- # If the transaction has content and has the header RT-Squelch-Replies-To
-
- my $msgid = $self->TemplateObj->MIMEObj->head->get('Message-Id');
- if ( my $attachment = $self->TransactionObj->Attachments->First ) {
-
- if ( $attachment->GetHeader('RT-DetectedAutoGenerated') ) {
-
- # What do we want to do with this? It's probably (?) a bounce
- # caused by one of the watcher addresses being broken.
- # Default ("true") is to redistribute, for historical reasons.
-
- if ( !RT->Config->Get('RedistributeAutoGeneratedMessages') ) {
-
- # Don't send to any watchers.
- @{ $self->{$_} } = () for (@EMAIL_RECIPIENT_HEADERS);
- $RT::Logger->info( $msgid
- . " The incoming message was autogenerated. "
- . "Not redistributing this message based on site configuration."
- );
- } elsif ( RT->Config->Get('RedistributeAutoGeneratedMessages') eq
- 'privileged' )
- {
-
- # Only send to "privileged" watchers.
- foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
- foreach my $addr ( @{ $self->{$type} } ) {
- my $user = RT::User->new($RT::SystemUser);
- $user->LoadByEmail($addr);
- push @blacklist, $addr if ( !$user->Privileged );
- }
- }
- $RT::Logger->info( $msgid
- . " The incoming message was autogenerated. "
- . "Not redistributing this message to unprivileged users based on site configuration."
- );
- }
- }
-
- if ( my $squelch = $attachment->GetHeader('RT-Squelch-Replies-To') ) {
- push @blacklist, split( /,/, $squelch );
- }
- }
-
-# Let's grab the SquelchMailTo attribue and push those entries into the @blacklist
- push @blacklist, map $_->Content, $self->TicketObj->SquelchMailTo;
- push @blacklist, $self->SquelchMailTo;
-
- # Cycle through the people we're sending to and pull out anyone on the
- # system blacklist
+ # TODO this one might be sort of broken. If we have several scrips +++
+ # sending several emails to several different persons, we need to
+ # pull out different message-ids. I'd suggest message ids like
+ # "rt-ticket#-transaction#-scrip#-receipient#"
+
+ $self->SetHeader( 'Message-ID',
+ "<rt-"
+ . $RT::VERSION ."-"
+ . $self->TicketObj->id() . "-"
+ . $self->TransactionObj->id() . "."
+ . rand(20) . "\@"
+ . $RT::Organization . ">" )
+ unless $self->TemplateObj->MIMEObj->head->get('Message-ID');
+}
- # Trim leading and trailing spaces.
- @blacklist = map { RT::User->CanonicalizeEmailAddress( $_->address ) } Email::Address->parse(join(', ', grep {defined} @blacklist));
+# }}}
- foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
- my @addrs;
- foreach my $addr ( @{ $self->{$type} } ) {
+# }}}
- # Weed out any RT addresses. We really don't want to talk to ourselves!
- # If we get a reply back, that means it's not an RT address
- if ( !RT::EmailParser->CullRTAddresses($addr) ) {
- $RT::Logger->info( $msgid . "$addr appears to point to this RT instance. Skipping" );
- next;
- }
- if ( grep /^\Q$addr\E$/, @blacklist ) {
- $RT::Logger->info( $msgid . "$addr was blacklisted for outbound mail on this transaction. Skipping");
- next;
- }
- push @addrs, $addr;
- }
- @{ $self->{$type} } = @addrs;
- }
-}
+# {{{ sub SetReturnAddress
=head2 SetReturnAddress is_comment => BOOLEAN
@@ -838,11 +409,8 @@ Calculate and set From and Reply-To headers based on the is_comment flag.
sub SetReturnAddress {
my $self = shift;
- my %args = (
- is_comment => 0,
- friendly_name => undef,
- @_
- );
+ my %args = ( is_comment => 0,
+ @_ );
# From and Reply-To
# $args{is_comment} should be set if the comment address is to be used.
@@ -850,37 +418,29 @@ sub SetReturnAddress {
if ( $args{'is_comment'} ) {
$replyto = $self->TicketObj->QueueObj->CommentAddress
- || RT->Config->Get('CommentAddress');
- } else {
+ || $RT::CommentAddress;
+ }
+ else {
$replyto = $self->TicketObj->QueueObj->CorrespondAddress
- || RT->Config->Get('CorrespondAddress');
+ || $RT::CorrespondAddress;
}
unless ( $self->TemplateObj->MIMEObj->head->get('From') ) {
- if ( RT->Config->Get('UseFriendlyFromLine') ) {
- my $friendly_name = $args{friendly_name};
-
- unless ( $friendly_name ) {
- $friendly_name = $self->TransactionObj->CreatorObj->FriendlyName;
- if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
- $friendly_name = $1;
- }
- }
-
- $friendly_name =~ s/"/\\"/g;
- $self->SetHeader(
- 'From',
- sprintf(
- RT->Config->Get('FriendlyFromLineFormat'),
- $self->MIMEEncodeString(
- $friendly_name, RT->Config->Get('EmailOutputEncoding')
- ),
- $replyto
- ),
- );
- } else {
- $self->SetHeader( 'From', $replyto );
- }
+ if ($RT::UseFriendlyFromLine) {
+ my $friendly_name = $self->TransactionObj->CreatorObj->RealName;
+ if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
+ $friendly_name = $1;
+ }
+
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader( 'From',
+ sprintf($RT::FriendlyFromLineFormat,
+ $self->MIMEEncodeString( $friendly_name, $RT::EmailOutputEncoding ), $replyto),
+ );
+ }
+ else {
+ $self->SetHeader( 'From', $replyto );
+ }
}
unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) {
@@ -889,6 +449,10 @@ sub SetReturnAddress {
}
+# }}}
+
+# {{{ sub SetHeader
+
=head2 SetHeader FIELD, VALUE
Set the FIELD of the current MIME object into VALUE.
@@ -902,151 +466,163 @@ sub SetHeader {
chomp $val;
chomp $field;
- my $head = $self->TemplateObj->MIMEObj->head;
- $head->fold_length( $field, 10000 );
- $head->replace( $field, $val );
- return $head->get($field);
+ $self->TemplateObj->MIMEObj->head->fold_length( $field, 10000 );
+ $self->TemplateObj->MIMEObj->head->replace( $field, $val );
+ return $self->TemplateObj->MIMEObj->head->get($field);
}
-=head2 SetSubject
+# }}}
-This routine sets the subject. it does not add the rt tag. That gets done elsewhere
-If subject is already defined via template, it uses that. otherwise, it tries to get
-the transaction's subject.
+# {{{ sub SetRecipients
-=cut
+=head2 SetRecipients
-sub SetSubject {
+Dummy method to be overriden by subclasses which want to set the recipients.
+
+=cut
+
+sub SetRecipients {
my $self = shift;
- my $subject;
+ return ();
+}
- if ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
- return ();
- }
+# }}}
- my $message = $self->TransactionObj->Attachments;
- $message->RowsPerPage(1);
- if ( $self->{'Subject'} ) {
- $subject = $self->{'Subject'};
- } elsif ( my $first = $message->First ) {
- my $tmp = $first->GetHeader('Subject');
- $subject = defined $tmp ? $tmp : $self->TicketObj->Subject;
- } else {
- $subject = $self->TicketObj->Subject;
- }
- $subject = '' unless defined $subject;
- chomp $subject;
+# {{{ sub SetTo
- $subject =~ s/(\r\n|\n|\s)/ /g;
+=head2 SetTo
- $self->SetHeader( 'Subject', $subject );
+Takes a string that is the addresses you want to send mail to
+=cut
+
+sub SetTo {
+ my $self = shift;
+ my $addresses = shift;
+ return $self->SetHeader( 'To', $addresses );
}
-=head2 SetSubjectToken
+# }}}
-This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
+# {{{ sub SetCc
+
+=head2 SetCc
+
+Takes a string that is the addresses you want to Cc
=cut
-sub SetSubjectToken {
- my $self = shift;
+sub SetCc {
+ my $self = shift;
+ my $addresses = shift;
- my $head = $self->TemplateObj->MIMEObj->head;
- $head->replace(
- Subject => RT::Interface::Email::AddSubjectTag(
- Encode::decode_utf8( $head->get('Subject') ),
- $self->TicketObj,
- ),
- );
+ return $self->SetHeader( 'Cc', $addresses );
}
-=head2 SetReferencesHeaders
+# }}}
+
+# {{{ sub SetBcc
-Set References and In-Reply-To headers for this message.
+=head2 SetBcc
+
+Takes a string that is the addresses you want to Bcc
=cut
-sub SetReferencesHeaders {
+sub SetBcc {
+ my $self = shift;
+ my $addresses = shift;
+
+ return $self->SetHeader( 'Bcc', $addresses );
+}
+
+# }}}
+
+# {{{ sub SetPrecedence
+
+sub SetPrecedence {
my $self = shift;
- my ( @in_reply_to, @references, @msgid );
-
- if ( my $top = $self->TransactionObj->Message->First ) {
- @in_reply_to = split( /\s+/m, $top->GetHeader('In-Reply-To') || '' );
- @references = split( /\s+/m, $top->GetHeader('References') || '' );
- @msgid = split( /\s+/m, $top->GetHeader('Message-ID') || '' );
- } else {
- return (undef);
+
+ unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") ) {
+ $self->SetHeader( 'Precedence', "bulk" );
}
+}
- # There are two main cases -- this transaction was created with
- # the RT Web UI, and hence we want to *not* append its Message-ID
- # to the References and In-Reply-To. OR it came from an outside
- # source, and we should treat it as per the RFC
- my $org = RT->Config->Get('Organization');
- if ( "@msgid" =~ /<(rt-.*?-\d+-\d+)\.(\d+)-0-0\@\Q$org\E>/ ) {
-
- # Make all references which are internal be to version which we
- # have sent out
-
- for ( @references, @in_reply_to ) {
- s/<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$org\E>$/
- "<$1." . $self->TicketObj->id .
- "-" . $self->ScripObj->id .
- "-" . $self->ScripActionObj->{_Message_ID} .
- "@" . $org . ">"/eg
- }
+# }}}
- # In reply to whatever the internal message was in reply to
- $self->SetHeader( 'In-Reply-To', join( " ", (@in_reply_to) ) );
+# {{{ sub SetSubject
+
+=head2 SetSubject
- # Default the references to whatever we're in reply to
- @references = @in_reply_to unless @references;
+This routine sets the subject. it does not add the rt tag. that gets done elsewhere
+If $self->{'Subject'} is already defined, it uses that. otherwise, it tries to get
+the transaction's subject.
- # References are unchanged from internal
- } else {
+=cut
- # In reply to that message
- $self->SetHeader( 'In-Reply-To', join( " ", (@msgid) ) );
+sub SetSubject {
+ my $self = shift;
+ my $subject;
- # Default the references to whatever we're in reply to
- @references = @in_reply_to unless @references;
+ unless ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
+ my $message = $self->TransactionObj->Attachments;
+ my $ticket = $self->TicketObj->Id;
- # Push that message onto the end of the references
- push @references, @msgid;
- }
+ if ( $self->{'Subject'} ) {
+ $subject = $self->{'Subject'};
+ }
+ elsif ( ( $message->First() )
+ && ( $message->First->Headers ) ) {
+ my $header = $message->First->Headers();
+ $header =~ s/\n\s+/ /g;
+ if ( $header =~ /^Subject: (.*?)$/m ) {
+ $subject = $1;
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
- # Push pseudo-ref to the front
- my $pseudo_ref = $self->PseudoReference;
- @references = ( $pseudo_ref, grep { $_ ne $pseudo_ref } @references );
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
- # If there are more than 10 references headers, remove all but the
- # first four and the last six (Gotta keep this from growing
- # forever)
- splice( @references, 4, -6 ) if ( $#references >= 10 );
+ $subject =~ s/(\r\n|\n|\s)/ /gi;
- # Add on the references
- $self->SetHeader( 'References', join( " ", @references ) );
- $self->TemplateObj->MIMEObj->head->fold_length( 'References', 80 );
+ chomp $subject;
+ $self->SetHeader( 'Subject', $subject );
+ }
+ return ($subject);
}
-=head2 PseudoReference
+# }}}
-Returns a fake Message-ID: header for the ticket to allow a base level of threading
+# {{{ sub SetSubjectToken
-=cut
+=head2 SetSubjectToken
+
+This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
-sub PseudoReference {
+=cut
+sub SetSubjectToken {
my $self = shift;
- my $pseudo_ref
- = '<RT-Ticket-'
- . $self->TicketObj->id . '@'
- . RT->Config->Get('Organization') . '>';
- return $pseudo_ref;
+ my $tag = "[$RT::rtname #" . $self->TicketObj->id . "]";
+ my $sub = $self->TemplateObj->MIMEObj->head->get('Subject');
+ unless ( $sub =~ /\Q$tag\E/ ) {
+ $sub =~ s/(\r\n|\n|\s)/ /gi;
+ chomp $sub;
+ $self->TemplateObj->MIMEObj->head->replace( 'Subject', "$tag $sub" );
+ }
}
+# }}}
+
+# }}}
+
+# {{{
+
=head2 SetHeaderAsEncoding($field_name, $charset_encoding)
This routine converts the field into specified charset encoding.
@@ -1057,37 +633,53 @@ sub SetHeaderAsEncoding {
my $self = shift;
my ( $field, $enc ) = ( shift, shift );
- my $head = $self->TemplateObj->MIMEObj->head;
-
- if ( lc($field) eq 'from' and RT->Config->Get('SMTPFrom') ) {
- $head->replace( $field, RT->Config->Get('SMTPFrom') );
- return;
+ if ($field eq 'From' and $RT::SMTPFrom) {
+ $self->TemplateObj->MIMEObj->head->replace( $field, $RT::SMTPFrom );
+ return;
}
- my $value = $head->get( $field );
- $value = $self->MIMEEncodeString( $value, $enc );
- $head->replace( $field, $value );
+ my $value = $self->TemplateObj->MIMEObj->head->get($field);
-}
+ # don't bother if it's us-ascii
+
+ # See RT::I18N, 'NOTES: Why Encode::_utf8_off before Encode::from_to'
-=head2 MIMEEncodeString
+ $value = $self->MIMEEncodeString($value, $enc);
-Takes a perl string and optional encoding pass it over
-L<RT::Interface::Email/EncodeToMIME>.
+ $self->TemplateObj->MIMEObj->head->replace( $field, $value );
-Basicly encode a string using B encoding according to RFC2047.
+
+}
+# }}}
+
+# {{{ MIMENcodeString
+
+=head2 MIMEEncodeString STRING ENCODING
+
+Takes a string and a possible encoding and returns the string wrapped in MIME goo.
=cut
sub MIMEEncodeString {
- my $self = shift;
- return RT::Interface::Email::EncodeToMIME( String => $_[0], Charset => $_[1] );
+ my $self = shift;
+ my $value = shift;
+ my $enc = shift;
+
+ chomp $value;
+ return ($value) unless $value =~ /[^\x20-\x7e]/;
+
+ $value =~ s/\s*$//;
+ Encode::_utf8_off($value);
+ my $res = Encode::from_to( $value, "utf-8", $enc );
+ $value = encode_mimeword( $value, 'B', $enc );
}
+# }}}
+
eval "require RT::Action::SendEmail_Vendor";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm});
eval "require RT::Action::SendEmail_Local";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm});
1;
diff --git a/rt/lib/RT/Attachment.pm b/rt/lib/RT/Attachment.pm
index 4327238e6..2ed520162 100755
--- a/rt/lib/RT/Attachment.pm
+++ b/rt/lib/RT/Attachment.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -135,7 +110,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -144,14 +119,14 @@ Returns the current value of id.
=cut
-=head2 TransactionId
+=item TransactionId
Returns the current value of TransactionId.
(In the database, TransactionId is stored as int(11).)
-=head2 SetTransactionId VALUE
+=item SetTransactionId VALUE
Set TransactionId to VALUE.
@@ -162,14 +137,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Parent
+=item Parent
Returns the current value of Parent.
(In the database, Parent is stored as int(11).)
-=head2 SetParent VALUE
+=item SetParent VALUE
Set Parent to VALUE.
@@ -180,14 +155,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 MessageId
+=item MessageId
Returns the current value of MessageId.
(In the database, MessageId is stored as varchar(160).)
-=head2 SetMessageId VALUE
+=item SetMessageId VALUE
Set MessageId to VALUE.
@@ -198,14 +173,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Subject
+=item Subject
Returns the current value of Subject.
(In the database, Subject is stored as varchar(255).)
-=head2 SetSubject VALUE
+=item SetSubject VALUE
Set Subject to VALUE.
@@ -216,14 +191,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Filename
+=item Filename
Returns the current value of Filename.
(In the database, Filename is stored as varchar(255).)
-=head2 SetFilename VALUE
+=item SetFilename VALUE
Set Filename to VALUE.
@@ -234,14 +209,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ContentType
+=item ContentType
Returns the current value of ContentType.
(In the database, ContentType is stored as varchar(80).)
-=head2 SetContentType VALUE
+=item SetContentType VALUE
Set ContentType to VALUE.
@@ -252,14 +227,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ContentEncoding
+=item ContentEncoding
Returns the current value of ContentEncoding.
(In the database, ContentEncoding is stored as varchar(80).)
-=head2 SetContentEncoding VALUE
+=item SetContentEncoding VALUE
Set ContentEncoding to VALUE.
@@ -270,14 +245,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Content
+=item Content
Returns the current value of Content.
(In the database, Content is stored as longtext.)
-=head2 SetContent VALUE
+=item SetContent VALUE
Set Content to VALUE.
@@ -288,14 +263,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Headers
+=item Headers
Returns the current value of Headers.
(In the database, Headers is stored as longtext.)
-=head2 SetHeaders VALUE
+=item SetHeaders VALUE
Set Headers to VALUE.
@@ -306,7 +281,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -315,7 +290,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -325,33 +300,33 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
TransactionId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Parent =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
MessageId =>
- {read => 1, write => 1, sql_type => 12, length => 160, is_blob => 0, is_numeric => 0, type => 'varchar(160)', default => ''},
+ {read => 1, write => 1, type => 'varchar(160)', default => ''},
Subject =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Filename =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ContentType =>
- {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ {read => 1, write => 1, type => 'varchar(80)', default => ''},
ContentEncoding =>
- {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ {read => 1, write => 1, type => 'varchar(80)', default => ''},
Content =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ {read => 1, write => 1, type => 'longtext', default => ''},
Headers =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ {read => 1, write => 1, type => 'longtext', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -383,7 +358,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Attachments.pm b/rt/lib/RT/Attachments.pm
index 416cde6ba..177cdd094 100755
--- a/rt/lib/RT/Attachments.pm
+++ b/rt/lib/RT/Attachments.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Attachment item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Condition/AnyTransaction.pm b/rt/lib/RT/Condition/AnyTransaction.pm
index 1b90aa53e..4519fcf5a 100644
--- a/rt/lib/RT/Condition/AnyTransaction.pm
+++ b/rt/lib/RT/Condition/AnyTransaction.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,36 +14,21 @@
# 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.)
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
+# END LICENSE BLOCK
+
package RT::Condition::AnyTransaction;
-use base 'RT::Condition';
+require RT::Condition::Generic;
use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/Generic.pm b/rt/lib/RT/Condition/Generic.pm
index 08baeda25..bd269315e 100755
--- a/rt/lib/RT/Condition/Generic.pm
+++ b/rt/lib/RT/Condition/Generic.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,61 +14,198 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
=head1 NAME
- RT::Condition::Generic - deprecated, see RT::Condition
+ RT::Condition::Generic - ;
=head1 SYNOPSIS
- use RT::Condition::Generic;
+ use RT::Condition::Generic;
+ my $foo = new RT::Condition::IsApplicable(
+ TransactionObj => $tr,
+ TicketObj => $ti,
+ ScripObj => $scr,
+ Argument => $arg,
+ Type => $type);
+
+ if ($foo->IsApplicable) {
+ # do something
+ }
+
=head1 DESCRIPTION
-This module is provided only for backwards compatibility.
=head1 METHODS
+=begin testing
+
+ok (require RT::Condition::Generic);
+
+=end testing
+
+
=cut
-use strict;
-use warnings;
package RT::Condition::Generic;
-use base 'RT::Condition';
+
+use RT::Base;
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Base);
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ ScripObj => undef,
+ TemplateObj => undef,
+ Argument => undef,
+ ApplicableTransTypes => undef,
+ @_ );
+
+ $self->{'Argument'} = $args{'Argument'};
+ $self->{'ScripObj'} = $args{'ScripObj'};
+ $self->{'TicketObj'} = $args{'TicketObj'};
+ $self->{'TransactionObj'} = $args{'TransactionObj'};
+ $self->{'ApplicableTransTypes'} = $args{'ApplicableTransTypes'};
+}
+# }}}
+
+# Access Scripwide data
+
+# {{{ sub Argument
+
+=head2 Argument
+
+Return the optional argument associated with this ScripCondition
+
+=cut
+
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+# {{{ sub TicketObj
+
+=head2 TicketObj
+
+Return the ticket object we're talking about
+
+=cut
+
+sub TicketObj {
+ my $self = shift;
+ return($self->{'TicketObj'});
+}
+# }}}
+
+# {{{ sub ScripObj
+
+=head2 ScripObj
+
+Return the Scrip object we're talking about
+
+=cut
+
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+# {{{ sub TransactionObj
+
+=head2 TransactionObj
+
+Return the transaction object we're talking about
+
+=cut
+
+sub TransactionObj {
+ my $self = shift;
+ return($self->{'TransactionObj'});
+}
+# }}}
+
+# {{{ sub Type
+
+=head2 Type
+
+
+
+=cut
+
+sub ApplicableTransTypes {
+ my $self = shift;
+ return($self->{'ApplicableTransTypes'});
+}
+# }}}
+
+
+# Scrip methods
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("No description for [_1]", ref $self));
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+#If this rule applies to this transaction, return true.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ return(undef);
+}
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self = shift;
+
+ # We need to clean up all the references that might maybe get
+ # oddly circular
+ $self->{'TemplateObj'} =undef
+ $self->{'TicketObj'} = undef;
+ $self->{'TransactionObj'} = undef;
+ $self->{'ScripObj'} = undef;
+
+}
+
+# }}}
eval "require RT::Condition::Generic_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Generic_Vendor.pm});
-warn "RT::Condition::Generic has become RT::Condition. Please adjust your RT::Condition::Generic_Vendor file at " . $INC{"RT/Condition/Generic_Vendor.pm"} if !$@;
-
eval "require RT::Condition::Generic_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Generic_Local.pm});
-warn "RT::Condition::Generic has become RT::Condition. Please adjust your RT::Condition::Generic_Local file at " . $INC{"RT/Condition/Generic_Local.pm"} if !$@;
1;
-
diff --git a/rt/lib/RT/Condition/StatusChange.pm b/rt/lib/RT/Condition/StatusChange.pm
index 285b71da6..8afabcda0 100644
--- a/rt/lib/RT/Condition/StatusChange.pm
+++ b/rt/lib/RT/Condition/StatusChange.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,35 +14,22 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
+# END LICENSE BLOCK
+
+
package RT::Condition::StatusChange;
-use base 'RT::Condition';
+require RT::Condition::Generic;
+
use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
=head2 IsApplicable
diff --git a/rt/lib/RT/CurrentUser.pm b/rt/lib/RT/CurrentUser.pm
index b674d4e60..4ca2f9891 100755
--- a/rt/lib/RT/CurrentUser.pm
+++ b/rt/lib/RT/CurrentUser.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,165 +14,182 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
=head1 NAME
RT::CurrentUser - an RT object representing the current user
=head1 SYNOPSIS
- use RT::CurrentUser;
-
- # laod
- my $current_user = new RT::CurrentUser;
- $current_user->Load(...);
- # or
- my $current_user = RT::CurrentUser->new( $user_obj );
- # or
- my $current_user = RT::CurrentUser->new( $address || $name || $id );
-
- # manipulation
- $current_user->UserObj->SetName('new_name');
+ use RT::CurrentUser
=head1 DESCRIPTION
-B<Read-only> subclass of L<RT::User> class. Used to define the current
-user. You should pass an instance of this class to constructors of
-many RT classes, then the instance used to check ACLs and localize
-strings.
=head1 METHODS
-See also L<RT::User> for a list of methods this class has.
-=head2 new
+=begin testing
+
+ok (require RT::CurrentUser);
-Returns new CurrentUser object. Unlike all other classes of RT it takes
-either subclass of C<RT::User> class object or scalar value that is
-passed to Load method.
+=end testing
=cut
package RT::CurrentUser;
+use RT::Record;
use RT::I18N;
use strict;
-use warnings;
+use vars qw/@ISA/;
+@ISA= qw(RT::Record);
-use base qw/RT::User/;
+# {{{ sub _Init
#The basic idea here is that $self->CurrentUser is always supposed
# to be a CurrentUser object. but that's hard to do when we're trying to load
# the CurrentUser object
-sub _Init {
- my $self = shift;
- my $User = shift;
-
- $self->{'table'} = "Users";
-
- if ( defined $User ) {
-
- if ( UNIVERSAL::isa( $User, 'RT::User' ) ) {
- $self->LoadById( $User->id );
- }
- elsif ( ref $User ) {
- $RT::Logger->crit(
- "RT::CurrentUser->new() called with a bogus argument: $User");
- }
- else {
- $self->Load( $User );
- }
- }
+sub _Init {
+ my $self = shift;
+ my $Name = shift;
- $self->_BuildTableAttributes;
+ $self->{'table'} = "Users";
-}
+ if (defined($Name)) {
+ $self->Load($Name);
+ }
+
+ $self->CurrentUser($self);
-=head2 Create, Delete and Set*
+}
+# }}}
-As stated above it's a subclass of L<RT::User>, but this class is read-only
-and calls to these methods are illegal. Return 'permission denied' message
-and log an error.
-
-=cut
+# {{{ sub Create
sub Create {
my $self = shift;
- $RT::Logger->error('RT::CurrentUser is read-only, RT::User for manipulation');
return (0, $self->loc('Permission Denied'));
}
+# }}}
+
+# {{{ sub Delete
+
sub Delete {
my $self = shift;
- $RT::Logger->error('RT::CurrentUser is read-only, RT::User for manipulation');
return (0, $self->loc('Permission Denied'));
}
-sub _Set {
- my $self = shift;
- $RT::Logger->error('RT::CurrentUser is read-only, RT::User for manipulation');
- return (0, $self->loc('Permission Denied'));
-}
+# }}}
+
+# {{{ sub UserObj
=head2 UserObj
-Returns the L<RT::User> object associated with this CurrentUser object.
+ Returns the RT::User object associated with this CurrentUser object.
=cut
sub UserObj {
my $self = shift;
-
- my $user = RT::User->new( $self );
- unless ( $user->LoadById( $self->Id ) ) {
- $RT::Logger->error(
- $self->loc("Couldn't load [_1] from the users database.\n", $self->Id)
- );
+
+ unless ($self->{'UserObj'}) {
+ use RT::User;
+ $self->{'UserObj'} = RT::User->new($self);
+ unless ($self->{'UserObj'}->Load($self->Id)) {
+ $RT::Logger->err($self->loc("Couldn't load [_1] from the users database.\n", $self->Id));
+ }
+
}
- return $user;
+ return ($self->{'UserObj'});
}
+# }}}
-sub _CoreAccessible {
- {
- Name => { 'read' => 1 },
- Gecos => { 'read' => 1 },
- RealName => { 'read' => 1 },
- Lang => { 'read' => 1 },
- Password => { 'read' => 0, 'write' => 0 },
- EmailAddress => { 'read' => 1, 'write' => 0 }
- };
-
+# {{{ sub PrincipalObj
+
+=head2 PrincipalObj
+
+ Returns this user's principal object. this is just a helper routine for
+ $self->UserObj->PrincipalObj
+
+=cut
+
+sub PrincipalObj {
+ my $self = shift;
+ return($self->UserObj->PrincipalObj);
}
+
+# }}}
+
+
+# {{{ sub PrincipalId
+
+=head2 PrincipalId
+
+ Returns this user's principal Id. this is just a helper routine for
+ $self->UserObj->PrincipalId
+
+=cut
+
+sub PrincipalId {
+ my $self = shift;
+ return($self->UserObj->PrincipalId);
+}
+
+
+# }}}
+
+
+# {{{ sub _Accessible
+sub _Accessible {
+ my $self = shift;
+ my %Cols = (
+ Name => 'read',
+ Gecos => 'read',
+ RealName => 'read',
+ Password => 'neither',
+ EmailAddress => 'read',
+ Privileged => 'read',
+ IsAdministrator => 'read'
+ );
+ return($self->SUPER::_Accessible(@_, %Cols));
+}
+# }}}
+
+# {{{ sub LoadByEmail
+
+=head2 LoadByEmail
+
+Loads a User into this CurrentUser object.
+Takes the email address of the user to load.
+
+=cut
+
+sub LoadByEmail {
+ my $self = shift;
+ my $identifier = shift;
+
+ $identifier = RT::User::CanonicalizeEmailAddress(undef, $identifier);
+
+ $self->LoadByCol("EmailAddress",$identifier);
+
+}
+# }}}
+
+# {{{ sub LoadByGecos
+
=head2 LoadByGecos
Loads a User into this CurrentUser object.
@@ -188,128 +199,171 @@ Takes a unix username as its only argument.
sub LoadByGecos {
my $self = shift;
- return $self->LoadByCol( "Gecos", shift );
+ my $identifier = shift;
+
+ $self->LoadByCol("Gecos",$identifier);
+
}
+# }}}
+
+# {{{ sub LoadByName
=head2 LoadByName
Loads a User into this CurrentUser object.
Takes a Name.
-
=cut
sub LoadByName {
my $self = shift;
- return $self->LoadByCol( "Name", shift );
+ my $identifier = shift;
+ $self->LoadByCol("Name",$identifier);
+
}
+# }}}
-=head2 LanguageHandle
-
-Returns this current user's langauge handle. Should take a language
-specification. but currently doesn't
+# {{{ sub Load
-=cut
+=head2 Load
-sub LanguageHandle {
- my $self = shift;
- if ( !defined $self->{'LangHandle'}
- || !UNIVERSAL::can( $self->{'LangHandle'}, 'maketext' )
- || @_ )
- {
- if ( my $lang = $self->Lang ) {
- push @_, $lang;
- }
- elsif ( $self->id && ($self->id == ($RT::SystemUser->id||0) || $self->id == ($RT::Nobody->id||0)) ) {
- # don't use ENV magic for system users
- push @_, 'en';
- }
+Loads a User into this CurrentUser object.
+Takes either an integer (users id column reference) or a Name
+The latter is deprecated. Instead, you should use LoadByName.
+Formerly, this routine also took email addresses.
- $self->{'LangHandle'} = RT::I18N->get_handle(@_);
- }
+=cut
- # Fall back to english.
- unless ( $self->{'LangHandle'} ) {
- die "We couldn't get a dictionary. Ne mogu naidti slovar. No puedo encontrar dictionario.";
- }
- return $self->{'LangHandle'};
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+
+ #if it's an int, load by id. otherwise, load by name.
+ if ($identifier !~ /\D/) {
+ $self->SUPER::LoadById($identifier);
+ }
+ else {
+ # This is a bit dangerous, we might get false authen if somebody
+ # uses ambigous userids or real names:
+ $self->LoadByCol("Name",$identifier);
+ }
}
-sub loc {
- my $self = shift;
- return '' if !defined $_[0] || $_[0] eq '';
+# }}}
- my $handle = $self->LanguageHandle;
+# {{{ sub IsPassword
- if (@_ == 1) {
- # pre-scan the lexicon hashes to return _AUTO keys verbatim,
- # to keep locstrings containing '[' and '~' from tripping over Maketext
- return $_[0] unless grep exists $_->{$_[0]}, @{ $handle->_lex_refs };
- }
+=head2 IsPassword
- return $handle->maketext(@_);
-}
+Takes a password as a string. Passes it off to IsPassword in this
+user's UserObj. If it is the user's password and the user isn't
+disabled, returns 1.
-sub loc_fuzzy {
- my $self = shift;
- return '' if !defined $_[0] || $_[0] eq '';
+Otherwise, returns undef.
- # XXX: work around perl's deficiency when matching utf8 data
- return $_[0] if Encode::is_utf8($_[0]);
+=cut
- return $self->LanguageHandle->maketext_fuzzy( @_ );
+sub IsPassword {
+ my $self = shift;
+ my $value = shift;
+
+ return ($self->UserObj->IsPassword($value));
}
-=head2 CurrentUser
+# }}}
-Return the current currentuser object
+# {{{ sub Privileged
+
+=head2 Privileged
+
+Returns true if the current user can be granted rights and be
+a member of groups.
=cut
-sub CurrentUser {
+sub Privileged {
my $self = shift;
- return($self);
-
+ return ($self->UserObj->Privileged());
}
-=head2 Authenticate
+# }}}
-Takes $password, $created and $nonce, and returns a boolean value
-representing whether the authentication succeeded.
-If both $nonce and $created are specified, validate $password against:
+# {{{ sub HasRight
- encode_base64(sha1(
- $nonce .
- $created .
- sha1_hex( "$username:$realm:$server_pass" )
- ))
+=head2 HasRight
-where $server_pass is the md5_hex(password) digest stored in the
-database, $created is in ISO time format, and $nonce is a random
-string no longer than 32 bytes.
+calls $self->UserObj->HasRight with the arguments passed in
=cut
-sub Authenticate {
- my ($self, $password, $created, $nonce, $realm) = @_;
+sub HasRight {
+ my $self = shift;
+ return ($self->UserObj->HasRight(@_));
+}
+
+# }}}
+
+# {{{ Localization
+
+=head2 LanguageHandle
+
+Returns this current user's langauge handle. Should take a language
+specification. but currently doesn't
- require Digest::MD5;
- require Digest::SHA1;
- require MIME::Base64;
+=begin testing
+
+ok (my $cu = RT::CurrentUser->new('root'));
+ok (my $lh = $cu->LanguageHandle);
+ok ($lh != undef);
+ok ($lh->isa('Locale::Maketext'));
+ok ($cu->loc('TEST_STRING') eq "Concrete Mixer", "Localized TEST_STRING into English");
+ok ($lh = $cu->LanguageHandle('fr'));
+ok ($cu->loc('Before') eq "Avant", "Localized TEST_STRING into Frenc");
+
+=end testing
+
+=cut
- my $username = $self->UserObj->Name or return;
- my $server_pass = $self->UserObj->__Value('Password') or return;
- my $auth_digest = MIME::Base64::encode_base64(Digest::SHA1::sha1(
- $nonce .
- $created .
- Digest::MD5::md5_hex("$username:$realm:$server_pass")
- ));
+sub LanguageHandle {
+ my $self = shift;
+ if ((!defined $self->{'LangHandle'}) ||
+ (!UNIVERSAL::can($self->{'LangHandle'}, 'maketext')) ||
+ (@_)) {
+ $self->{'LangHandle'} = RT::I18N->get_handle(@_);
+ }
+ # Fall back to english.
+ unless ($self->{'LangHandle'}) {
+ die "We couldn't get a dictionary. Nye mogu naidti slovar. No puedo encontrar dictionario.";
+ }
+ return ($self->{'LangHandle'});
+}
- chomp($password);
- chomp($auth_digest);
+sub loc {
+ my $self = shift;
+ return '' if $_[0] eq '';
+
+ my $handle = $self->LanguageHandle;
+
+ if (@_ == 1) {
+ # pre-scan the lexicon hashes to return _AUTO keys verbatim,
+ # to keep locstrings containing '[' and '~' from tripping over Maketext
+ return $_[0] unless grep { exists $_->{$_[0]} } @{ $handle->_lex_refs };
+ }
+
+ return $handle->maketext(@_);
+}
+
+sub loc_fuzzy {
+ my $self = shift;
+ return '' if $_[0] eq '';
+
+ # XXX: work around perl's deficiency when matching utf8 data
+ return $_[0] if Encode::is_utf8($_[0]);
+ my $result = $self->LanguageHandle->maketext_fuzzy(@_);
- return ($password eq $auth_digest);
+ return($result);
}
+# }}}
eval "require RT::CurrentUser_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Vendor.pm});
@@ -317,3 +371,4 @@ eval "require RT::CurrentUser_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Local.pm});
1;
+
diff --git a/rt/lib/RT/Date.pm b/rt/lib/RT/Date.pm
index fc4c43ce4..355370ada 100644
--- a/rt/lib/RT/Date.pm
+++ b/rt/lib/RT/Date.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
=head1 NAME
RT::Date - a simple Object Oriented date.
@@ -60,6 +35,11 @@ RT Date is a simple Date Object designed to be speedy and easy for RT to use
The fact that it assumes that a time of 0 means "never" is probably a bug.
+=begin testing
+
+ok (require RT::Date);
+
+=end testing
=head1 METHODS
@@ -69,11 +49,12 @@ The fact that it assumes that a time of 0 means "never" is probably a bug.
package RT::Date;
use Time::Local;
-use POSIX qw(tzset);
+
+use RT::Base;
use strict;
-use warnings;
-use base qw/RT::Base/;
+use vars qw/@ISA/;
+@ISA = qw/RT::Base/;
use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
@@ -81,68 +62,30 @@ $MINUTE = 60;
$HOUR = 60 * $MINUTE;
$DAY = 24 * $HOUR;
$WEEK = 7 * $DAY;
-$MONTH = 30.4375 * $DAY;
-$YEAR = 365.25 * $DAY;
-
-our @MONTHS = (
- 'Jan', # loc
- 'Feb', # loc
- 'Mar', # loc
- 'Apr', # loc
- 'May', # loc
- 'Jun', # loc
- 'Jul', # loc
- 'Aug', # loc
- 'Sep', # loc
- 'Oct', # loc
- 'Nov', # loc
- 'Dec', # loc
-);
-
-our @DAYS_OF_WEEK = (
- 'Sun', # loc
- 'Mon', # loc
- 'Tue', # loc
- 'Wed', # loc
- 'Thu', # loc
- 'Fri', # loc
- 'Sat', # loc
-);
-
-our @FORMATTERS = (
- 'DefaultFormat', # loc
- 'ISO', # loc
- 'W3CDTF', # loc
- 'RFC2822', # loc
- 'RFC2616', # loc
- 'iCal', # loc
-);
-if ( eval 'use DateTime qw(); 1;' && eval 'use DateTime::Locale qw(); 1;' &&
- DateTime->can('format_cldr') && DateTime::Locale::root->can('date_format_full') ) {
- push @FORMATTERS, 'LocalizedDateTime'; # loc
+$MONTH = 4 * $WEEK;
+$YEAR = 365 * $DAY;
+
+# {{{ sub new
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->CurrentUser(@_);
+ $self->Unix(0);
+ return $self;
}
-=head2 new
-
-Object constructor takes one argument C<RT::CurrentUser> object.
-
-=cut
+# }}}
-sub new {
- my $proto = shift;
- my $class = ref($proto) || $proto;
- my $self = {};
- bless ($self, $class);
- $self->CurrentUser(@_);
- $self->Unix(0);
- return $self;
-}
+# {{{ sub Set
-=head2 Set
+=head2 sub Set
-Takes a param hash with the fields C<Format>, C<Value> and C<Timezone>.
+takes a param hash with the fields 'Format' and 'Value'
-If $args->{'Format'} is 'unix', takes the number of seconds since the epoch.
+if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
If $args->{'Format'} is ISO, tries to parse an ISO date.
@@ -151,218 +94,243 @@ things out. This is a heavyweight operation that should never be called from
within RT's core. But it's really useful for something like the textbox date
entry where we let the user do whatever they want.
-If $args->{'Value'} is 0, assumes you mean never.
+If $args->{'Value'} is 0, assumes you mean never.
+
+=begin testing
+
+use_ok(RT::Date);
+my $date = RT::Date->new($RT::SystemUser);
+$date->Set(Format => 'unix', Value => '0');
+ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
+
+=end testing
=cut
sub Set {
my $self = shift;
- my %args = (
- Format => 'unix',
- Value => time,
- Timezone => 'user',
- @_
- );
-
- return $self->Unix(0) unless $args{'Value'};
+ my %args = ( Format => 'unix',
+ Value => time,
+ @_ );
+ if ( !$args{'Value'}
+ || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
+ $self->Unix(-1);
+ return ( $self->Unix() );
+ }
if ( $args{'Format'} =~ /^unix$/i ) {
- return $self->Unix( $args{'Value'} );
+ $self->Unix( $args{'Value'} );
}
- elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
- $args{'Value'} =~ s!/!-!g;
- if ( ( $args{'Value'} =~ /^(\d{4})?(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
- || ( $args{'Value'} =~ /^(\d{4})?(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
- || ( $args{'Value'} =~ /^(?:(\d{4})-)?(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
- || ( $args{'Value'} =~ /^(?:(\d{4})-)?(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
+ elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
+ $args{'Value'} =~ s!/!-!g;
+
+ if (( $args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
+ || ( $args{'Value'} =~
+ /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
+ || ( $args{'Value'} =~
+ /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
+ || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
) {
- my ($year, $mon, $mday, $hours, $min, $sec) = ($1, $2, $3, $4, $5, $6);
-
- # use current year if string has no value
- $year ||= (localtime time)[5] + 1900;
+ my $year = $1;
+ my $mon = $2;
+ my $mday = $3;
+ my $hours = $4;
+ my $min = $5;
+ my $sec = $6;
#timegm expects month as 0->11
$mon--;
- #now that we've parsed it, deal with the case where everything was 0
- return $self->Unix(0) if $mon < 0 || $mon > 11;
-
- my $tz = lc $args{'Format'} eq 'datemanip'? 'user': 'utc';
- $self->Unix( $self->Timelocal( $tz, $sec, $min, $hours, $mday, $mon, $year ) );
-
- $self->Unix(0) unless $self->Unix > 0;
+ #now that we've parsed it, deal with the case where everything
+ #was 0
+ if ( $mon == -1 ) {
+ $self->Unix(-1);
+ }
+ else {
+
+ #Dateamnip strings aren't in GMT.
+ if ( $args{'Format'} =~ /^datemanip$/i ) {
+ $self->Unix(
+ timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
+ }
+
+ #ISO and SQL dates are in GMT
+ else {
+ $self->Unix(
+ timegm( $sec, $min, $hours, $mday, $mon, $year ) );
+ }
+
+ $self->Unix(-1) unless $self->Unix;
+ }
}
else {
- $RT::Logger->warning(
- "Couldn't parse date '$args{'Value'}' as a $args{'Format'} format"
- );
- return $self->Unix(0);
+ use Carp;
+ Carp::cluck;
+ $RT::Logger->debug(
+ "Couldn't parse date $args{'Value'} as a $args{'Format'}");
+
}
}
elsif ( $args{'Format'} =~ /^unknown$/i ) {
require Time::ParseDate;
- # the module supports only legacy timezones like PDT or EST...
- # so we parse date as GMT and later apply offset, this only
- # should be applied to absolute times, so compensate shift in NOW
- my $now = time;
- $now += ($self->Localtime( $args{Timezone}, $now ))[9];
- my $date = Time::ParseDate::parsedate(
- $args{'Value'},
- GMT => 1,
- NOW => $now,
- UK => RT->Config->Get('DateDayBeforeMonth'),
- PREFER_PAST => RT->Config->Get('AmbiguousDayInPast'),
- PREFER_FUTURE => RT->Config->Get('AmbiguousDayInFuture'),
- );
- # apply timezone offset
- $date -= ($self->Localtime( $args{Timezone}, $date ))[9];
-
- $RT::Logger->debug(
- "RT::Date used Time::ParseDate to make '$args{'Value'}' $date\n"
- );
-
- return $self->Set( Format => 'unix', Value => $date);
- }
- else {
- $RT::Logger->error(
- "Unknown Date format: $args{'Format'}\n"
- );
- return $self->Unix(0);
- }
-
- return $self->Unix;
-}
-
-=head2 SetToNow
-Set the object's time to the current time. Takes no arguments
-and returns unix time.
+ #Convert it to an ISO format string
-=cut
+ my $date = Time::ParseDate::parsedate($args{'Value'},
+ UK => $RT::DateDayBeforeMonth,
+ PREFER_PAST => $RT::AmbiguousDayInPast,
+ PREFER_FUTURE => !($RT::AmbiguousDayInPast));
-sub SetToNow {
- return $_[0]->Unix(time);
-}
+ #This date has now been set to a date in the _local_ timezone.
+ #since ISO dates are known to be in GMT (for RT's purposes);
-=head2 SetToMidnight [Timezone => 'utc']
+ $RT::Logger->debug( "RT::Date used date::parse to make "
+ . $args{'Value'}
+ . " $date\n" );
-Sets the date to midnight (at the beginning of the day).
-Returns the unixtime at midnight.
+ return ( $self->Set( Format => 'unix', Value => "$date" ) );
+ }
+ else {
+ die "Unknown Date format: " . $args{'Format'} . "\n";
+ }
-Arguments:
+ return ( $self->Unix() );
+}
-=over 4
+# }}}
-=item Timezone
+# {{{ sub SetToMidnight
-Timezone context C<user>, C<server> or C<UTC>. See also L</Timezone>.
+=head2 SetToMidnight
-=back
+Sets the date to midnight (at the beginning of the day) GMT
+Returns the unixtime at midnight.
=cut
sub SetToMidnight {
my $self = shift;
- my %args = ( Timezone => '', @_ );
- my $new = $self->Timelocal(
- $args{'Timezone'},
- 0,0,0,($self->Localtime( $args{'Timezone'} ))[3..9]
- );
- return $self->Unix( $new );
+
+ use Time::Local;
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($self->Unix);
+ $self->Unix(timegm (0,0,0,$mday,$mon,$year,$wday,$yday));
+
+ return ($self->Unix);
+
+
}
+
+# }}}
+
+# {{{ sub SetToNow
+sub SetToNow {
+ my $self = shift;
+ return($self->Set(Format => 'unix', Value => time))
+}
+# }}}
+
+# {{{ sub Diff
+
=head2 Diff
-Takes either an C<RT::Date> object or the date in unixtime format as a string,
-if nothing is specified uses the current time.
+Takes either an RT::Date object or the date in unixtime format as a string
-Returns the differnce between the time in the current object and that time
-as a number of seconds. Returns C<undef> if any of two compared values is
-incorrect or not set.
+Returns the differnce between $self and that time as a number of seconds
=cut
sub Diff {
my $self = shift;
my $other = shift;
- $other = time unless defined $other;
- if ( UNIVERSAL::isa( $other, 'RT::Date' ) ) {
- $other = $other->Unix;
- }
- return undef unless $other=~ /^\d+$/ && $other > 0;
-
- my $unix = $self->Unix;
- return undef unless $unix > 0;
- return $unix - $other;
+ if (ref($other) eq 'RT::Date') {
+ $other=$other->Unix;
+ }
+ return ($self->Unix - $other);
}
+# }}}
+
+# {{{ sub DiffAsString
-=head2 DiffAsString
+=head2 sub DiffAsString
-Takes either an C<RT::Date> object or the date in unixtime format as a string,
-if nothing is specified uses the current time.
+Takes either an RT::Date object or the date in unixtime format as a string
-Returns the differnce between C<$self> and that time as a number of seconds as
-a localized string fit for human consumption. Returns empty string if any of
-two compared values is incorrect or not set.
+Returns the differnce between $self and that time as a number of seconds as
+as string fit for human consumption
=cut
sub DiffAsString {
my $self = shift;
- my $diff = $self->Diff( @_ );
- return '' unless defined $diff;
+ my $other = shift;
+
- return $self->DurationAsString( $diff );
+ if ($other < 1) {
+ return ("");
+ }
+ if ($self->Unix < 1) {
+ return("");
+ }
+ my $diff = $self->Diff($other);
+
+ return ($self->DurationAsString($diff));
}
+# }}}
+
+# {{{ sub DurationAsString
+
=head2 DurationAsString
-Takes a number of seconds. Returns a localized string describing
-that duration.
+Takes a number of seconds. returns a string describing that duration
=cut
sub DurationAsString {
+
my $self = shift;
- my $duration = int shift;
+ my $duration = shift;
+
+ my ( $negative, $s );
- my ( $negative, $s, $time_unit );
- $negative = 1 if $duration < 0;
- $duration = abs $duration;
+ $negative = 1 if ( $duration < 0 );
+ $duration = abs($duration);
+
+ my $time_unit;
if ( $duration < $MINUTE ) {
$s = $duration;
$time_unit = $self->loc("sec");
}
elsif ( $duration < ( 2 * $HOUR ) ) {
- $s = int( $duration / $MINUTE + 0.5 );
+ $s = int( $duration / $MINUTE );
$time_unit = $self->loc("min");
}
elsif ( $duration < ( 2 * $DAY ) ) {
- $s = int( $duration / $HOUR + 0.5 );
+ $s = int( $duration / $HOUR );
$time_unit = $self->loc("hours");
}
elsif ( $duration < ( 2 * $WEEK ) ) {
- $s = int( $duration / $DAY + 0.5 );
+ $s = int( $duration / $DAY );
$time_unit = $self->loc("days");
}
elsif ( $duration < ( 2 * $MONTH ) ) {
- $s = int( $duration / $WEEK + 0.5 );
+ $s = int( $duration / $WEEK );
$time_unit = $self->loc("weeks");
}
elsif ( $duration < $YEAR ) {
- $s = int( $duration / $MONTH + 0.5 );
+ $s = int( $duration / $MONTH );
$time_unit = $self->loc("months");
}
else {
- $s = int( $duration / $YEAR + 0.5 );
+ $s = int( $duration / $YEAR );
$time_unit = $self->loc("years");
}
-
- if ( $negative ) {
+ if (0) { # For now, never display the "AGO" # $negative) {
return $self->loc( "[_1] [_2] ago", $s, $time_unit );
}
else {
@@ -370,46 +338,46 @@ sub DurationAsString {
}
}
-=head2 AgeAsString
+# }}}
-Takes nothing. Returns a string that's the differnce between the
-time in the object and now.
+# {{{ sub AgeAsString
-=cut
+=head2 sub AgeAsString
+
+Takes nothing
-sub AgeAsString { return $_[0]->DiffAsString }
+Returns a string that's the differnce between the time in the object and now
+=cut
+sub AgeAsString {
+ my $self = shift;
+ return ($self->DiffAsString(time));
+ }
+# }}}
-=head2 AsString
+# {{{ sub AsString
-Returns the object's time as a localized string with curent user's prefered
-format and timezone.
+=head2 sub AsString
-If the current user didn't choose prefered format then system wide setting is
-used or L</DefaultFormat> if the latter is not specified. See config option
-C<DateTimeFormat>.
+Returns the object\'s time as a string with the current timezone.
=cut
sub AsString {
my $self = shift;
- my %args = (@_);
-
- return $self->loc("Not set") unless $self->Unix > 0;
+ return ($self->loc("Not set")) if ($self->Unix <= 0);
- my $format = RT->Config->Get( 'DateTimeFormat', $self->CurrentUser ) || 'DefaultFormat';
- $format = { Format => $format } unless ref $format;
- %args = (%$format, %args);
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
- return $self->Get( Timezone => 'user', %args );
+ return $self->loc("[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]", $self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900));
}
+# }}}
+# {{{ GetWeekday
=head2 GetWeekday DAY
-Takes an integer day of week and returns a localized string for
-that day of week. Valid values are from range 0-6, Note that B<0
-is sunday>.
+Takes an integer day of week and returns a localized string for that day of week
=cut
@@ -417,628 +385,169 @@ sub GetWeekday {
my $self = shift;
my $dow = shift;
- return $self->loc($DAYS_OF_WEEK[$dow])
- if $DAYS_OF_WEEK[$dow];
- return '';
+ return $self->loc('Mon.') if ($dow == 1);
+ return $self->loc('Tue.') if ($dow == 2);
+ return $self->loc('Wed.') if ($dow == 3);
+ return $self->loc('Thu.') if ($dow == 4);
+ return $self->loc('Fri.') if ($dow == 5);
+ return $self->loc('Sat.') if ($dow == 6);
+ return $self->loc('Sun.') if ($dow == 0);
}
-=head2 GetMonth MONTH
+# }}}
-Takes an integer month and returns a localized string for that month.
-Valid values are from from range 0-11.
+# {{{ GetMonth
+=head2 GetMonth DAY
+
+Takes an integer month and returns a localized string for that month
=cut
sub GetMonth {
my $self = shift;
- my $mon = shift;
-
- return $self->loc($MONTHS[$mon])
- if $MONTHS[$mon];
- return '';
+ my $mon = shift;
+
+ # We do this rather than an array so that we don't call localize 12x what we need to
+ return $self->loc('Jan.') if ($mon == 0);
+ return $self->loc('Feb.') if ($mon == 1);
+ return $self->loc('Mar.') if ($mon == 2);
+ return $self->loc('Apr.') if ($mon == 3);
+ return $self->loc('May.') if ($mon == 4);
+ return $self->loc('Jun.') if ($mon == 5);
+ return $self->loc('Jul.') if ($mon == 6);
+ return $self->loc('Aug.') if ($mon == 7);
+ return $self->loc('Sep.') if ($mon == 8);
+ return $self->loc('Oct.') if ($mon == 9);
+ return $self->loc('Nov.') if ($mon == 10);
+ return $self->loc('Dec.') if ($mon == 11);
}
-=head2 AddSeconds SECONDS
+# }}}
-Takes a number of seconds and returns the new unix time.
+# {{{ sub AddSeconds
-Negative value can be used to substract seconds.
+=head2 sub AddSeconds
+
+Takes a number of seconds as a string
+
+Returns the new time
=cut
sub AddSeconds {
my $self = shift;
- my $delta = shift or return $self->Unix;
+ my $delta = shift;
$self->Set(Format => 'unix', Value => ($self->Unix + $delta));
-
+
return ($self->Unix);
-}
-
-=head2 AddDays [DAYS]
-
-Adds C<24 hours * DAYS> to the current time. Adds one day when
-no argument is specified. Negative value can be used to substract
-days.
-
-Returns new unix time.
-
-=cut
-
-sub AddDays {
- my $self = shift;
- my $days = shift || 1;
- return $self->AddSeconds( $days * $DAY );
-}
-
-=head2 AddDay
-
-Adds 24 hours to the current time. Returns new unix time.
-
-=cut
-
-sub AddDay { return $_[0]->AddSeconds($DAY) }
-
-=head2 Unix [unixtime]
-
-Optionally takes a date in unix seconds since the epoch format.
-Returns the number of seconds since the epoch
-
-=cut
-
-sub Unix {
- my $self = shift;
- $self->{'time'} = int(shift || 0) if @_;
- return $self->{'time'};
-}
-
-=head2 DateTime
-
-Alias for L</Get> method. Arguments C<Date> and <Time>
-are fixed to true values, other arguments could be used
-as described in L</Get>.
-
-=cut
-
-sub DateTime {
- my $self = shift;
- unless (defined $self) {
- use Carp; Carp::confess("undefined $self");
- }
- return $self->Get( @_, Date => 1, Time => 1 );
-}
-
-=head2 Date
-
-Takes Format argument which allows you choose date formatter.
-Pass throught other arguments to the formatter method.
-
-Returns the object's formatted date. Default formatter is ISO.
-
-=cut
-
-sub Date {
- my $self = shift;
- return $self->Get( @_, Date => 1, Time => 0 );
-}
-
-=head2 Time
-
-
-=cut
-
-sub Time {
- my $self = shift;
- return $self->Get( @_, Date => 0, Time => 1 );
-}
-
-=head2 Get
-
-Returnsa a formatted and localized string that represets time of
-the current object.
-
-
-=cut
+
-sub Get
-{
- my $self = shift;
- my %args = (Format => 'ISO', @_);
- my $formatter = $args{'Format'};
- $formatter = 'ISO' unless $self->can($formatter);
- return $self->$formatter( %args );
}
-=head2 Output formatters
-
-Fomatter is a method that returns date and time in different configurable
-format.
-
-Each method takes several arguments:
-
-=over 1
-
-=item Date
-
-=item Time
-
-=item Timezone - Timezone context C<server>, C<user> or C<UTC>
-
-=back
+# }}}
-Formatters may also add own arguments to the list, for example
-in RFC2822 format day of time in output is optional so it
-understand boolean argument C<DayOfTime>.
+# {{{ sub AddDays
-=head3 Formatters
+=head2 AddDays $DAYS
-Returns an array of available formatters.
+Adds 24 hours * $DAYS to the current time
=cut
-sub Formatters
-{
- my $self = shift;
-
- return @FORMATTERS;
-}
-
-=head3 DefaultFormat
-
-=cut
-
-sub DefaultFormat
-{
+sub AddDays {
my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- Seconds => 1,
- @_,
- );
+ my $days = shift;
+ $self->AddSeconds($days * $DAY);
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
- $wday = $self->GetWeekday($wday);
- $mon = $self->GetMonth($mon);
- ($mday, $hour, $min, $sec) = map { sprintf "%02d", $_ } ($mday, $hour, $min, $sec);
-
- if( $args{'Date'} && !$args{'Time'} ) {
- return $self->loc('[_1] [_2] [_3] [_4]',
- $wday,$mon,$mday,$year);
- } elsif( !$args{'Date'} && $args{'Time'} ) {
- if( $args{'Seconds'} ) {
- return $self->loc('[_1]:[_2]:[_3]',
- $hour,$min,$sec);
- } else {
- return $self->loc('[_1]:[_2]',
- $hour,$min);
- }
- } else {
- if( $args{'Seconds'} ) {
- return $self->loc('[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]',
- $wday,$mon,$mday,$hour,$min,$sec,$year);
- } else {
- return $self->loc('[_1] [_2] [_3] [_4]:[_5] [_6]',
- $wday,$mon,$mday,$hour,$min,$year);
- }
- }
}
-=head3 LocalizedDateTime
+# }}}
-Returns date and time as string, with user localization.
+# {{{ sub AddDay
-Supports arguments: C<DateFormat> and C<TimeFormat> which may contains date and
-time format as specified in DateTime::Locale (default to full_date_format and
-medium_time_format), C<AbbrDay> and C<AbbrMonth> which may be set to 0 if
-you want full Day/Month names instead of abbreviated ones.
+=head2 AddDay
-Require optionnal DateTime::Locale module.
+Adds 24 hours to the current time
=cut
-sub LocalizedDateTime
-{
+sub AddDay {
my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- DateFormat => 'date_format_full',
- TimeFormat => 'time_format_medium',
- AbbrDay => 1,
- AbbrMonth => 1,
- @_,
- );
-
- return $self->loc("DateTime module missing") unless ( eval 'use DateTime qw(); 1;' );
- return $self->loc("DateTime::Locale module missing") unless ( eval 'use DateTime::Locale qw(); 1;' );
- return $self->loc("DateTime doesn't support format_cldr, you must upgrade to use this feature")
- unless can DateTime::('format_cldr');
-
-
- my $date_format = $args{'DateFormat'};
- my $time_format = $args{'TimeFormat'};
-
- my $lang = $self->CurrentUser->UserObj->Lang;
- unless ($lang) {
- require I18N::LangTags::Detect;
- $lang = ( I18N::LangTags::Detect::detect(), 'en' )[0];
- }
+ $self->AddSeconds($DAY);
-
- my $formatter = DateTime::Locale->load($lang);
- return $self->loc("DateTime::Locale doesn't support date_format_full, you must upgrade to use this feature")
- unless $formatter->can('date_format_full');
- $date_format = $formatter->$date_format;
- $time_format = $formatter->$time_format;
- $date_format =~ s/EEEE/EEE/g if ( $args{'AbbrDay'} );
- $date_format =~ s/MMMM/MMM/g if ( $args{'AbbrMonth'} );
-
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
- $mon++;
- my $tz = $self->Timezone($args{'Timezone'});
-
- # FIXME : another way to call this module without conflict with local
- # DateTime method?
- my $dt = new DateTime::( locale => $lang,
- time_zone => $tz,
- year => $year,
- month => $mon,
- day => $mday,
- hour => $hour,
- minute => $min,
- second => $sec,
- nanosecond => 0,
- );
-
- if ( $args{'Date'} && !$args{'Time'} ) {
- return $dt->format_cldr($date_format);
- } elsif ( !$args{'Date'} && $args{'Time'} ) {
- return $dt->format_cldr($time_format);
- } else {
- return $dt->format_cldr($date_format) . " " . $dt->format_cldr($time_format);
- }
-}
-
-=head3 ISO
-
-Returns the object's date in ISO format C<YYYY-MM-DD mm:hh:ss>.
-ISO format is locale independant, but adding timezone offset info
-is not implemented yet.
-
-Supports arguments: C<Timezone>, C<Date>, C<Time> and C<Seconds>.
-See </Output formatters> for description of arguments.
-
-=cut
-
-sub ISO {
- my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- Seconds => 1,
- @_,
- );
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
-
- #the month needs incrementing, as gmtime returns 0-11
- $mon++;
-
- my $res = '';
- $res .= sprintf("%04d-%02d-%02d", $year, $mon, $mday) if $args{'Date'};
- $res .= sprintf(' %02d:%02d', $hour, $min) if $args{'Time'};
- $res .= sprintf(':%02d', $sec, $min) if $args{'Time'} && $args{'Seconds'};
- $res =~ s/^\s+//;
-
- return $res;
}
-=head3 W3CDTF
-
-Returns the object's date and time in W3C date time format
-(L<http://www.w3.org/TR/NOTE-datetime>).
-
-Format is locale independand and is close enought to ISO, but
-note that date part is B<not optional> and output string
-has timezone offset mark in C<[+-]hh:mm> format.
-
-Supports arguments: C<Timezone>, C<Time> and C<Seconds>.
-See </Output formatters> for description of arguments.
-
-=cut
-
-sub W3CDTF {
- my $self = shift;
- my %args = (
- Time => 1,
- Timezone => '',
- Seconds => 1,
- @_,
- Date => 1,
- );
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime( $args{'Timezone'} );
-
- #the month needs incrementing, as gmtime returns 0-11
- $mon++;
-
- my $res = '';
- $res .= sprintf("%04d-%02d-%02d", $year, $mon, $mday);
- if ( $args{'Time'} ) {
- $res .= sprintf('T%02d:%02d', $hour, $min);
- $res .= sprintf(':%02d', $sec, $min) if $args{'Seconds'};
- if ( $offset ) {
- $res .= sprintf "%s%02d:%02d", $self->_SplitOffset( $offset );
- } else {
- $res .= 'Z';
- }
- }
-
- return $res;
-};
+# }}}
+# {{{ sub Unix
-=head3 RFC2822 (MIME)
+=head2 sub Unix [unixtime]
-Returns the object's date and time in RFC2822 format,
-for example C<Sun, 06 Nov 1994 08:49:37 +0000>.
-Format is locale independand as required by RFC. Time
-part always has timezone offset in digits with sign prefix.
-
-Supports arguments: C<Timezone>, C<Date>, C<Time>, C<DayOfWeek>
-and C<Seconds>. See </Output formatters> for description of
-arguments.
+Optionally takes a date in unix seconds since the epoch format.
+Returns the number of seconds since the epoch
=cut
-sub RFC2822 {
+sub Unix {
my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- DayOfWeek => 1,
- Seconds => 1,
- @_,
- );
-
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
-
- my ($date, $time) = ('','');
- $date .= "$DAYS_OF_WEEK[$wday], " if $args{'DayOfWeek'} && $args{'Date'};
- $date .= "$mday $MONTHS[$mon] $year" if $args{'Date'};
-
- if ( $args{'Time'} ) {
- $time .= sprintf("%02d:%02d", $hour, $min);
- $time .= sprintf(":%02d", $sec) if $args{'Seconds'};
- $time .= sprintf " %s%02d%02d", $self->_SplitOffset( $offset );
- }
-
- return join ' ', grep $_, ($date, $time);
+
+ $self->{'time'} = shift if (@_);
+
+ return ($self->{'time'});
}
+# }}}
-=head3 RFC2616 (HTTP)
+# {{{ sub ISO
-Returns the object's date and time in RFC2616 (HTTP/1.1) format,
-for example C<Sun, 06 Nov 1994 08:49:37 GMT>. While the RFC describes
-version 1.1 of HTTP, but the same form date can be used in version 1.0.
+=head2 ISO
-Format is fixed length, locale independand and always represented in GMT
-what makes it quite useless for users, but any date in HTTP transfers
-must be presented using this format.
+Takes nothing
- HTTP-date = rfc1123 | ...
- rfc1123 = wkday "," SP date SP time SP "GMT"
- date = 2DIGIT SP month SP 4DIGIT
- ; day month year (e.g., 02 Jun 1982)
- time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
- ; 00:00:00 - 23:59:59
- wkday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
- month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun"
- | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
-
-Supports arguments: C<Date> and C<Time>, but you should use them only for
-some personal reasons, RFC2616 doesn't define any optional parts.
-See </Output formatters> for description of arguments.
+Returns the object's date in ISO format
=cut
-sub RFC2616 {
- my $self = shift;
- my %args = ( Date => 1, Time => 1,
- @_,
- Timezone => 'utc',
- Seconds => 1, DayOfWeek => 1,
- );
-
- my $res = $self->RFC2822( @_ );
- $res =~ s/\s*[+-]\d\d\d\d$/ GMT/ if $args{'Time'};
- return $res;
-}
-
-=head4 iCal
-
-Returns the object's date and time in iCalendar format,
-
-Supports arguments: C<Date> and C<Time>.
-See </Output formatters> for description of arguments.
-
-=cut
+sub ISO {
+ my $self=shift;
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
+
+ return ('1970-01-01 00:00:00') if ($self->Unix == -1);
-sub iCal {
- my $self = shift;
- my %args = (
- Date => 1, Time => 1,
- @_,
- );
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime( 'utc' );
+ # 0 1 2 3 4 5 6 7 8
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
+ #make the year YYYY
+ $year+=1900;
#the month needs incrementing, as gmtime returns 0-11
$mon++;
-
- my $res;
- if ( $args{'Date'} && !$args{'Time'} ) {
- $res = sprintf( '%04d%02d%02d', $year, $mon, $mday );
- }
- elsif ( !$args{'Date'} && $args{'Time'} ) {
- $res = sprintf( 'T%02d%02d%02dZ', $hour, $min, $sec );
- }
- else {
- $res = sprintf( '%04d%02d%02dT%02d%02d%02dZ', $year, $mon, $mday, $hour, $min, $sec );
- }
- return $res;
-}
-
-# it's been added by mistake in 3.8.0
-sub iCalDate { return (shift)->iCal( Time => 0, @_ ) }
-
-sub _SplitOffset {
- my ($self, $offset) = @_;
- my $sign = $offset < 0? '-': '+';
- $offset = int( (abs $offset) / 60 + 0.001 );
- my $mins = $offset % 60;
- my $hours = int( $offset/60 + 0.001 );
- return $sign, $hours, $mins;
-}
-
-=head2 Timezones handling
-
-=head3 Localtime $context [$time]
-
-Takes one mandatory argument C<$context>, which determines whether
-we want "user local", "system" or "UTC" time. Also, takes optional
-argument unix C<$time>, default value is the current unix time.
-
-Returns object's date and time in the format provided by perl's
-builtin functions C<localtime> and C<gmtime> with two exceptions:
-
-1) "Year" is a four-digit year, rather than "years since 1900"
-
-2) The last element of the array returned is C<offset>, which
-represents timezone offset against C<UTC> in seconds.
-
-=cut
-
-sub Localtime
-{
- my $self = shift;
- my $tz = $self->Timezone(shift);
-
- my $unix = shift || $self->Unix;
- $unix = 0 unless $unix >= 0;
+
+ $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
- my @local;
- if ($tz eq 'UTC') {
- @local = gmtime($unix);
- } else {
- {
- local $ENV{'TZ'} = $tz;
- ## Using POSIX::tzset fixes a bug where the TZ environment variable
- ## is cached.
- POSIX::tzset();
- @local = localtime($unix);
- }
- POSIX::tzset(); # return back previouse value
- }
- $local[5] += 1900; # change year to 4+ digits format
- my $offset = Time::Local::timegm_nocheck(@local) - $unix;
- return @local, $offset;
-}
-
-=head3 Timelocal $context @time
-
-Takes argument C<$context>, which determines whether we should
-treat C<@time> as "user local", "system" or "UTC" time.
-
-C<@time> is array returned by L<Localtime> functions. Only first
-six elements are mandatory - $sec, $min, $hour, $mday, $mon and $year.
-You may pass $wday, $yday and $isdst, these are ignored.
-
-If you pass C<$offset> as ninth argument, it's used instead of
-C<$context>. It's done such way as code
-C<$self->Timelocal('utc', $self->Localtime('server'))> doesn't
-makes much sense and most probably would produce unexpected
-result, so the method ignore 'utc' context and uses offset
-returned by L<Localtime> method.
-
-=cut
-
-sub Timelocal {
- my $self = shift;
- my $tz = shift;
- if ( defined $_[9] ) {
- return timegm(@_[0..5]) - $_[9];
- } else {
- $tz = $self->Timezone( $tz );
- if ( $tz eq 'UTC' ) {
- return Time::Local::timegm(@_[0..5]);
- } else {
- my $rv;
- {
- local $ENV{'TZ'} = $tz;
- ## Using POSIX::tzset fixes a bug where the TZ environment variable
- ## is cached.
- POSIX::tzset();
- $rv = Time::Local::timelocal(@_[0..5]);
- };
- POSIX::tzset(); # switch back to previouse value
- return $rv;
- }
- }
+ return ($date);
}
+# }}}
-=head3 Timezone $context
-
-Returns the timezone name.
-
-Takes one argument, C<$context> argument which could be C<user>, C<server> or C<utc>.
-=over
+# {{{ sub LocalTimezone
+=head2 LocalTimezone
-=item user
-
-Default value is C<user> that mean it returns current user's Timezone value.
-
-=item server
-
-If context is C<server> it returns value of the C<Timezone> RT config option.
-
-=item utc
-
-If both server's and user's timezone names are undefined returns 'UTC'.
-
-=back
+ Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
+pull from a 'Timezone' attribute of the CurrentUser
=cut
-sub Timezone {
+sub LocalTimezone {
my $self = shift;
- my $context = lc(shift);
- $context = 'utc' unless $context =~ /^(?:utc|server|user)$/i;
+ return $self->CurrentUser->Timezone
+ if $self->CurrentUser and $self->CurrentUser->can('Timezone');
- my $tz;
- if( $context eq 'user' ) {
- $tz = $self->CurrentUser->UserObj->Timezone;
- } elsif( $context eq 'server') {
- $tz = RT->Config->Get('Timezone');
- } else {
- $tz = 'UTC';
- }
- $tz ||= RT->Config->Get('Timezone') || 'UTC';
- $tz = 'UTC' if lc $tz eq 'gmt';
- return $tz;
+ return ($RT::Timezone);
}
+# }}}
eval "require RT::Date_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
diff --git a/rt/lib/RT/Extension/ActivityReports.pm b/rt/lib/RT/Extension/ActivityReports.pm
new file mode 100644
index 000000000..52d8ba6ab
--- /dev/null
+++ b/rt/lib/RT/Extension/ActivityReports.pm
@@ -0,0 +1,3 @@
+package RT::Extension::ActivityReports;
+
+our $VERSION = '0.2';
diff --git a/rt/lib/RT/Group.pm b/rt/lib/RT/Group.pm
index 17444b06d..4dcef3f07 100755
--- a/rt/lib/RT/Group.pm
+++ b/rt/lib/RT/Group.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -94,7 +69,7 @@ Create takes a hash of values and creates a row in the database:
varchar(255) 'Description'.
varchar(64) 'Domain'.
varchar(64) 'Type'.
- int(11) 'Instance'.
+ varchar(64) 'Instance'.
=cut
@@ -123,7 +98,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -132,14 +107,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -150,14 +125,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -168,14 +143,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Domain
+=item Domain
Returns the current value of Domain.
(In the database, Domain is stored as varchar(64).)
-=head2 SetDomain VALUE
+=item SetDomain VALUE
Set Domain to VALUE.
@@ -186,14 +161,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(64).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -204,40 +179,40 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Instance
+=item Instance
Returns the current value of Instance.
-(In the database, Instance is stored as int(11).)
+(In the database, Instance is stored as varchar(64).)
-=head2 SetInstance VALUE
+=item SetInstance VALUE
Set Instance to VALUE.
Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Instance will be stored as a int(11).)
+(In the database, Instance will be stored as a varchar(64).)
=cut
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Domain =>
- {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
Instance =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
}
};
@@ -269,7 +244,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/GroupMember.pm b/rt/lib/RT/GroupMember.pm
index f7fa73e9e..8de1a73fe 100755
--- a/rt/lib/RT/GroupMember.pm
+++ b/rt/lib/RT/GroupMember.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -114,7 +89,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -123,14 +98,14 @@ Returns the current value of id.
=cut
-=head2 GroupId
+=item GroupId
Returns the current value of GroupId.
(In the database, GroupId is stored as int(11).)
-=head2 SetGroupId VALUE
+=item SetGroupId VALUE
Set GroupId to VALUE.
@@ -141,14 +116,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 MemberId
+=item MemberId
Returns the current value of MemberId.
(In the database, MemberId is stored as int(11).)
-=head2 SetMemberId VALUE
+=item SetMemberId VALUE
Set MemberId to VALUE.
@@ -160,15 +135,15 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
GroupId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
MemberId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
}
};
@@ -200,7 +175,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/GroupMembers.pm b/rt/lib/RT/GroupMembers.pm
index 0e7514d3a..31cb9536f 100755
--- a/rt/lib/RT/GroupMembers.pm
+++ b/rt/lib/RT/GroupMembers.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::GroupMember item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Groups.pm b/rt/lib/RT/Groups.pm
index f8dac135b..29f12a5a0 100755
--- a/rt/lib/RT/Groups.pm
+++ b/rt/lib/RT/Groups.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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:
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# (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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Group item
@@ -116,6 +91,8 @@ sub NewItem {
};
+
+
=head1 SEE ALSO
This class allows "overlay" methods to be placed
@@ -124,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Groups_Overlay.pm b/rt/lib/RT/Groups_Overlay.pm
index 8d09755da..aba5cc0b3 100644
--- a/rt/lib/RT/Groups_Overlay.pm
+++ b/rt/lib/RT/Groups_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Groups - a collection of RT::Group objects
@@ -53,7 +52,7 @@
=head1 SYNOPSIS
use RT::Groups;
- my $groups = RT::Groups->new($CurrentUser);
+ my $groups = $RT::Groups->new($CurrentUser);
$groups->UnLimit();
while (my $group = $groups->Next()) {
print $group->Id ." is a group id\n";
@@ -65,6 +64,11 @@
=head1 METHODS
+=begin testing
+
+ok (require RT::Groups);
+
+=end testing
=cut
@@ -84,6 +88,24 @@ use RT::Users;
# {{{ sub _Init
+=begin testing
+
+# next had bugs
+# Groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => xx );
+my $g = RT::Group->new($RT::SystemUser);
+my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'GroupsNotEqualTest');
+ok ($id, "created group #". $g->id) or diag("error: $msg");
+
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $g->id );
+$groups->LimitToUserDefinedGroups();
+my $bug = grep $_->id == $g->id, @{$groups->ItemsArrayRef};
+ok (!$bug, "didn't find group");
+
+=end testing
+
+=cut
+
sub _Init {
my $self = shift;
$self->{'table'} = "Groups";
@@ -150,7 +172,7 @@ sub LimitToSystemInternalGroups {
# {{{ LimiToUserDefinedGroups
-=head2 LimitToUserDefinedGroups
+=head2 LimitToUserDefined Groups
Return only UserDefined Groups
@@ -167,7 +189,7 @@ sub LimitToUserDefinedGroups {
# }}}
-# {{{ LimiToPersonalGroupsFor
+# {{{ LimiToPersonalGroups
=head2 LimitToPersonalGroupsFor PRINCIPAL_ID
@@ -243,6 +265,27 @@ sub LimitToRolesForSystem {
Limits the set of groups returned to groups which have
Principal PRINCIPAL_ID as a member
+
+=begin testing
+
+my $u = RT::User->new($RT::SystemUser);
+$u->Create(Name => 'Membertests');
+my $g = RT::Group->new($RT::SystemUser);
+my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'Membertests');
+ok ($id, $msg);
+
+my ($aid, $amsg) =$g->AddMember($u->id);
+ok ($aid, $amsg);
+ok($g->HasMember($u->PrincipalObj),"G has member u");
+
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->LimitToUserDefinedGroups();
+$groups->WithMember(PrincipalId => $u->id);
+ok ($groups->Count == 1,"found the 1 group - " . $groups->Count);
+ok ($groups->First->Id == $g->Id, "it's the right one");
+
+=end testing
+
=cut
@@ -264,42 +307,92 @@ sub WithMember {
$self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'});
}
-sub WithoutMember {
- my $self = shift;
- my %args = (
- PrincipalId => undef,
- Recursively => undef,
- @_
- );
-
- my $members = $args{'Recursively'} ? 'CachedGroupMembers' : 'GroupMembers';
- my $members_alias = $self->Join(
- TYPE => 'LEFT',
- FIELD1 => 'id',
- TABLE2 => $members,
- FIELD2 => 'GroupId',
- );
- $self->Limit(
- LEFTJOIN => $members_alias,
- ALIAS => $members_alias,
- FIELD => 'MemberId',
- OPERATOR => '=',
- VALUE => $args{'PrincipalId'},
- );
- $self->Limit(
- ALIAS => $members_alias,
- FIELD => 'MemberId',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- QUOTEVALUE => 0,
- );
-}
=head2 WithRight { Right => RIGHTNAME, Object => RT::Record, IncludeSystemRights => 1, IncludeSuperusers => 0, EquivObjects => [ ] }
Find all groups which have RIGHTNAME for RT::Record. Optionally include global rights and superusers. By default, include the global rights, but not the superusers.
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+my ($id, $msg) =$q->Create( Name => 'GlobalACLTest');
+ok ($id, $msg);
+
+my $testuser = RT::User->new($RT::SystemUser);
+($id,$msg) = $testuser->Create(Name => 'JustAnAdminCc');
+ok ($id,$msg);
+
+my $global_admin_cc = RT::Group->new($RT::SystemUser);
+$global_admin_cc->LoadSystemRoleGroup('AdminCc');
+ok($global_admin_cc->id, "Found the global admincc group");
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'OwnTicket', Object => $q);
+is($groups->Count, 1);
+($id, $msg) = $global_admin_cc->PrincipalObj->GrantRight(Right =>'OwnTicket', Object=> $RT::System);
+ok ($id,$msg);
+ok (!$testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does not have the right to own tickets in the test queue");
+($id, $msg) = $q->AddWatcher(Type => 'AdminCc', PrincipalId => $testuser->id);
+ok($id,$msg);
+ok ($testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does have the right to own tickets now. thank god.");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'OwnTicket', Object => $q);
+ok ($id,$msg);
+is($groups->Count, 3);
+
+my $RTxGroup = RT::Group->new($RT::SystemUser);
+($id, $msg) = $RTxGroup->CreateUserDefinedGroup( Name => 'RTxGroup', Description => "RTx extension group");
+ok ($id,$msg);
+
+my $RTxSysObj = {};
+bless $RTxSysObj, 'RTx::System';
+*RTx::System::Id = sub { 1; };
+*RTx::System::id = *RTx::System::Id;
+my $ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System', ObjectId => 1);
+ok ($id, "ACL for RTxSysObj created");
+
+my $RTxObj = {};
+bless $RTxObj, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 4; };
+*RTx::System::Record::id = *RTx::System::Record::Id;
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxSysObj);
+is($groups->Count, 1, "RTxGroupRight found for RTxSysObj");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj);
+is($groups->Count, 0, "RTxGroupRight not found for RTxObj");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj using EquivObjects");
+
+$ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
+ok ($id, "ACL for RTxObj created");
+
+my $RTxObj2 = {};
+bless $RTxObj2, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 5; };
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj2");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj2");
+
+
+
+=end testing
=cut
@@ -322,6 +415,7 @@ sub WithRight {
$from_group->WithGroupRight( %args );
#XXX: DIRTY HACK
+ use DBIx::SearchBuilder 1.50; #no version on ::Union :(
use DBIx::SearchBuilder::Union;
my $union = new DBIx::SearchBuilder::Union;
$union->add($from_role);
diff --git a/rt/lib/RT/Handle.pm b/rt/lib/RT/Handle.pm
index 46070cec7..5cdb65e5b 100644
--- a/rt/lib/RT/Handle.pm
+++ b/rt/lib/RT/Handle.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,1042 +14,85 @@
# 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.)
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
=head1 NAME
-RT::Handle - RT's database handle
+ RT::Handle - RT's database handle
=head1 SYNOPSIS
- use RT;
- BEGIN { RT::LoadConfig() };
- use RT::Handle;
+ use RT::Handle;
=head1 DESCRIPTION
-C<RT::Handle> is RT specific wrapper over one of L<DBIx::SearchBuilder::Handle>
-classes. As RT works with different types of DBs we subclass repsective handler
-from L<DBIx::SerachBuilder>. Type of the DB is defined by C<DatabasseType> RT's
-config option. You B<must> load this module only when the configs have been
-loaded.
-
-=cut
+=begin testing
-package RT::Handle;
+ok(require RT::Handle);
-use strict;
-use warnings;
-use vars qw/@ISA/;
+=end testing
=head1 METHODS
-=head2 FinalizeDatabaseType
-
-Sets RT::Handle's superclass to the correct subclass of
-L<DBIx::SearchBuilder::Handle>, using the C<DatabaseType> configuration.
-
=cut
-sub FinalizeDatabaseType {
- eval "use DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .";
- \@ISA= qw(DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .");";
+package RT::Handle;
- if ($@) {
- die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
- "Perhaps you've picked an invalid database type or spelled it incorrectly.\n".
- $@;
- }
-}
+use strict;
+use vars qw/@ISA/;
+
+eval "use DBIx::SearchBuilder::Handle::$RT::DatabaseType;
+\@ISA= qw(DBIx::SearchBuilder::Handle::$RT::DatabaseType);";
+#TODO check for errors here.
=head2 Connect
-Connects to RT's database using credentials and options from the RT config.
-Takes nothing.
+Connects to RT's database handle.
+Takes nothing. Calls SUPER::Connect with the needed args
=cut
sub Connect {
- my $self = shift;
-
- my $db_type = RT->Config->Get('DatabaseType');
- if ( $db_type eq 'Oracle' ) {
- $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
- $ENV{'NLS_NCHAR'} = "AL32UTF8";
- }
+my $self=shift;
- $self->SUPER::Connect(
- User => RT->Config->Get('DatabaseUser'),
- Password => RT->Config->Get('DatabasePassword'),
- );
+# Unless the database port is a positive integer, we really don't want to pass it.
- if ( $db_type eq 'mysql' ) {
- my $version = $self->DatabaseVersion;
- ($version) = $version =~ /^(\d+\.\d+)/;
- $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
- }
-
- $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
+$self->SUPER::Connect(
+ User => $RT::DatabaseUser,
+ Password => $RT::DatabasePassword,
+ );
+
}
-=head2 BuildDSN
+=item BuildDSN
-Build the DSN for the RT database. Doesn't take any parameters, draws all that
-from the config.
+Build the DSN for the RT database. doesn't take any parameters, draws all that
+from the config file.
=cut
-require File::Spec;
sub BuildDSN {
my $self = shift;
- # Unless the database port is a positive integer, we really don't want to pass it.
- my $db_port = RT->Config->Get('DatabasePort');
- $db_port = undef unless (defined $db_port && $db_port =~ /^(\d+)$/);
- my $db_host = RT->Config->Get('DatabaseHost');
- $db_host = undef unless $db_host;
- my $db_name = RT->Config->Get('DatabaseName');
- my $db_type = RT->Config->Get('DatabaseType');
- $db_name = File::Spec->catfile($RT::VarPath, $db_name)
- if $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name);
-
- my %args = (
- Host => $db_host,
- Database => $db_name,
- Port => $db_port,
- Driver => $db_type,
- RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
- DisconnectHandleOnDestroy => 1,
- );
- if ( $db_type eq 'Oracle' && $db_host ) {
- $args{'SID'} = delete $args{'Database'};
- }
- $self->SUPER::BuildDSN( %args );
-}
-
-=head2 DSN
-
-Returns the DSN for this handle. In order to get correct value you must
-build DSN first, see L</BuildDSN>.
-
-This is method can be called as class method, in this case creates
-temporary handle object, L</BuildDSN builds DSN> and returns it.
-
-=cut
-
-sub DSN {
- my $self = shift;
- return $self->SUPER::DSN if ref $self;
-
- my $handle = $self->new;
- $handle->BuildDSN;
- return $handle->DSN;
-}
-
-=head2 SystemDSN
-
-Returns a DSN suitable for database creates and drops
-and user creates and drops.
-
-Gets RT's DSN first (see L<DSN>) and then change it according
-to requirements of a database system RT's using.
-
-=cut
-
-sub SystemDSN {
- my $self = shift;
-
- my $db_name = RT->Config->Get('DatabaseName');
- my $db_type = RT->Config->Get('DatabaseType');
-
- my $dsn = $self->DSN;
- if ( $db_type eq 'mysql' ) {
- # with mysql, you want to connect sans database to funge things
- $dsn =~ s/dbname=\Q$db_name//;
- }
- elsif ( $db_type eq 'Pg' ) {
- # with postgres, you want to connect to template1 database
- $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
- }
- elsif ( $db_type eq 'Informix' ) {
- # with Informix, you want to connect sans database:
- $dsn =~ s/Informix:\Q$db_name/Informix:/;
- }
- return $dsn;
-}
-
-=head2 Database compatibility and integrity checks
-
-
-
-=cut
-
-sub CheckIntegrity {
- my $self = shift;
-
- my $dsn = $self->DSN;
- my $user = RT->Config->Get('DatabaseUser');
- my $pass = RT->Config->Get('DatabasePassword');
-
- my $dbh = DBI->connect(
- $dsn, $user, $pass,
- { RaiseError => 0, PrintError => 0 },
- );
- unless ( $dbh ) {
- return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr);
- }
-
- RT::ConnectToDatabase();
- RT::InitLogging();
-
- require RT::CurrentUser;
- my $test_user = new RT::CurrentUser;
- $test_user->Load('RT_System');
- unless ( $test_user->id ) {
- return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'");
- }
-
- $test_user = new RT::CurrentUser;
- $test_user->Load('Nobody');
- unless ( $test_user->id ) {
- return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'");
- }
-
- return $dbh;
-}
-
-sub CheckCompatibility {
- my $self = shift;
- my $dbh = shift;
- my $state = shift || 'post';
-
- my $db_type = RT->Config->Get('DatabaseType');
- if ( $db_type eq "mysql" ) {
- # Check which version we're running
- my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
- return (0, "couldn't get version of the mysql server")
- unless $version;
-
- ($version) = $version =~ /^(\d+\.\d+)/;
- return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
- if $version < 4;
-
- # MySQL must have InnoDB support
- my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
- if ( lc $innodb eq "no" ) {
- return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
- "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
- } elsif ( lc $innodb eq "disabled" ) {
- return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
- "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
- }
-
- if ( $state eq 'post' ) {
- my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Tickets")->[1];
- unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
- return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
- }
- }
- if ( $version >= 4.1 && $state eq 'post' ) {
- my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
- unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
- return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
- ."Follow instructions in the UPGRADING.mysql file.");
- }
- }
- }
- return (1)
-}
-
-=head2 Database maintanance
-
-=head3 CreateDatabase $DBH
-
-Creates a new database. This method can be used as class method.
-
-Takes DBI handle. Many database systems require special handle to
-allow you to create a new database, so you have to use L<SystemDSN>
-method during connection.
-
-Fetches type and name of the DB from the config.
-
-=cut
-
-sub CreateDatabase {
- my $self = shift;
- my $dbh = shift or return (0, "No DBI handle provided");
- my $db_type = RT->Config->Get('DatabaseType');
- my $db_name = RT->Config->Get('DatabaseName');
-
- my $status;
- if ( $db_type eq 'SQLite' ) {
- return (1, 'Skipped as SQLite doesn\'t need any action');
- }
- elsif ( $db_type eq 'Oracle' ) {
- my $db_user = RT->Config->Get('DatabaseUser');
- my $db_pass = RT->Config->Get('DatabasePassword');
- $status = $dbh->do(
- "CREATE USER $db_user IDENTIFIED BY $db_pass"
- ." default tablespace USERS"
- ." temporary tablespace TEMP"
- ." quota unlimited on USERS"
- );
- unless ( $status ) {
- return $status, "Couldn't create user $db_user identified by $db_pass."
- ."\nError: ". $dbh->errstr;
- }
- $status = $dbh->do( "GRANT connect, resource TO $db_user" );
- unless ( $status ) {
- return $status, "Couldn't grant connect and resource to $db_user."
- ."\nError: ". $dbh->errstr;
- }
- return (1, "Created user $db_user. All RT's objects should be in his schema.");
- }
- elsif ( $db_type eq 'Pg' ) {
- # XXX: as we get external DBH we don't know if RaiseError or PrintError
- # are enabled, so we have to setup it here and restore them back
- $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0")
- || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0");
- }
- elsif ( $db_type eq 'Informix' ) {
- local $ENV{'DB_LOCALE'} = 'en_us.utf8';
- $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG");
- }
- else {
- $status = $dbh->do("CREATE DATABASE $db_name");
- }
- return ($status, $DBI::errstr);
-}
-
-=head3 DropDatabase $DBH [Force => 0]
-
-Drops RT's database. This method can be used as class method.
-
-Takes DBI handle as first argument. Many database systems require
-special handle to allow you to create a new database, so you have
-to use L<SystemDSN> method during connection.
-
-Fetches type and name of the DB from the config.
-
-=cut
-
-sub DropDatabase {
- my $self = shift;
- my $dbh = shift or return (0, "No DBI handle provided");
-
- my $db_type = RT->Config->Get('DatabaseType');
- my $db_name = RT->Config->Get('DatabaseName');
-
- if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) {
- my $db_user = RT->Config->Get('DatabaseUser');
- my $status = $dbh->do( "DROP USER $db_user CASCADE" );
- unless ( $status ) {
- return 0, "Couldn't drop user $db_user."
- ."\nError: ". $dbh->errstr;
- }
- return (1, "Successfully dropped user '$db_user' with his schema.");
- }
- elsif ( $db_type eq 'SQLite' ) {
- my $path = $db_name;
- $path = "$RT::VarPath/$path" unless substr($path, 0, 1) eq '/';
- unlink $path or return (0, "Couldn't remove '$path': $!");
- return (1);
- } else {
- $dbh->do("DROP DATABASE ". $db_name)
- or return (0, $DBI::errstr);
- }
- return (1);
-}
-
-=head2 InsertACL
-
-=cut
-
-sub InsertACL {
- my $self = shift;
- my $dbh = shift;
- my $base_path = shift || $RT::EtcPath;
-
- my $db_type = RT->Config->Get('DatabaseType');
- return (1) if $db_type eq 'SQLite';
-
- $dbh = $self->dbh if !$dbh && ref $self;
- return (0, "No DBI handle provided") unless $dbh;
-
- return (0, "'$base_path' doesn't exist") unless -e $base_path;
-
- my $path;
- if ( -d $base_path ) {
- $path = File::Spec->catfile( $base_path, "acl.$db_type");
- $path = $self->GetVersionFile($dbh, $path);
-
- $path = File::Spec->catfile( $base_path, "acl")
- unless $path && -e $path;
- return (0, "Couldn't find ACLs for $db_type")
- unless -e $path;
- } else {
- $path = $base_path;
- }
-
- local *acl;
- do $path || return (0, "Couldn't load ACLs: " . $@);
- my @acl = acl($dbh);
- foreach my $statement (@acl) {
- my $sth = $dbh->prepare($statement)
- or return (0, "Couldn't prepare SQL query:\n $statement\n\nERROR: ". $dbh->errstr);
- unless ( $sth->execute ) {
- return (0, "Couldn't run SQL query:\n $statement\n\nERROR: ". $sth->errstr);
- }
- }
- return (1);
-}
-
-=head2 InsertSchema
-
-=cut
-
-sub InsertSchema {
- my $self = shift;
- my $dbh = shift;
- my $base_path = (shift || $RT::EtcPath);
-
- $dbh = $self->dbh if !$dbh && ref $self;
- return (0, "No DBI handle provided") unless $dbh;
-
- my $db_type = RT->Config->Get('DatabaseType');
-
- my $file;
- if ( -d $base_path ) {
- $file = $base_path . "/schema." . $db_type;
- } else {
- $file = $base_path;
- }
-
- $file = $self->GetVersionFile( $dbh, $file );
- unless ( $file ) {
- return (0, "Couldn't find schema file(s) '$file*'");
- }
- unless ( -f $file && -r $file ) {
- return (0, "File '$file' doesn't exist or couldn't be read");
- }
-
- my (@schema);
-
- open my $fh_schema, "<$file";
-
- my $has_local = 0;
- open my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type )
- and $has_local = 1;
-
- my $statement = "";
- foreach my $line ( <$fh_schema>, ($_ = ';;'), $has_local? <$fh_schema_local>: () ) {
- $line =~ s/\#.*//g;
- $line =~ s/--.*//g;
- $statement .= $line;
- if ( $line =~ /;(\s*)$/ ) {
- $statement =~ s/;(\s*)$//g;
- push @schema, $statement;
- $statement = "";
- }
- }
- close $fh_schema; close $fh_schema_local;
-
- if ( $db_type eq 'Oracle' ) {
- my $db_user = RT->Config->Get('DatabaseUser');
- my $status = $dbh->do( "ALTER SESSION SET CURRENT_SCHEMA=$db_user" );
- unless ( $status ) {
- return $status, "Couldn't set current schema to $db_user."
- ."\nError: ". $dbh->errstr;
- }
- }
-
- local $SIG{__WARN__} = sub {};
- my $is_local = 0;
- $dbh->begin_work or return (0, "Couldn't begin transaction: ". $dbh->errstr);
- foreach my $statement (@schema) {
- if ( $statement =~ /^\s*;$/ ) {
- $is_local = 1; next;
- }
-
- my $sth = $dbh->prepare($statement)
- or return (0, "Couldn't prepare SQL query:\n$statement\n\nERROR: ". $dbh->errstr);
- unless ( $sth->execute or $is_local ) {
- return (0, "Couldn't run SQL query:\n$statement\n\nERROR: ". $sth->errstr);
- }
- }
- $dbh->commit or return (0, "Couldn't commit transaction: ". $dbh->errstr);
- return (1);
-}
-
-=head1 GetVersionFile
-
-Takes base name of the file as argument, scans for <base name>-<version> named
-files and returns file name with closest version to the version of the RT DB.
-
-=cut
-
-sub GetVersionFile {
- my $self = shift;
- my $dbh = shift;
- my $base_name = shift;
+$RT::DatabasePort = undef unless (defined $RT::DatabasePort && $RT::DatabasePort =~ /^(\d+)$/);
+$RT::DatabaseHost = undef unless (defined $RT::DatabaseHost && $RT::DatabaseHost ne '');
- my $db_version = ref $self
- ? $self->DatabaseVersion
- : do {
- my $tmp = RT::Handle->new;
- $tmp->dbh($dbh);
- $tmp->DatabaseVersion;
- };
+ $self->SUPER::BuildDSN(Host => $RT::DatabaseHost,
+ Database => $RT::DatabaseName,
+ Port => $RT::DatabasePort,
+ Driver => $RT::DatabaseType,
+ RequireSSL => $RT::DatabaseRequireSSL,
+ DisconnectHandleOnDestroy => 1
+ );
+
- require File::Glob;
- my @files = File::Glob::bsd_glob("$base_name*");
- return '' unless @files;
-
- my %version = map { $_ =~ /\.\w+-([-\w\.]+)$/; ($1||0) => $_ } @files;
- my $version;
- foreach ( reverse sort cmp_version keys %version ) {
- if ( cmp_version( $db_version, $_ ) >= 0 ) {
- $version = $_;
- last;
- }
- }
-
- return defined $version? $version{ $version } : undef;
-}
-
-sub cmp_version($$) {
- my ($a, $b) = (@_);
- $b =~ s/HEAD$/9999/;
- my @a = split /[^0-9]+/, $a;
- my @b = split /[^0-9]+/, $b;
- for ( my $i = 0; $i < @a; $i++ ) {
- return 1 unless defined $b[$i];
- return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
- }
- return 0 if @a == @b;
- return -1;
}
-
-=head2 InsertInitialData
-
-Inserts system objects into RT's DB, like system user or 'nobody',
-internal groups and other records required. However, this method
-doesn't insert any real users like 'root' and you have to use
-InsertData or another way to do that.
-
-Takes no arguments. Returns status and message tuple.
-
-It's safe to call this method even if those objects already exist.
-
-=cut
-
-sub InsertInitialData {
- my $self = shift;
-
- my @warns;
-
- # create RT_System user and grant him rights
- {
- require RT::CurrentUser;
-
- my $test_user = RT::User->new( new RT::CurrentUser );
- $test_user->Load('RT_System');
- if ( $test_user->id ) {
- push @warns, "Found system user in the DB.";
- }
- else {
- my $user = RT::User->new( new RT::CurrentUser );
- my ( $val, $msg ) = $user->_BootstrapCreate(
- Name => 'RT_System',
- RealName => 'The RT System itself',
- Comments => 'Do not delete or modify this user. '
- . 'It is integral to RT\'s internal database structures',
- Creator => '1',
- LastUpdatedBy => '1',
- );
- return ($val, $msg) unless $val;
- }
- DBIx::SearchBuilder::Record::Cachable->FlushCache;
- }
-
- # init RT::SystemUser and RT::System objects
- RT::InitSystemObjects();
- unless ( $RT::SystemUser->id ) {
- return (0, "Couldn't load system user");
- }
-
- # grant SuperUser right to system user
- {
- my $test_ace = RT::ACE->new( $RT::SystemUser );
- $test_ace->LoadByCols(
- PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
- PrincipalType => 'Group',
- RightName => 'SuperUser',
- ObjectType => 'RT::System',
- ObjectId => 1,
- );
- if ( $test_ace->id ) {
- push @warns, "System user has global SuperUser right.";
- } else {
- my $ace = RT::ACE->new( $RT::SystemUser );
- my ( $val, $msg ) = $ace->_BootstrapCreate(
- PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
- PrincipalType => 'Group',
- RightName => 'SuperUser',
- ObjectType => 'RT::System',
- ObjectId => 1,
- );
- return ($val, $msg) unless $val;
- }
- DBIx::SearchBuilder::Record::Cachable->FlushCache;
- }
-
- # system groups
- # $self->loc('Everyone'); # For the string extractor to get a string to localize
- # $self->loc('Privileged'); # For the string extractor to get a string to localize
- # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
- foreach my $name (qw(Everyone Privileged Unprivileged)) {
- my $group = RT::Group->new( $RT::SystemUser );
- $group->LoadSystemInternalGroup( $name );
- if ( $group->id ) {
- push @warns, "System group '$name' already exists.";
- next;
- }
-
- $group = RT::Group->new( $RT::SystemUser );
- my ( $val, $msg ) = $group->_Create(
- Type => $name,
- Domain => 'SystemInternal',
- Description => 'Pseudogroup for internal use', # loc
- Name => '',
- Instance => '',
- );
- return ($val, $msg) unless $val;
- }
-
- # nobody
- {
- my $user = RT::User->new( $RT::SystemUser );
- $user->Load('Nobody');
- if ( $user->id ) {
- push @warns, "Found 'Nobody' user in the DB.";
- }
- else {
- my ( $val, $msg ) = $user->Create(
- Name => 'Nobody',
- RealName => 'Nobody in particular',
- Comments => 'Do not delete or modify this user. It is integral '
- .'to RT\'s internal data structures',
- Privileged => 0,
- );
- return ($val, $msg) unless $val;
- }
-
- if ( $user->HasRight( Right => 'OwnTicket', Object => $RT::System ) ) {
- push @warns, "User 'Nobody' has global OwnTicket right.";
- } else {
- my ( $val, $msg ) = $user->PrincipalObj->GrantRight(
- Right => 'OwnTicket',
- Object => $RT::System,
- );
- return ($val, $msg) unless $val;
- }
- }
-
- # rerun to get init Nobody as well
- RT::InitSystemObjects();
-
- # system role groups
- foreach my $name (qw(Owner Requestor Cc AdminCc)) {
- my $group = RT::Group->new( $RT::SystemUser );
- $group->LoadSystemRoleGroup( $name );
- if ( $group->id ) {
- push @warns, "System role '$name' already exists.";
- next;
- }
-
- $group = RT::Group->new( $RT::SystemUser );
- my ( $val, $msg ) = $group->_Create(
- Type => $name,
- Domain => 'RT::System-Role',
- Description => 'SystemRolegroup for internal use', # loc
- Name => '',
- Instance => '',
- );
- return ($val, $msg) unless $val;
- }
-
- push @warns, "You appear to have a functional RT database."
- if @warns;
-
- return (1, join "\n", @warns);
-}
-
-=head2 InsertData
-
-Load some sort of data into the database, takes path to a file.
-
-=cut
-
-sub InsertData {
- my $self = shift;
- my $datafile = shift;
-
- # Slurp in stuff to insert from the datafile. Possible things to go in here:-
- our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
- @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
- local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
- @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
-
- local $@;
- $RT::Logger->debug("Going to load '$datafile' data file");
- eval { require $datafile }
- or return (0, "Couldn't load data from '$datafile' for import:\n\nERROR:". $@);
-
- if ( @Initial ) {
- $RT::Logger->debug("Running initial actions...");
- foreach ( @Initial ) {
- local $@;
- eval { $_->(); 1 } or return (0, "One of initial functions failed: $@");
- }
- $RT::Logger->debug("Done.");
- }
- if ( @Groups ) {
- $RT::Logger->debug("Creating groups...");
- foreach my $item (@Groups) {
- my $new_entry = RT::Group->new( $RT::SystemUser );
- my $member_of = delete $item->{'MemberOf'};
- my ( $return, $msg ) = $new_entry->_Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- next;
- } else {
- $RT::Logger->debug($return .".");
- }
- if ( $member_of ) {
- $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
- foreach( @$member_of ) {
- my $parent = RT::Group->new($RT::SystemUser);
- if ( ref $_ eq 'HASH' ) {
- $parent->LoadByCols( %$_ );
- }
- elsif ( !ref $_ ) {
- $parent->LoadUserDefinedGroup( $_ );
- }
- else {
- $RT::Logger->error(
- "(Error: wrong format of MemberOf field."
- ." Should be name of user defined group or"
- ." hash reference with 'column => value' pairs."
- ." Use array reference to add to multiple groups)"
- );
- next;
- }
- unless ( $parent->Id ) {
- $RT::Logger->error("(Error: couldn't load group to add member)");
- next;
- }
- my ( $return, $msg ) = $parent->AddMember( $new_entry->Id );
- unless ( $return ) {
- $RT::Logger->error( $msg );
- } else {
- $RT::Logger->debug( $return ."." );
- }
- }
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Users ) {
- $RT::Logger->debug("Creating users...");
- foreach my $item (@Users) {
- my $new_entry = new RT::User( $RT::SystemUser );
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- } else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Queues ) {
- $RT::Logger->debug("Creating queues...");
- for my $item (@Queues) {
- my $new_entry = new RT::Queue($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- } else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @CustomFields ) {
- $RT::Logger->debug("Creating custom fields...");
- for my $item ( @CustomFields ) {
- my $new_entry = new RT::CustomField( $RT::SystemUser );
- my $values = delete $item->{'Values'};
-
- my @queues;
- # if ref then it's list of queues, so we do things ourself
- if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
- $item->{'LookupType'} = 'RT::Queue-RT::Ticket';
- @queues = @{ delete $item->{'Queue'} };
- }
-
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless( $return ) {
- $RT::Logger->error( $msg );
- next;
- }
-
- foreach my $value ( @{$values} ) {
- my ( $return, $msg ) = $new_entry->AddValue(%$value);
- $RT::Logger->error( $msg ) unless $return;
- }
-
- # apply by default
- if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
- my $ocf = RT::ObjectCustomField->new($RT::SystemUser);
- $ocf->Create( CustomField => $new_entry->Id );
- }
-
- for my $q (@queues) {
- my $q_obj = RT::Queue->new($RT::SystemUser);
- $q_obj->Load($q);
- unless ( $q_obj->Id ) {
- $RT::Logger->error("Could not find queue ". $q );
- next;
- }
- my $OCF = RT::ObjectCustomField->new($RT::SystemUser);
- ( $return, $msg ) = $OCF->Create(
- CustomField => $new_entry->Id,
- ObjectId => $q_obj->Id,
- );
- $RT::Logger->error( $msg ) unless $return and $OCF->Id;
- }
- }
-
- $RT::Logger->debug("done.");
- }
- if ( @ACL ) {
- $RT::Logger->debug("Creating ACL...");
- for my $item (@ACL) {
-
- my ($princ, $object);
-
- # Global rights or Queue rights?
- if ( $item->{'CF'} ) {
- $object = RT::CustomField->new( $RT::SystemUser );
- my @columns = ( Name => $item->{'CF'} );
- push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
- $object->LoadByName( @columns );
- } elsif ( $item->{'Queue'} ) {
- $object = RT::Queue->new($RT::SystemUser);
- $object->Load( $item->{'Queue'} );
- } else {
- $object = $RT::System;
- }
-
- $RT::Logger->error("Couldn't load object") and next unless $object and $object->Id;
-
- # Group rights or user rights?
- if ( $item->{'GroupDomain'} ) {
- $princ = RT::Group->new($RT::SystemUser);
- if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
- $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
- } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
- $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
- } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
- $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
- } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
- $item->{'Queue'} )
- {
- $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
- Queue => $object->id);
- } else {
- $princ->Load( $item->{'GroupId'} );
- }
- } else {
- $princ = RT::User->new($RT::SystemUser);
- $princ->Load( $item->{'UserId'} );
- }
-
- # Grant it
- my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
- Right => $item->{'Right'},
- Object => $object
- );
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
-
- if ( @ScripActions ) {
- $RT::Logger->debug("Creating ScripActions...");
-
- for my $item (@ScripActions) {
- my $new_entry = RT::ScripAction->new($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
-
- $RT::Logger->debug("done.");
- }
-
- if ( @ScripConditions ) {
- $RT::Logger->debug("Creating ScripConditions...");
-
- for my $item (@ScripConditions) {
- my $new_entry = RT::ScripCondition->new($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
-
- $RT::Logger->debug("done.");
- }
-
- if ( @Templates ) {
- $RT::Logger->debug("Creating templates...");
-
- for my $item (@Templates) {
- my $new_entry = new RT::Template($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Scrips ) {
- $RT::Logger->debug("Creating scrips...");
-
- for my $item (@Scrips) {
- my $new_entry = new RT::Scrip($RT::SystemUser);
-
- my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
- push @queues, 0 unless @queues; # add global queue at least
-
- foreach my $q ( @queues ) {
- my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Attributes ) {
- $RT::Logger->debug("Creating predefined searches...");
- my $sys = RT::System->new($RT::SystemUser);
-
- for my $item (@Attributes) {
- my $obj = delete $item->{Object}; # XXX: make this something loadable
- $obj ||= $sys;
- my ( $return, $msg ) = $obj->AddAttribute (%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Final ) {
- $RT::Logger->debug("Running final actions...");
- for ( @Final ) {
- local $@;
- eval { $_->(); };
- $RT::Logger->error( "Failed to run one of final actions: $@" )
- if $@;
- }
- $RT::Logger->debug("done.");
- }
-
- my $db_type = RT->Config->Get('DatabaseType');
- $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
-
- $RT::Logger->debug("Done setting up database content.");
-
-# TODO is it ok to return 1 here? If so, the previous codes in this sub
-# should return (0, $msg) if error happens instead of just warning.
-# anyway, we need to return something here to tell if everything is ok
- return( 1, 'Done inserting data' );
-}
-
-=head2 ACLEquivGroupId
-
-Given a userid, return that user's acl equivalence group
-
-=cut
-
-sub ACLEquivGroupId {
- my $id = shift;
-
- my $cu = $RT::SystemUser;
- unless ( $cu ) {
- require RT::CurrentUser;
- $cu = new RT::CurrentUser;
- $cu->LoadByName('RT_System');
- warn "Couldn't load RT_System user" unless $cu->id;
- }
-
- my $equiv_group = RT::Group->new( $cu );
- $equiv_group->LoadACLEquivalenceGroup( $id );
- return $equiv_group->Id;
-}
-
-__PACKAGE__->FinalizeDatabaseType;
-
eval "require RT::Handle_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm});
eval "require RT::Handle_Local";
diff --git a/rt/lib/RT/I18N/en_malkovich.po b/rt/lib/RT/I18N/en_malkovich.po
deleted file mode 100644
index 74769f1a3..000000000
--- a/rt/lib/RT/I18N/en_malkovich.po
+++ /dev/null
@@ -1,3973 +0,0 @@
-msgid ""
-msgstr ""
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: html/Approvals/Elements/Approve:26 html/Approvals/Elements/ShowDependency:49 html/SelfService/Display.html:24 html/Ticket/Display.html:25 html/Ticket/Display.html:29
-#. ($TicketObj->Id, $TicketObj->Subject)
-#. ($Ticket->id, $Ticket->Subject)
-#. ($ticket->Id, $ticket->Subject)
-#. ($link->BaseObj->Id, $link->BaseObj->Subject)
-msgid "#%1: %2"
-msgstr "#%1: %2"
-
-#: html/Search/Elements/SelectPersonType:30 lib/RT/Date.pm:337
-#. ($s, $time_unit)
-#. ($option, $subtype)
-msgid "%1 %2"
-msgstr "%1 %2"
-
-#: lib/RT/Tickets_Overlay.pm:828
-#. ($args{'FIELD'}, $args{'OPERATOR'}, $args{'VALUE'})
-msgid "%1 %2 %3"
-msgstr "%1 %2 %3"
-
-#: lib/RT/Date.pm:373
-#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
-msgid "%1 %2 %3 %4:%5:%6 %7"
-msgstr "%1 %2 %3 %4:%5:%6 %7"
-
-#: lib/RT/Ticket_Overlay.pm:3451 lib/RT/Transaction_Overlay.pm:550 lib/RT/Transaction_Overlay.pm:593
-#. ($cf->Name, $new_value->Content)
-#. ($field, $self->NewValue)
-#. ($self->Field, $principal->Object->Name)
-msgid "%1 %2 added"
-msgstr "%1 %2 Malkovich"
-
-#: lib/RT/Date.pm:334
-#. ($s, $time_unit)
-msgid "%1 %2 ago"
-msgstr "%1 %2 ago"
-
-#: lib/RT/Ticket_Overlay.pm:3457 lib/RT/Transaction_Overlay.pm:557
-#. ($cf->Name, $old_value, $new_value->Content)
-#. ($field, $self->OldValue, $self->NewValue)
-msgid "%1 %2 changed to %3"
-msgstr "%1 %2 Malkovich to %3"
-
-#: lib/RT/Ticket_Overlay.pm:3454 lib/RT/Transaction_Overlay.pm:553 lib/RT/Transaction_Overlay.pm:599
-#. ($cf->Name, $old_value)
-#. ($field, $self->OldValue)
-#. ($self->Field, $principal->Object->Name)
-msgid "%1 %2 deleted"
-msgstr "%1 %2 Malkovich"
-
-#: html/Admin/Elements/EditScrips:43 html/Admin/Elements/ListGlobalScrips:27 html/Ticket/Elements/PreviewScrips:53
-#. ($scrip->ConditionObj->Name, $scrip->ActionObj->Name, $scrip->TemplateObj->Name)
-#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
-msgid "%1 %2 with template %3"
-msgstr "%1 %2 Malkovich %3"
-
-#: bin/rt-crontool:165 bin/rt-crontool:172 bin/rt-crontool:178
-#. ("--search-argument", "--search")
-#. ("--condition-argument", "--condition")
-#. ("--action-argument", "--action")
-msgid "%1 - An argument to pass to %2"
-msgstr "%1 - A Malkovich to pass to %2"
-
-#: bin/rt-crontool:181
-#. ("--verbose")
-msgid "%1 - Output status updates to STDOUT"
-msgstr "%1 - Malkovich Malkovich to MALKOVICH"
-
-#: bin/rt-crontool:175
-#. ("--action")
-msgid "%1 - Specify the action module you want to use"
-msgstr "%1 - Malkovich the Malkovich Malkovich to use"
-
-#: bin/rt-crontool:169
-#. ("--condition")
-msgid "%1 - Specify the condition module you want to use"
-msgstr "%1 - Malkovich the Malkovich Malkovich to use"
-
-#: bin/rt-crontool:162
-#. ("--search")
-msgid "%1 - Specify the search module you want to use"
-msgstr "%1 - Malkovich the Malkovich Malkovich to use"
-
-#: lib/RT/ScripAction_Overlay.pm:114
-#. ($self->Id)
-msgid "%1 ScripAction loaded"
-msgstr "%1 Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3484
-#. ($args{'Value'}, $cf->Name)
-msgid "%1 added as a value for %2"
-msgstr "%1 Malkovich as a Malkovich %2"
-
-#: lib/RT/Link_Overlay.pm:111 lib/RT/Link_Overlay.pm:118
-#. ($args{'Base'})
-#. ($args{'Target'})
-msgid "%1 appears to be a local object, but can't be found in the database"
-msgstr "%1 Malkovich to be a Malkovich, but can't be Malkovich in the Malkovich"
-
-#: html/Ticket/Elements/ShowDates:52 lib/RT/Transaction_Overlay.pm:458
-#. ($self->BriefDescription , $self->CreatorObj->Name)
-#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
-msgid "%1 by %2"
-msgstr "%1 by %2"
-
-#: lib/RT/Transaction_Overlay.pm:512 lib/RT/Transaction_Overlay.pm:688 lib/RT/Transaction_Overlay.pm:697 lib/RT/Transaction_Overlay.pm:700
-#. ($self->Field , ( $self->OldValue || $no_value ) , $self->NewValue)
-#. ($self->Field , $q1->Name , $q2->Name)
-#. ($self->Field, $t2->AsString, $t1->AsString)
-#. ($self->Field, $self->OldValue, $self->NewValue)
-msgid "%1 changed from %2 to %3"
-msgstr "%1 Malkovich %2 to %3"
-
-#: lib/RT/Record.pm:739
-msgid "%1 could not be set to %2."
-msgstr "%1 Malkovich be set to %2."
-
-#: lib/RT/Ticket_Overlay.pm:2739
-#. ($self)
-msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
-msgstr "%1 couldn't Malkovich to Malkovich. RT's Malkovich be Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "%1 highest priority tickets I own..."
-msgstr "%1 Malkovich Malkovich I Malkovich..."
-
-#: html/Elements/MyTickets:26
-#. ($rows)
-msgid "%1 highest priority tickets I requested..."
-msgstr "%1 Malkovich Malkovich I Malkovich..."
-
-#: bin/rt-crontool:157
-#. ($0)
-msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
-msgstr "%1 is a tool to act on Malkovich a Malkovich Malkovich, such as cron."
-
-#: lib/RT/Queue_Overlay.pm:784
-#. ($principal->Object->Name, $args{'Type'})
-msgid "%1 is no longer a %2 for this queue."
-msgstr "%1 is no Malkovich a %2 Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:3540
-#. ($args{'Value'}, $cf->Name)
-msgid "%1 is no longer a value for custom field %2"
-msgstr "%1 is no Malkovich a Malkovich Malkovich %2"
-
-#: html/Ticket/Create.html:155 html/Ticket/Create.html:156 html/Ticket/Elements/ShowBasics:36 html/Ticket/Elements/ShowBasics:42 html/Ticket/Elements/ShowBasics:47
-#. ('<input size=3 name="TimeWorked" value="'.$ARGS{TimeWorked}.'">')
-#. ('<input size=3 name="TimeLeft" value="'.$ARGS{TimeLeft}.'">')
-#. ($Ticket->TimeEstimated)
-#. ($Ticket->TimeWorked)
-#. ($Ticket->TimeLeft)
-msgid "%1 min"
-msgstr "%1 min"
-
-#: html/User/Elements/DelegateRights:75
-#. (loc($ObjectType =~ /^RT::(.*)$/))
-msgid "%1 rights"
-msgstr "%1 Malkovich"
-
-#: lib/RT/Action/ResolveMembers.pm:41
-#. (ref $self)
-msgid "%1 will resolve all members of a resolved group ticket."
-msgstr "%1 Malkovich Malkovich of a Malkovich Malkovich."
-
-#: lib/RT/Transaction_Overlay.pm:408
-#. ($self)
-msgid "%1: no attachment specified"
-msgstr "%1: no Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:56
-#. ($size)
-msgid "%1b"
-msgstr "%1b"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:53
-#. (int( $size / 102.4 ) / 10)
-msgid "%1k"
-msgstr "%1k"
-
-#: lib/RT/Ticket_Overlay.pm:1252
-#. ($args{'Status'})
-msgid "'%1' is an invalid value for status"
-msgstr "'%1' is a Malkovich Malkovich"
-
-#: html/Admin/Elements/EditCustomFieldValues:24 html/Admin/Elements/EditQueueWatchers:28 html/Admin/Elements/EditScrips:34 html/Admin/Elements/EditTemplates:35 html/Admin/Groups/Members.html:51 html/Elements/EditLinks:32 html/Ticket/Elements/EditPeople:45 html/User/Groups/Members.html:54
-msgid "(Check box to delete)"
-msgstr "(Malkovich to Malkovich)"
-
-#: html/Ticket/Elements/PreviewScrips:49
-msgid "(Check boxes to disable notifications to the listed recipients)"
-msgstr "(Malkovich to Malkovich Malkovich to the Malkovich Malkovich)"
-
-#: html/Ticket/Elements/PreviewScrips:71
-msgid "(Check boxes to enable notifications to the listed recipients)"
-msgstr "(Malkovich to Malkovich Malkovich to the Malkovich Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "(Enter ticket ids or URLs, seperated with spaces)"
-msgstr "(Malkovich Malkovich or URLs, Malkovich Malkovich)"
-
-#: html/Admin/Queues/Modify.html:53 html/Admin/Queues/Modify.html:59
-#. ($RT::CorrespondAddress)
-#. ($RT::CommentAddress)
-msgid "(If left blank, will default to %1"
-msgstr "(If Malkovich, Malkovich to %1"
-
-#: html/Admin/Elements/EditCustomFields:32 html/Admin/Elements/ListGlobalCustomFields:31
-msgid "(No custom fields)"
-msgstr "(No Malkovich)"
-
-#: html/Admin/Groups/Members.html:49 html/User/Groups/Members.html:52
-msgid "(No members)"
-msgstr "(No Malkovich)"
-
-#: html/Admin/Elements/EditScrips:31 html/Admin/Elements/ListGlobalScrips:31
-msgid "(No scrips)"
-msgstr "(No Malkovich)"
-
-#: html/Admin/Elements/EditTemplates:30
-msgid "(No templates)"
-msgstr "(No Malkovich)"
-
-#: html/Ticket/Update.html:66
-msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich. Does <b>not</b> Malkovich Malkovich Malkovich Malkovich.)"
-
-#: html/Ticket/Create.html:78
-msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich Malkovich Malkovich. Malkovich <b>will</b> Malkovich Malkovich.)"
-
-#: html/Ticket/Update.html:62
-msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich. Does <b>not</b> Malkovich Malkovich Malkovich Malkovich.)"
-
-#: html/Ticket/Create.html:68
-msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich. Malkovich <b>will</b> Malkovich Malkovich.)"
-
-#: html/Admin/Groups/index.html:32 html/User/Groups/index.html:32
-msgid "(empty)"
-msgstr "(Malkovich)"
-
-#: html/Admin/Users/index.html:38
-msgid "(no name listed)"
-msgstr "(no Malkovich)"
-
-#: html/Admin/Elements/SelectRights:47 html/Elements/SelectCustomFieldValue:29 html/Ticket/Elements/EditCustomField:64 html/Ticket/Elements/ShowCustomFields:35 lib/RT/Transaction_Overlay.pm:511
-msgid "(no value)"
-msgstr "(no Malkovich)"
-
-#: html/Elements/EditLinks:105 html/Ticket/Elements/BulkLinks:27
-msgid "(only one ticket)"
-msgstr "(Malkovich)"
-
-#: html/Elements/TicketList:167
-msgid "(pending approval)"
-msgstr "(Malkovich Malkovich)"
-
-#: html/Elements/TicketList:170
-msgid "(pending other Collection)"
-msgstr "(Malkovich Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "(pending other tickets)"
-msgstr "(Malkovich Malkovich)"
-
-#: html/Admin/Users/Modify.html:49
-msgid "(required)"
-msgstr "(Malkovich)"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:60
-msgid "(untitled)"
-msgstr "(Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "..."
-msgstr "..."
-
-#: html/Ticket/Elements/ShowBasics:31
-msgid "<% $Ticket->Status%>"
-msgstr "<% $Ticket->Status %>"
-
-#: html/Elements/SelectTicketTypes:26
-msgid "<% $_ %>"
-msgstr "<% $_ %>"
-
-#: docs/design_docs/string-extraction-guide.txt:54 html/Elements/CreateTicket:25 lib/RT/StyleGuide.pod:767
-#. ($m->scomp('/Elements/SelectNewTicketQueue'))
-msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
-msgstr "<input type=\"submit\" value=\"Malkovich in\">&nbsp;%1"
-
-#: etc/initialdata:218
-msgid "A blank template"
-msgstr "A Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:156 lib/RT/Principal_Overlay.pm:180
-msgid "ACE not found"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:830
-msgid "ACEs can only be created and deleted."
-msgstr "Malkovich be Malkovich and Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "Aborting to avoid unintended ticket modifications.\\n"
-msgstr "Malkovich to Malkovich Malkovich Malkovich Malkovich.\\n"
-
-#: html/User/Elements/Tabs:31
-msgid "About me"
-msgstr "Malkovich me"
-
-#: html/Admin/Users/Modify.html:79
-msgid "Access control"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:49
-msgid "Action"
-msgstr "Malkovich"
-
-#: lib/RT/Scrip_Overlay.pm:148
-#. ($args{'ScripAction'})
-msgid "Action %1 not found"
-msgstr "Malkovich %1 Malkovich"
-
-#: bin/rt-crontool:119
-msgid "Action committed."
-msgstr "Malkovich Malkovich."
-
-#: bin/rt-crontool:115
-msgid "Action prepared..."
-msgstr "Malkovich..."
-
-#: html/Search/Bulk.html:93
-msgid "Add AdminCc"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:89
-msgid "Add Cc"
-msgstr "Add Cc"
-
-#: html/Ticket/Create.html:113 html/Ticket/Update.html:81
-msgid "Add More Files"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:85
-msgid "Add Requestor"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:24
-msgid "Add Value"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:54
-msgid "Add a scrip which will apply to all queues"
-msgstr "Add a Malkovich Malkovich to Malkovich"
-
-#: html/Search/Bulk.html:125
-msgid "Add comments or replies to selected tickets"
-msgstr "Malkovich or Malkovich to Malkovich Malkovich"
-
-#: html/Admin/Groups/Members.html:41 html/User/Groups/Members.html:38
-msgid "Add members"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:65 html/Ticket/Elements/AddWatchers:27
-msgid "Add new watchers"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:684
-#. ($args{'Type'})
-msgid "Added principal as a %1 for this queue"
-msgstr "Malkovich as a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1547
-#. ($self->loc($args{'Type'}))
-msgid "Added principal as a %1 for this ticket"
-msgstr "Malkovich as a %1 Malkovich"
-
-#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:111
-msgid "Address1"
-msgstr "Malkovich1"
-
-#: html/Admin/Users/Modify.html:124 html/User/Prefs.html:115
-msgid "Address2"
-msgstr "Malkovich2"
-
-#: html/Ticket/Create.html:73
-msgid "Admin Cc"
-msgstr "Malkovich Cc"
-
-#: etc/initialdata:295
-msgid "Admin Comment"
-msgstr "Malkovich"
-
-#: etc/initialdata:274
-msgid "Admin Correspondence"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/index.html:24 html/Admin/Queues/index.html:27
-msgid "Admin queues"
-msgstr "Malkovich"
-
-#: html/Admin/Global/index.html:25 html/Admin/Global/index.html:27
-msgid "Admin/Global configuration"
-msgstr "Malkovich/Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Admin/Queue/Basics"
-msgstr "Malkovich/Malkovich/Malkovich"
-
-#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:38 lib/RT/ACE_Overlay.pm:88
-msgid "AdminCc"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:73
-msgid "AdminCustomFields"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Group_Overlay.pm:146
-msgid "AdminGroup"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:148
-msgid "AdminGroupMembership"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/System.pm:58
-msgid "AdminOwnPersonalGroups"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:69
-msgid "AdminQueue"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:59
-msgid "AdminUsers"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:47 html/Ticket/Elements/EditPeople:53
-msgid "Administrative Cc"
-msgstr "Malkovich Cc"
-
-#: html/Elements/SelectDateRelation:35
-msgid "After"
-msgstr "Malkovich"
-
-#: etc/initialdata:363
-msgid "All Approvals Passed"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:94
-msgid "All Custom Fields"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/index.html:52
-msgid "All Queues"
-msgstr "Malkovich"
-
-#: html/Elements/Tabs:58
-msgid "Approval"
-msgstr "Malkovich"
-
-#: html/Approvals/Display.html:45 html/Approvals/Elements/ShowDependency:41 html/Approvals/index.html:64
-#. ($Ticket->Id, $Ticket->Subject)
-#. ($ticket->id, $msg)
-#. ($link->BaseObj->Id, $link->BaseObj->Subject)
-msgid "Approval #%1: %2"
-msgstr "Malkovich #%1: %2"
-
-#: html/Approvals/index.html:53
-#. ($ticket->Id)
-msgid "Approval #%1: Notes not recorded due to a system error"
-msgstr "Malkovich #%1: Malkovich Malkovich to a Malkovich"
-
-#: html/Approvals/index.html:51
-#. ($ticket->Id)
-msgid "Approval #%1: Notes recorded"
-msgstr "Malkovich #%1: Malkovich"
-
-#: etc/initialdata:351
-msgid "Approval Passed"
-msgstr "Malkovich"
-
-#: etc/initialdata:374
-msgid "Approval Rejected"
-msgstr "Malkovich Malkovich"
-
-#: html/Approvals/Elements/Approve:43
-msgid "Approve"
-msgstr "Malkovich"
-
-#: etc/initialdata:504
-msgid "Approver's notes: %1"
-msgstr "Malkovich's Malkovich: %1"
-
-#: lib/RT/Date.pm:414
-msgid "Apr."
-msgstr "Apr."
-
-#: html/Elements/SelectSortOrder:34 html/Search/Elements/DisplayOptions:52
-msgid "Ascending"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:134 html/SelfService/Update.html:47 html/Ticket/ModifyAll.html:82 html/Ticket/Update.html:81
-msgid "Attach"
-msgstr "Malkovich"
-
-#: html/SelfService/Create.html:64 html/Ticket/Create.html:109
-msgid "Attach file"
-msgstr "Malkovich"
-
-#: html/SelfService/Update.html:36 html/Ticket/Create.html:97 html/Ticket/Update.html:70
-msgid "Attached file"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:416
-msgid "Attachment created"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1251
-msgid "Attachment filename"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowAttachments:25
-msgid "Attachments"
-msgstr "Malkovich"
-
-#: lib/RT/Attributes_Overlay.pm:158
-msgid "Attribute Deleted"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Date.pm:418
-msgid "Aug."
-msgstr "Aug."
-
-#: NOT FOUND IN SOURCE
-msgid "AuthSystem"
-msgstr "Malkovich"
-
-#: etc/initialdata:221
-msgid "Autoreply"
-msgstr "Malkovich"
-
-#: etc/initialdata:72
-msgid "Autoreply To Requestors"
-msgstr "Malkovich To Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Bad data in %1"
-msgstr "Malkovich in %1"
-
-#: html/Admin/Elements/GroupTabs:38 html/Admin/Elements/QueueTabs:38 html/Admin/Elements/UserTabs:37 html/Ticket/Elements/Tabs:91 html/User/Elements/GroupTabs:37
-msgid "Basics"
-msgstr "Malkovich"
-
-#: html/Ticket/Update.html:64
-msgid "Bcc"
-msgstr "Bcc"
-
-#: html/Admin/Elements/EditScrip:73
-msgid "Be sure to save your changes"
-msgstr "Be sure to Malkovich Malkovich"
-
-#: html/Elements/SelectDateRelation:33 lib/RT/CurrentUser.pm:336
-msgid "Before"
-msgstr "Malkovich"
-
-#: etc/initialdata:217
-msgid "Blank"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/ShowHistory:38 html/Ticket/Elements/ShowHistory:44
-msgid "Brief headers"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:24 html/Search/Bulk.html:25
-msgid "Bulk ticket update"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:1533
-msgid "Can not modify system users"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:68
-msgid "Can this principal see this queue"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:211
-msgid "Can't add a custom field value without a name"
-msgstr "Can't add a Malkovich Malkovich Malkovich a name"
-
-#: lib/RT/Link_Overlay.pm:126
-msgid "Can't link a ticket to itself"
-msgstr "Can't link a Malkovich to Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2716
-msgid "Can't merge into a merged ticket. You should never get this error"
-msgstr "Can't Malkovich a Malkovich. Malkovich Malkovich Malkovich"
-
-#: lib/RT/Record.pm:1060 lib/RT/Record.pm:1138
-msgid "Can't specifiy both base and target"
-msgstr "Can't Malkovich Malkovich and Malkovich"
-
-#: html/autohandler:132
-#. ($msg)
-msgid "Cannot create user: %1"
-msgstr "Malkovich Malkovich: %1"
-
-#: etc/initialdata:50 html/Admin/Queues/People.html:43 html/SelfService/Create.html:48 html/Ticket/Create.html:63 html/Ticket/Elements/EditPeople:50 html/Ticket/Elements/ShowPeople:34 html/Ticket/Update.html:59 lib/RT/ACE_Overlay.pm:87
-msgid "Cc"
-msgstr "Cc"
-
-#: html/SelfService/Prefs.html:30
-msgid "Change password"
-msgstr "Malkovich"
-
-#: html/SelfService/Update.html:39 html/Ticket/Create.html:100 html/Ticket/Update.html:73
-msgid "Check box to delete"
-msgstr "Malkovich to Malkovich"
-
-#: html/Admin/Elements/SelectRights:30
-msgid "Check box to revoke right"
-msgstr "Malkovich to Malkovich"
-
-#: html/Elements/EditLinks:121 html/Elements/EditLinks:63 html/Elements/ShowLinks:56 html/Ticket/Create.html:183 html/Ticket/Elements/BulkLinks:42
-msgid "Children"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:129 html/User/Prefs.html:119
-msgid "City"
-msgstr "City"
-
-#: html/Ticket/Elements/ShowDates:47
-msgid "Closed"
-msgstr "Malkovich"
-
-#: html/SelfService/Closed.html:24
-msgid "Closed Tickets"
-msgstr "Malkovich"
-
-#: html/SelfService/Elements/Tabs:44
-msgid "Closed tickets"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/ShowTransaction:152 html/Ticket/Elements/Tabs:154
-msgid "Comment"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Modify.html:57
-msgid "Comment Address"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:88
-msgid "Comment on tickets"
-msgstr "Malkovich on Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:88
-msgid "CommentOnTicket"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Comments"
-msgstr "Malkovich"
-
-#: html/Ticket/ModifyAll.html:69 html/Ticket/Update.html:51
-msgid "Comments (Not sent to requestors)"
-msgstr "Malkovich (Malkovich to Malkovich)"
-
-#: html/Search/Bulk.html:129
-msgid "Comments (not sent to requestors)"
-msgstr "Malkovich (Malkovich to Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "Comments about %1"
-msgstr "Malkovich %1"
-
-#: html/Admin/Users/Modify.html:182 html/Ticket/Elements/ShowRequestor:45
-msgid "Comments about this user"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:537
-msgid "Comments added"
-msgstr "Malkovich"
-
-#: lib/RT/Action/Generic.pm:149
-msgid "Commit Stubbed"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:41
-msgid "Condition"
-msgstr "Malkovich"
-
-#: bin/rt-crontool:105
-msgid "Condition matches..."
-msgstr "Malkovich Malkovich..."
-
-#: lib/RT/Scrip_Overlay.pm:164
-msgid "Condition not found"
-msgstr "Malkovich Malkovich"
-
-#: html/Elements/Tabs:52
-msgid "Configuration"
-msgstr "Malkovich"
-
-#: html/SelfService/Prefs.html:32
-msgid "Confirm"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "ContactInfoSystem"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/ModifyTemplate:43 html/Elements/SelectAttachmentField:26 html/Ticket/ModifyAll.html:86
-msgid "Content"
-msgstr "Malkovich"
-
-#: etc/initialdata:286
-msgid "Correspondence"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Correspondence Address"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:533
-msgid "Correspondence added"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3471
-msgid "Could not add new custom field value for ticket. "
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich. "
-
-#: lib/RT/Ticket_Overlay.pm:2967 lib/RT/Ticket_Overlay.pm:2975 lib/RT/Ticket_Overlay.pm:2992
-msgid "Could not change owner. "
-msgstr "Malkovich Malkovich. "
-
-#: html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditCustomFields:164
-#. ($msg)
-msgid "Could not create CustomField"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/User/Groups/Modify.html:76 lib/RT/Group_Overlay.pm:474 lib/RT/Group_Overlay.pm:481
-msgid "Could not create group"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Global/Template.html:74 html/Admin/Queues/Template.html:71
-#. ($msg)
-msgid "Could not create template: %1"
-msgstr "Malkovich Malkovich: %1"
-
-#: lib/RT/Ticket_Overlay.pm:1185 lib/RT/Ticket_Overlay.pm:364
-msgid "Could not create ticket. Queue not set"
-msgstr "Malkovich Malkovich. Malkovich"
-
-#: lib/RT/User_Overlay.pm:226 lib/RT/User_Overlay.pm:240 lib/RT/User_Overlay.pm:249 lib/RT/User_Overlay.pm:258 lib/RT/User_Overlay.pm:267 lib/RT/User_Overlay.pm:281 lib/RT/User_Overlay.pm:291 lib/RT/User_Overlay.pm:462
-msgid "Could not create user"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:662 lib/RT/Ticket_Overlay.pm:1515
-msgid "Could not find or create that user"
-msgstr "Malkovich or Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:723 lib/RT/Ticket_Overlay.pm:1596
-msgid "Could not find that principal"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Groups/Members.html:87 html/User/Groups/Members.html:89 html/User/Groups/Modify.html:81
-msgid "Could not load group"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:682
-#. ($args{'Type'})
-msgid "Could not make that principal a %1 for this queue"
-msgstr "Malkovich Malkovich Malkovich a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1536
-#. ($self->loc($args{'Type'}))
-msgid "Could not make that principal a %1 for this ticket"
-msgstr "Malkovich Malkovich Malkovich a %1 Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:781
-#. ($args{'Type'})
-msgid "Could not remove that principal as a %1 for this queue"
-msgstr "Malkovich Malkovich Malkovich as a %1 Malkovich"
-
-#: lib/RT/Group_Overlay.pm:977
-msgid "Couldn't add member to group"
-msgstr "Couldn't Malkovich to Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3481 lib/RT/Ticket_Overlay.pm:3537
-#. ($Msg)
-msgid "Couldn't create a transaction: %1"
-msgstr "Couldn't Malkovich a Malkovich: %1"
-
-#: lib/RT/Record.pm:748
-msgid "Couldn't find row"
-msgstr "Couldn't Malkovich"
-
-#: lib/RT/Group_Overlay.pm:951
-msgid "Couldn't find that principal"
-msgstr "Couldn't Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:245
-msgid "Couldn't find that value"
-msgstr "Couldn't Malkovich"
-
-#: lib/RT/CurrentUser.pm:123
-#. ($self->Id)
-msgid "Couldn't load %1 from the users database.\\n"
-msgstr "Couldn't load %1 from the Malkovich.\\n"
-
-#: html/Admin/Groups/GroupRights.html:87 html/Admin/Groups/UserRights.html:74
-#. ($id)
-msgid "Couldn't load group %1"
-msgstr "Couldn't Malkovich %1"
-
-#: lib/RT/Link_Overlay.pm:169 lib/RT/Link_Overlay.pm:178 lib/RT/Link_Overlay.pm:205
-msgid "Couldn't load link"
-msgstr "Couldn't Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:145 html/Admin/Queues/CustomFields.html:35 html/Admin/Queues/People.html:120
-#. ($id)
-msgid "Couldn't load queue"
-msgstr "Couldn't Malkovich"
-
-#: html/Admin/Queues/GroupRights.html:100 html/Admin/Queues/UserRights.html:71
-#. ($id)
-msgid "Couldn't load queue %1"
-msgstr "Couldn't Malkovich %1"
-
-#: NOT FOUND IN SOURCE
-msgid "Couldn't load that user (%1)"
-msgstr "Couldn't Malkovich (%1)"
-
-#: html/SelfService/Display.html:116
-#. ($id)
-msgid "Couldn't load ticket '%1'"
-msgstr "Couldn't Malkovich '%1'"
-
-#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:131
-msgid "Country"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/CreateUserCalled:25 html/Admin/Elements/EditCustomField:62 html/Admin/Elements/EditScrip:110 html/Admin/Groups/Modify.html:55 html/Admin/Queues/Template.html:44 html/Elements/QuickCreate:23 html/Ticket/Create.html:134 html/Ticket/Create.html:195 html/User/Groups/Modify.html:55
-msgid "Create"
-msgstr "Malkovich"
-
-#: etc/initialdata:135
-msgid "Create Tickets"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:74
-msgid "Create a CustomField"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Queues/CustomField.html:47
-#. ($QueueObj->Name())
-msgid "Create a CustomField for queue %1"
-msgstr "Malkovich a Malkovich Malkovich %1"
-
-#: html/Admin/Global/CustomField.html:47
-msgid "Create a CustomField which applies to all queues"
-msgstr "Malkovich a Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Groups/Modify.html:66 html/Admin/Groups/Modify.html:92
-msgid "Create a new group"
-msgstr "Malkovich a Malkovich"
-
-#: html/User/Groups/Modify.html:66 html/User/Groups/Modify.html:91
-msgid "Create a new personal group"
-msgstr "Malkovich a Malkovich Malkovich"
-
-#: html/Ticket/Create.html:24 html/Ticket/Create.html:27 html/Ticket/Create.html:35
-msgid "Create a new ticket"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Users/Modify.html:211 html/Admin/Users/Modify.html:268
-msgid "Create a new user"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Queues/Modify.html:103
-msgid "Create a queue"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Queues/Scrip.html:58
-#. ($QueueObj->Name)
-msgid "Create a scrip for queue %1"
-msgstr "Malkovich a Malkovich %1"
-
-#: html/Admin/Global/Template.html:68 html/Admin/Queues/Template.html:64
-msgid "Create a template"
-msgstr "Malkovich a Malkovich"
-
-#: html/SelfService/Create.html:24
-msgid "Create a ticket"
-msgstr "Malkovich a Malkovich"
-
-#: etc/initialdata:137
-msgid "Create new tickets based on this scrip's template"
-msgstr "Malkovich Malkovich on Malkovich's Malkovich"
-
-#: html/SelfService/Create.html:77
-msgid "Create ticket"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:86
-msgid "Create tickets in this queue"
-msgstr "Malkovich in Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:73
-msgid "Create, delete and modify custom fields"
-msgstr "Malkovich, Malkovich and Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:69
-msgid "Create, delete and modify queues"
-msgstr "Malkovich, Malkovich and Malkovich"
-
-#: lib/RT/System.pm:58
-msgid "Create, delete and modify the members of personal groups"
-msgstr "Malkovich, Malkovich and Malkovich the Malkovich of Malkovich"
-
-#: lib/RT/System.pm:59
-msgid "Create, delete and modify users"
-msgstr "Malkovich, Malkovich and Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:86
-msgid "CreateTicket"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:25 html/Ticket/Elements/ShowDates:27 lib/RT/Ticket_Overlay.pm:1279
-msgid "Created"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:87
-#. ($CustomFieldObj->Name())
-msgid "Created CustomField %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Elements/EditLinks:27
-msgid "Current Links"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrips:29
-msgid "Current Scrips"
-msgstr "Malkovich"
-
-#: html/Admin/Groups/Members.html:38 html/User/Groups/Members.html:41
-msgid "Current members"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectRights:28
-msgid "Current rights"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Current search criteria"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/People.html:40 html/Ticket/Elements/EditPeople:44
-msgid "Current watchers"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Global/CustomField.html:54
-#. ($CustomField)
-msgid "Custom Field #%1"
-msgstr "Malkovich #%1"
-
-#: html/Admin/Elements/QueueTabs:52 html/Admin/Elements/SystemTabs:39 html/Admin/Global/index.html:49 html/Ticket/Elements/ShowSummary:35
-msgid "Custom Fields"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:101
-msgid "Custom action cleanup code"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrip:93
-msgid "Custom action preparation code"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrip:85
-msgid "Custom condition"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1693
-#. ($CF->Name , $args{OPERATOR} , $args{VALUE})
-msgid "Custom field %1 %2 %3"
-msgstr "Malkovich %1 %2 %3"
-
-#: lib/RT/Tickets_Overlay.pm:1688
-#. ($CF->Name)
-msgid "Custom field %1 has a value."
-msgstr "Malkovich %1 has a Malkovich."
-
-#: lib/RT/Tickets_Overlay.pm:1685
-#. ($CF->Name)
-msgid "Custom field %1 has no value."
-msgstr "Malkovich %1 has no Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:3373
-#. ($args{'Field'})
-msgid "Custom field %1 not found"
-msgstr "Malkovich %1 Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:195
-msgid "Custom field deleted"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3523
-msgid "Custom field not found"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:355
-#. ($args{'Content'}, $self->Name)
-msgid "Custom field value %1 could not be found for custom field %2"
-msgstr "Malkovich Malkovich %1 Malkovich be Malkovich Malkovich %2"
-
-#: lib/RT/CustomField_Overlay.pm:255
-msgid "Custom field value could not be deleted"
-msgstr "Malkovich Malkovich Malkovich be Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:361
-msgid "Custom field value could not be found"
-msgstr "Malkovich Malkovich Malkovich be Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:253 lib/RT/CustomField_Overlay.pm:363
-msgid "Custom field value deleted"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:541
-msgid "CustomField"
-msgstr "Malkovich"
-
-#: html/SelfService/Display.html:38 html/Ticket/Create.html:160 html/Ticket/Elements/ShowSummary:54 html/Ticket/Elements/Tabs:94 html/Ticket/ModifyAll.html:43
-msgid "Dates"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:422
-msgid "Dec."
-msgstr "Dec."
-
-#: etc/initialdata:222
-msgid "Default Autoresponse template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:296
-msgid "Default admin comment template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:287
-msgid "Default correspondence template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:253
-msgid "Default transaction template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:519
-#. ($type, $self->Field, $self->OldValue, $self->NewValue)
-msgid "Default: %1/%2 changed from %3 to %4"
-msgstr "Malkovich: %1/%2 Malkovich %3 to %4"
-
-#: html/User/Delegation.html:24 html/User/Delegation.html:27
-msgid "Delegate rights"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:62
-msgid "Delegate specific rights which have been granted to you."
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich to you."
-
-#: lib/RT/System.pm:62
-msgid "DelegateRights"
-msgstr "Malkovich"
-
-#: html/User/Elements/Tabs:37
-msgid "Delegation"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrips:53 html/Search/Elements/EditFormat:66 html/Search/Elements/EditSearches:15
-msgid "Delete"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrips:52
-msgid "Delete selected scrips"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:91
-msgid "Delete tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:91
-msgid "DeleteTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:162
-msgid "Deleting this object could break referential integrity"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:329
-msgid "Deleting this object would break referential integrity"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:478
-msgid "Deleting this object would violate referential integrity"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich Malkovich"
-
-#: html/Approvals/Elements/Approve:44
-msgid "Deny"
-msgstr "Deny"
-
-#: html/Elements/EditLinks:113 html/Elements/EditLinks:44 html/Elements/ShowLinks:36 html/Ticket/Create.html:181 html/Ticket/Elements/BulkLinks:34 html/Ticket/Elements/ShowDependencies:31
-msgid "Depended on by"
-msgstr "Malkovich on by"
-
-#: lib/RT/Transaction_Overlay.pm:621
-#. ($value)
-msgid "Dependency by %1 added"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:661
-#. ($value)
-msgid "Dependency by %1 deleted"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:618
-#. ($value)
-msgid "Dependency on %1 added"
-msgstr "Malkovich on %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:658
-#. ($value)
-msgid "Dependency on %1 deleted"
-msgstr "Malkovich on %1 Malkovich"
-
-#: html/Elements/EditLinks:109 html/Elements/EditLinks:35 html/Elements/SelectLinkType:26 html/Elements/ShowLinks:26 html/Ticket/Create.html:180 html/Ticket/Elements/BulkLinks:30 html/Ticket/Elements/ShowDependencies:24
-msgid "Depends on"
-msgstr "Malkovich on"
-
-#: html/Elements/SelectSortOrder:34 html/Search/Elements/DisplayOptions:57
-msgid "Descending"
-msgstr "Malkovich"
-
-#: html/SelfService/Create.html:72 html/Ticket/Create.html:118
-msgid "Describe the issue below"
-msgstr "Malkovich the Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:35 html/Admin/Elements/EditCustomField:38 html/Admin/Elements/EditScrip:34 html/Admin/Elements/ModifyTemplate:35 html/Admin/Groups/Modify.html:48 html/Admin/Queues/Modify.html:47 html/Elements/SelectGroups:26 html/Search/Elements/EditSearches:8 html/User/Groups/Modify.html:48
-msgid "Description"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:86
-msgid "Display"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:70
-msgid "Display Access Control List"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:76
-msgid "Display Scrip templates for this queue"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:79
-msgid "Display Scrips for this queue"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowHistory:34
-msgid "Display mode"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:53
-msgid "Do anything and everything"
-msgstr "Do Malkovich and Malkovich"
-
-#: html/Elements/Refresh:29
-msgid "Don't refresh this page."
-msgstr "Don't Malkovich Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "Don't show search results"
-msgstr "Don't Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:60
-msgid "Download"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Download all the tickets as a tab delimited file"
-msgstr "Malkovich the Malkovich as a Malkovich Malkovich"
-
-#: html/Elements/SelectDateType:31 html/Ticket/Create.html:166 html/Ticket/Elements/EditDates:44 html/Ticket/Elements/ShowDates:43 lib/RT/Ticket_Overlay.pm:1283
-msgid "Due"
-msgstr "Due"
-
-#: NOT FOUND IN SOURCE
-msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
-msgstr "MALKOVICH: Couldn't Malkovich '%1': %2.\\n"
-
-#: html/Admin/Queues/CustomFields.html:45
-#. ($Queue->Name)
-msgid "Edit Custom Fields for %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Search/Bulk.html:141 html/Ticket/ModifyLinks.html:35
-msgid "Edit Links"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/Templates.html:41
-#. ($QueueObj->Name)
-msgid "Edit Templates for queue %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Admin/Global/index.html:45
-msgid "Edit system templates"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/Modify.html:118
-#. ($QueueObj->Name)
-msgid "Editing Configuration for queue %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: NOT FOUND IN SOURCE
-msgid "Editing Configuration for user %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/Admin/Elements/EditCustomField:90
-#. ($CustomFieldObj->Name())
-msgid "Editing CustomField %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Admin/Groups/Members.html:31
-#. ($Group->Name)
-msgid "Editing membership for group %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/User/Groups/Members.html:128
-#. ($Group->Name)
-msgid "Editing membership for personal group %1"
-msgstr "Malkovich Malkovich Malkovich Malkovich %1"
-
-#: lib/RT/Record.pm:1075 lib/RT/Record.pm:1152
-msgid "Either base or target must be specified"
-msgstr "Malkovich or Malkovich be Malkovich"
-
-#: html/Admin/Users/Modify.html:52 html/Elements/SelectUsers:26 html/Ticket/Elements/AddWatchers:55 html/User/Prefs.html:43
-msgid "Email"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:206
-msgid "Email address in use"
-msgstr "Malkovich in use"
-
-#: NOT FOUND IN SOURCE
-msgid "EmailAddress"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "EmailEncoding"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:50
-msgid "Enabled (Unchecking this box disables this custom field)"
-msgstr "Malkovich (Malkovich Malkovich Malkovich Malkovich Malkovich)"
-
-#: html/Admin/Groups/Modify.html:52 html/User/Groups/Modify.html:52
-msgid "Enabled (Unchecking this box disables this group)"
-msgstr "Malkovich (Malkovich Malkovich Malkovich Malkovich)"
-
-#: html/Admin/Queues/Modify.html:83
-msgid "Enabled (Unchecking this box disables this queue)"
-msgstr "Malkovich (Malkovich Malkovich Malkovich Malkovich)"
-
-#: html/Admin/Elements/EditCustomFields:97
-msgid "Enabled Custom Fields"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/index.html:55
-msgid "Enabled Queues"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:106 html/Admin/Groups/Modify.html:116 html/Admin/Queues/Modify.html:140 html/Admin/Users/Modify.html:308 html/User/Groups/Modify.html:116
-#. (loc_fuzzy($msg))
-msgid "Enabled status %1"
-msgstr "Malkovich %1"
-
-#: lib/RT/CustomField_Overlay.pm:433
-msgid "Enter multiple values"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:430
-msgid "Enter one value"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:142
-msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
-msgstr "Malkovich or URIs to Malkovich to. Malkovich Malkovich Malkovich Malkovich."
-
-#: html/Elements/Login:39 html/SelfService/Error.html:24 html/SelfService/Error.html:25
-msgid "Error"
-msgstr "Error"
-
-#: lib/RT/Queue_Overlay.pm:593
-msgid "Error in parameters to Queue->AddWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Error in parameters to Queue->DelWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1468
-msgid "Error in parameters to Ticket->AddWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Error in parameters to Ticket->DelWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: etc/initialdata:20
-msgid "Everyone"
-msgstr "Malkovich"
-
-#: bin/rt-crontool:190
-msgid "Example:"
-msgstr "Malkovich:"
-
-#: NOT FOUND IN SOURCE
-msgid "ExternalAuthId"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "ExternalContactInfoId"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:72
-msgid "Extra info"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:342
-msgid "Failed to find 'Privileged' users pseudogroup."
-msgstr "Malkovich to find 'Malkovich' Malkovich Malkovich."
-
-#: lib/RT/User_Overlay.pm:349
-msgid "Failed to find 'Unprivileged' users pseudogroup"
-msgstr "Malkovich to find 'Malkovich' Malkovich Malkovich"
-
-#: bin/rt-crontool:134
-#. ($modname, $@)
-msgid "Failed to load module %1. (%2)"
-msgstr "Malkovich to Malkovich %1. (%2)"
-
-#: lib/RT/Date.pm:412
-msgid "Feb."
-msgstr "Feb."
-
-#: html/Search/Elements/PickBasics:60 html/Ticket/Create.html:154 html/Ticket/Elements/EditBasics:57 lib/RT/Tickets_Overlay.pm:1153
-msgid "Final Priority"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1274
-msgid "FinalPriority"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:60 html/Ticket/Elements/EditPeople:33
-msgid "Find group whose"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/People.html:56 html/Admin/Users/index.html:45 html/Ticket/Elements/EditPeople:29
-msgid "Find people whose"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Results.html:72
-msgid "Find tickets"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:59
-msgid "First"
-msgstr "Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:746
-msgid "Foo Bar Baz"
-msgstr "Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:737
-msgid "Foo!"
-msgstr "Foo!"
-
-#: html/Search/Bulk.html:84
-msgid "Force change"
-msgstr "Malkovich"
-
-#: html/Search/Results.html:70
-#. ($ticketcount)
-msgid "Found %quant(%1,ticket)"
-msgstr "Malkovich %quant(%1,Malkovich)"
-
-#: lib/RT/Record.pm:750
-msgid "Found Object"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "FreeformContactInfo"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:37
-msgid "FreeformMultiple"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:36
-msgid "FreeformSingle"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:392
-msgid "Fri."
-msgstr "Fri."
-
-#: html/Ticket/Elements/ShowHistory:40 html/Ticket/Elements/ShowHistory:50
-msgid "Full headers"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:587
-#. ($New->Name)
-msgid "Given to %1"
-msgstr "Malkovich to %1"
-
-#: html/Admin/Elements/Tabs:40 html/Admin/index.html:37
-msgid "Global"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectTemplate:37
-#. (loc($Template->Name))
-msgid "Global template: %1"
-msgstr "Malkovich: %1"
-
-#: html/Tools/Offline.html:69
-msgid "Go"
-msgstr "Go"
-
-#: html/Admin/Elements/EditCustomFields:73 html/Admin/Groups/index.html:39 html/Admin/Queues/People.html:58 html/Admin/Queues/People.html:62 html/Admin/Queues/index.html:43 html/Admin/Users/index.html:48 html/Ticket/Elements/EditPeople:31 html/Ticket/Elements/EditPeople:35 html/index.html:69
-msgid "Go!"
-msgstr "Go!"
-
-#: html/Elements/GotoTicket:24 html/SelfService/Elements/GotoTicket:24
-msgid "Goto ticket"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/AddWatchers:45 html/Ticket/Elements/ShowGroupMembers:33 html/User/Elements/DelegateRights:77
-msgid "Group"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/GroupTabs:44 html/Admin/Elements/QueueTabs:56 html/Admin/Elements/SystemTabs:43 html/Admin/Global/index.html:54
-msgid "Group Rights"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:957
-msgid "Group already has member"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Groups/Modify.html:76
-#. ($create_msg)
-msgid "Group could not be created: %1"
-msgstr "Malkovich be Malkovich: %1"
-
-#: lib/RT/Group_Overlay.pm:497
-msgid "Group created"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:1129
-msgid "Group has no such member"
-msgstr "Malkovich no Malkovich"
-
-#: lib/RT/Group_Overlay.pm:937 lib/RT/Queue_Overlay.pm:669 lib/RT/Queue_Overlay.pm:729 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1602
-msgid "Group not found"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectNewGroupMembers:34 html/Admin/Elements/Tabs:34 html/Admin/Groups/Members.html:63 html/Admin/Queues/People.html:82 html/Admin/index.html:31 html/User/Groups/Members.html:66
-msgid "Groups"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:963
-msgid "Groups can't be members of their members"
-msgstr "Malkovich can't be Malkovich of Malkovich"
-
-#: lib/RT/Interface/CLI.pm:72 lib/RT/Interface/CLI.pm:72
-msgid "Hello!"
-msgstr "Malkovich!"
-
-#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:753
-#. ($name)
-msgid "Hello, %1"
-msgstr "Malkovich, %1"
-
-#: html/Ticket/Elements/ShowHistory:29 html/Ticket/Elements/Tabs:89
-msgid "History"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "HomePhone"
-msgstr "Malkovich"
-
-#: html/Elements/Tabs:43
-msgid "Homepage"
-msgstr "Malkovich"
-
-#: lib/RT/Base.pm:86
-#. (6)
-msgid "I have %quant(%1,concrete mixer)."
-msgstr "I have %quant(%1,Malkovich)."
-
-#: html/Search/Elements/PickBasics:104 html/Ticket/Elements/ShowBasics:26 lib/RT/Tickets_Overlay.pm:1080
-msgid "Id"
-msgstr "Id"
-
-#: html/Admin/Users/Modify.html:43 html/User/Prefs.html:38
-msgid "Identity"
-msgstr "Malkovich"
-
-#: etc/initialdata:429
-msgid "If an approval is rejected, reject the original and delete pending approvals"
-msgstr "If a Malkovich is Malkovich, Malkovich the Malkovich and Malkovich Malkovich"
-
-#: bin/rt-crontool:186
-msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
-msgstr "If Malkovich Malkovich, a Malkovich Malkovich Malkovich Malkovich to Malkovich Malkovich Malkovich to RT."
-
-#: html/Admin/Queues/People.html:104 html/Ticket/Modify.html:38 html/Ticket/ModifyAll.html:93 html/Ticket/ModifyPeople.html:37
-msgid "If you've updated anything above, be sure to"
-msgstr "If you've Malkovich Malkovich, be sure to"
-
-#: lib/RT/Record.pm:742
-msgid "Illegal value for %1"
-msgstr "Malkovich Malkovich %1"
-
-#: lib/RT/Record.pm:745
-msgid "Immutable field"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:72
-msgid "Include disabled custom fields in listing."
-msgstr "Malkovich Malkovich Malkovich in Malkovich."
-
-#: html/Admin/Queues/index.html:42
-msgid "Include disabled queues in listing."
-msgstr "Malkovich Malkovich in Malkovich."
-
-#: html/Admin/Users/index.html:46
-msgid "Include disabled users in search."
-msgstr "Malkovich Malkovich in Malkovich."
-
-#: html/Search/Elements/PickBasics:59 lib/RT/Tickets_Overlay.pm:1129
-msgid "Initial Priority"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1273 lib/RT/Ticket_Overlay.pm:1275
-msgid "InitialPriority"
-msgstr "Malkovich"
-
-#: lib/RT/ScripAction_Overlay.pm:97
-msgid "Input error"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3797
-msgid "Internal Error"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:186
-#. ($id->{error_message})
-msgid "Internal Error: %1"
-msgstr "Malkovich: %1"
-
-#: lib/RT/Group_Overlay.pm:644
-msgid "Invalid Group Type"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Principal_Overlay.pm:127
-msgid "Invalid Right"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:747
-msgid "Invalid data"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Invalid owner. Defaulting to 'nobody'."
-msgstr "Malkovich. Malkovich to 'Malkovich'."
-
-#: lib/RT/Scrip_Overlay.pm:133 lib/RT/Template_Overlay.pm:251
-msgid "Invalid queue"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:243 lib/RT/ACE_Overlay.pm:252 lib/RT/ACE_Overlay.pm:258 lib/RT/ACE_Overlay.pm:269 lib/RT/ACE_Overlay.pm:274
-msgid "Invalid right"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:161
-#. ($key)
-msgid "Invalid value for %1"
-msgstr "Malkovich Malkovich %1"
-
-#: lib/RT/Ticket_Overlay.pm:3380
-msgid "Invalid value for custom field"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:385
-msgid "Invalid value for status"
-msgstr "Malkovich Malkovich"
-
-#: bin/rt-crontool:187
-msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
-msgstr "It is Malkovich Malkovich Malkovich Malkovich Malkovich be Malkovich to Malkovich."
-
-#: bin/rt-crontool:188
-msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
-msgstr "It is Malkovich Malkovich a non-Malkovich Malkovich the Malkovich Malkovich and RT Malkovich to Malkovich."
-
-#: bin/rt-crontool:159
-msgid "It takes several arguments:"
-msgstr "It Malkovich Malkovich:"
-
-#: lib/RT/Date.pm:411
-msgid "Jan."
-msgstr "Jan."
-
-#: lib/RT/Group_Overlay.pm:149
-msgid "Join or leave this group"
-msgstr "Join or Malkovich Malkovich"
-
-#: lib/RT/Date.pm:417
-msgid "Jul."
-msgstr "Jul."
-
-#: html/Ticket/Elements/Tabs:100
-msgid "Jumbo"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:416
-msgid "Jun."
-msgstr "Jun."
-
-#: NOT FOUND IN SOURCE
-msgid "Lang"
-msgstr "Lang"
-
-#: html/User/Prefs.html:54
-msgid "Language"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:74
-msgid "Last"
-msgstr "Last"
-
-#: html/Ticket/Elements/EditDates:37 html/Ticket/Elements/ShowDates:39
-msgid "Last Contact"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Last Contact</a>"
-msgstr "Malkovich</a>"
-
-#: html/Elements/SelectDateType:28
-msgid "Last Contacted"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Last Notified"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:29
-msgid "Last Updated"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:82
-msgid "Let this user access RT"
-msgstr "Malkovich Malkovich RT"
-
-#: html/Admin/Users/Modify.html:86
-msgid "Let this user be granted rights"
-msgstr "Malkovich be Malkovich"
-
-#: lib/RT/Record.pm:1086
-msgid "Link already exists"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Record.pm:1100
-msgid "Link could not be created"
-msgstr "Malkovich be Malkovich"
-
-#: lib/RT/Record.pm:1106
-#. ($TransString)
-msgid "Link created (%1)"
-msgstr "Malkovich (%1)"
-
-#: lib/RT/Record.pm:1167
-#. ($TransString)
-msgid "Link deleted (%1)"
-msgstr "Malkovich (%1)"
-
-#: lib/RT/Record.pm:1173
-msgid "Link not found"
-msgstr "Malkovich"
-
-#: html/Ticket/ModifyLinks.html:24 html/Ticket/ModifyLinks.html:28
-#. ($Ticket->Id)
-msgid "Link ticket #%1"
-msgstr "Malkovich #%1"
-
-#: html/Ticket/Create.html:174 html/Ticket/Elements/ShowSummary:61 html/Ticket/Elements/Tabs:98 html/Ticket/ModifyAll.html:56
-msgid "Links"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:111 html/User/Prefs.html:104
-msgid "Location"
-msgstr "Malkovich"
-
-#: lib/RT.pm:184
-#. ($RT::LogDir)
-msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
-msgstr "Malkovich %1 Malkovich or couldn't be Malkovich.\\n RT can't run."
-
-#: html/Elements/Header:69
-#. ("<b>".$session{'CurrentUser'}->Name."</b>")
-msgid "Logged in as %1"
-msgstr "Malkovich in as %1"
-
-#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:35 html/Elements/Login:44 html/Elements/Login:54 lib/RT/StyleGuide.pod:777
-msgid "Login"
-msgstr "Malkovich"
-
-#: html/Elements/Header:66
-msgid "Logout"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:83
-msgid "Make Owner"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:107
-msgid "Make Status"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:115
-msgid "Make date Due"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:117
-msgid "Make date Resolved"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Bulk.html:111
-msgid "Make date Started"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Bulk.html:109
-msgid "Make date Starts"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Bulk.html:113
-msgid "Make date Told"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:103
-msgid "Make priority"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:105
-msgid "Make queue"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:101
-msgid "Make subject"
-msgstr "Malkovich"
-
-#: html/Admin/index.html:32
-msgid "Manage groups and group membership"
-msgstr "Malkovich and Malkovich Malkovich"
-
-#: html/Admin/index.html:38
-msgid "Manage properties and configuration which apply to all queues"
-msgstr "Malkovich Malkovich and Malkovich Malkovich to Malkovich"
-
-#: html/Admin/index.html:35
-msgid "Manage queues and queue-specific properties"
-msgstr "Malkovich and Malkovich-Malkovich Malkovich"
-
-#: html/Admin/index.html:29
-msgid "Manage users and passwords"
-msgstr "Malkovich and Malkovich"
-
-#: lib/RT/Date.pm:413
-msgid "Mar."
-msgstr "Mar."
-
-#: lib/RT/Date.pm:415
-msgid "May."
-msgstr "May."
-
-#: lib/RT/Transaction_Overlay.pm:634
-#. ($value)
-msgid "Member %1 added"
-msgstr "Malkovich %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:674
-#. ($value)
-msgid "Member %1 deleted"
-msgstr "Malkovich %1 Malkovich"
-
-#: lib/RT/Group_Overlay.pm:974
-msgid "Member added"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:1136
-msgid "Member deleted"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:1140
-msgid "Member not deleted"
-msgstr "Malkovich Malkovich"
-
-#: html/Elements/SelectLinkType:25
-msgid "Member of"
-msgstr "Malkovich of"
-
-#: html/Admin/Elements/GroupTabs:41 html/User/Elements/GroupTabs:41
-msgid "Members"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:631
-#. ($value)
-msgid "Membership in %1 added"
-msgstr "Malkovich in %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:671
-#. ($value)
-msgid "Membership in %1 deleted"
-msgstr "Malkovich in %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2813
-msgid "Merge Successful"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2733
-msgid "Merge failed. Couldn't set EffectiveId"
-msgstr "Malkovich. Couldn't Malkovich"
-
-#: html/Elements/EditLinks:104 html/Ticket/Elements/BulkLinks:26
-msgid "Merge into"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:135 html/Ticket/Update.html:83
-msgid "Message"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Message body not shown because it is too large or is not plain text."
-msgstr "Malkovich Malkovich Malkovich it is Malkovich or is Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:2514
-msgid "Message could not be recorded"
-msgstr "Malkovich Malkovich be Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Message recipients"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2517
-msgid "Message recorded"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Record.pm:749
-msgid "Missing a primary key?: %1"
-msgstr "Malkovich a Malkovich?: %1"
-
-#: html/Admin/Users/Modify.html:166 html/User/Prefs.html:71
-msgid "Mobile"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "MobilePhone"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:71
-msgid "Modify Access Control List"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Global/CustomFields.html:43 html/Admin/Global/index.html:50
-msgid "Modify Custom Fields which apply to all queues"
-msgstr "Malkovich Malkovich Malkovich to Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:74
-msgid "Modify Scrip templates for this queue"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:77
-msgid "Modify Scrips for this queue"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Queues/CustomField.html:44
-#. ($QueueObj->Name())
-msgid "Modify a CustomField for queue %1"
-msgstr "Malkovich a Malkovich Malkovich %1"
-
-#: html/Admin/Global/CustomField.html:52
-msgid "Modify a CustomField which applies to all queues"
-msgstr "Malkovich a Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Queues/Scrip.html:53
-#. ($QueueObj->Name)
-msgid "Modify a scrip for queue %1"
-msgstr "Malkovich a Malkovich %1"
-
-#: html/Admin/Global/Scrip.html:47
-msgid "Modify a scrip which applies to all queues"
-msgstr "Malkovich a Malkovich Malkovich to Malkovich"
-
-#: html/Ticket/ModifyDates.html:24 html/Ticket/ModifyDates.html:28
-#. ($TicketObj->Id)
-msgid "Modify dates for #%1"
-msgstr "Malkovich Malkovich #%1"
-
-#: html/Ticket/ModifyDates.html:34
-#. ($TicketObj->Id)
-msgid "Modify dates for ticket # %1"
-msgstr "Malkovich Malkovich # %1"
-
-#: html/Admin/Global/GroupRights.html:24 html/Admin/Global/GroupRights.html:27 html/Admin/Global/index.html:55
-msgid "Modify global group rights"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Global/GroupRights.html:32
-msgid "Modify global group rights."
-msgstr "Malkovich Malkovich Malkovich."
-
-#: html/Admin/Global/UserRights.html:24 html/Admin/Global/UserRights.html:27 html/Admin/Global/index.html:59
-msgid "Modify global user rights"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Global/UserRights.html:32
-msgid "Modify global user rights."
-msgstr "Malkovich Malkovich."
-
-#: lib/RT/Group_Overlay.pm:146
-msgid "Modify group metadata or delete group"
-msgstr "Malkovich Malkovich or Malkovich"
-
-#: html/Admin/Groups/GroupRights.html:24 html/Admin/Groups/GroupRights.html:28 html/Admin/Groups/GroupRights.html:34
-#. ($GroupObj->Name)
-msgid "Modify group rights for group %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/Admin/Queues/GroupRights.html:24 html/Admin/Queues/GroupRights.html:28
-#. ($QueueObj->Name)
-msgid "Modify group rights for queue %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: lib/RT/Group_Overlay.pm:148
-msgid "Modify membership roster for this group"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/System.pm:60
-msgid "Modify one's own RT account"
-msgstr "Malkovich's own RT Malkovich"
-
-#: html/Admin/Queues/People.html:24 html/Admin/Queues/People.html:28
-#. ($QueueObj->Name)
-msgid "Modify people related to queue %1"
-msgstr "Malkovich Malkovich to Malkovich %1"
-
-#: html/Ticket/ModifyPeople.html:24 html/Ticket/ModifyPeople.html:28 html/Ticket/ModifyPeople.html:34
-#. ($Ticket->id)
-#. ($Ticket->Id)
-msgid "Modify people related to ticket #%1"
-msgstr "Malkovich Malkovich to Malkovich #%1"
-
-#: html/Admin/Queues/Scrips.html:45
-#. ($QueueObj->Name)
-msgid "Modify scrips for queue %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Admin/Global/Scrips.html:43 html/Admin/Global/index.html:41
-msgid "Modify scrips which apply to all queues"
-msgstr "Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Global/Template.html:24 html/Admin/Global/Template.html:29 html/Admin/Global/Template.html:80 html/Admin/Queues/Template.html:77
-#. (loc($TemplateObj->Name()))
-#. ($TemplateObj->id)
-msgid "Modify template %1"
-msgstr "Malkovich %1"
-
-#: html/Admin/Global/Templates.html:43
-msgid "Modify templates which apply to all queues"
-msgstr "Malkovich Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Groups/Modify.html:86 html/User/Groups/Modify.html:85
-#. ($Group->Name)
-msgid "Modify the group %1"
-msgstr "Malkovich the Malkovich %1"
-
-#: lib/RT/Queue_Overlay.pm:72
-msgid "Modify the queue watchers"
-msgstr "Malkovich the Malkovich"
-
-#: html/Admin/Users/Modify.html:263
-#. ($UserObj->Name)
-msgid "Modify the user %1"
-msgstr "Malkovich the user %1"
-
-#: html/Ticket/ModifyAll.html:36
-#. ($Ticket->Id)
-msgid "Modify ticket # %1"
-msgstr "Malkovich # %1"
-
-#: html/Ticket/Modify.html:24 html/Ticket/Modify.html:27 html/Ticket/Modify.html:33
-#. ($TicketObj->Id)
-msgid "Modify ticket #%1"
-msgstr "Malkovich #%1"
-
-#: lib/RT/Queue_Overlay.pm:90
-msgid "Modify tickets"
-msgstr "Malkovich"
-
-#: html/Admin/Groups/UserRights.html:24 html/Admin/Groups/UserRights.html:28 html/Admin/Groups/UserRights.html:34
-#. ($GroupObj->Name)
-msgid "Modify user rights for group %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/Admin/Queues/UserRights.html:24 html/Admin/Queues/UserRights.html:28
-#. ($QueueObj->Name)
-msgid "Modify user rights for queue %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: lib/RT/Queue_Overlay.pm:71
-msgid "ModifyACL"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:149
-msgid "ModifyOwnMembership"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:72
-msgid "ModifyQueueWatchers"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:77
-msgid "ModifyScrips"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:60
-msgid "ModifySelf"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:74
-msgid "ModifyTemplate"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:90
-msgid "ModifyTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:388
-msgid "Mon."
-msgstr "Mon."
-
-#: html/Ticket/Elements/ShowRequestor:40
-#. ($name)
-msgid "More about %1"
-msgstr "Malkovich %1"
-
-#: html/Admin/Elements/EditCustomFields:60
-msgid "Move down"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectSingleOrMultiple:26
-msgid "Multiple"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:197
-msgid "Must specify 'Name' attribute"
-msgstr "Malkovich 'Name' Malkovich"
-
-#: html/SelfService/Elements/MyRequests:48
-#. ($friendly_status)
-msgid "My %1 tickets"
-msgstr "My %1 Malkovich"
-
-#: html/Approvals/index.html:24 html/Approvals/index.html:25
-msgid "My approvals"
-msgstr "My Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:31 html/Admin/Elements/EditCustomField:33 html/Admin/Elements/ModifyTemplate:27 html/Admin/Groups/Modify.html:43 html/Elements/SelectGroups:25 html/Elements/SelectUsers:27 html/User/Groups/Modify.html:43
-msgid "Name"
-msgstr "Name"
-
-#: lib/RT/User_Overlay.pm:204
-msgid "Name in use"
-msgstr "Name in use"
-
-#: html/Ticket/Elements/ShowDates:52
-msgid "Never"
-msgstr "Malkovich"
-
-#: html/Elements/Quicksearch:29
-msgid "New"
-msgstr "New"
-
-#: html/Elements/EditLinks:93
-msgid "New Links"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:92 html/User/Prefs.html:87
-msgid "New Password"
-msgstr "Malkovich"
-
-#: etc/initialdata:332
-msgid "New Pending Approval"
-msgstr "Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "New Search"
-msgstr "Malkovich"
-
-#: html/Admin/Global/CustomField.html:40 html/Admin/Global/CustomFields.html:38 html/Admin/Queues/CustomField.html:51 html/Admin/Queues/CustomFields.html:40
-msgid "New custom field"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/GroupTabs:53 html/User/Elements/GroupTabs:51
-msgid "New group"
-msgstr "Malkovich"
-
-#: html/SelfService/Prefs.html:31
-msgid "New password"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:773
-msgid "New password notification sent"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/QueueTabs:69
-msgid "New queue"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectRights:41
-msgid "New rights"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:39 html/Admin/Global/Scrips.html:38 html/Admin/Queues/Scrip.html:42 html/Admin/Queues/Scrips.html:54
-msgid "New scrip"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Template.html:59 html/Admin/Global/Templates.html:38 html/Admin/Queues/Template.html:57 html/Admin/Queues/Templates.html:49
-msgid "New template"
-msgstr "Malkovich"
-
-#: html/SelfService/Elements/Tabs:47
-msgid "New ticket"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2700
-msgid "New ticket doesn't exist"
-msgstr "Malkovich doesn't Malkovich"
-
-#: html/Admin/Elements/UserTabs:50
-msgid "New user"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/CreateUserCalled:25
-msgid "New user called"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:54 html/Ticket/Elements/EditPeople:28
-msgid "New watchers"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "New window setting"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Elements/Tabs:70
-msgid "Next"
-msgstr "Next"
-
-#: NOT FOUND IN SOURCE
-msgid "NickName"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:62 html/User/Prefs.html:50
-msgid "Nickname"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:89 html/Admin/Elements/EditCustomFields:103
-msgid "No CustomField"
-msgstr "No Malkovich"
-
-#: html/Admin/Groups/GroupRights.html:83 html/Admin/Groups/UserRights.html:70
-msgid "No Group defined"
-msgstr "No Malkovich"
-
-#: lib/RT/Tickets_Overlay_SQL.pm:452
-msgid "No Query"
-msgstr "No Malkovich"
-
-#: html/Admin/Queues/GroupRights.html:96 html/Admin/Queues/UserRights.html:67
-msgid "No Queue defined"
-msgstr "No Malkovich"
-
-#: bin/rt-crontool:52
-msgid "No RT user found. Please consult your RT administrator.\\n"
-msgstr "No RT Malkovich. Malkovich Malkovich RT Malkovich.\\n"
-
-#: html/Admin/Global/Template.html:78 html/Admin/Queues/Template.html:75
-msgid "No Template"
-msgstr "No Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "No Ticket specified. Aborting ticket "
-msgstr "No Malkovich Malkovich. Malkovich "
-
-#: html/Approvals/Elements/Approve:45
-msgid "No action"
-msgstr "No Malkovich"
-
-#: lib/RT/Record.pm:744
-msgid "No column specified"
-msgstr "No Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowRequestor:46
-msgid "No comment entered about this user"
-msgstr "No Malkovich Malkovich Malkovich"
-
-#: lib/RT/Action/Generic.pm:159 lib/RT/Condition/Generic.pm:175 lib/RT/Search/ActiveTicketsInQueue.pm:55 lib/RT/Search/Generic.pm:112
-#. (ref $self)
-msgid "No description for %1"
-msgstr "No Malkovich %1"
-
-#: lib/RT/Users_Overlay.pm:159
-msgid "No group specified"
-msgstr "No Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2475
-msgid "No message attached"
-msgstr "No Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:991
-msgid "No password set"
-msgstr "No Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:296
-msgid "No permission to create queues"
-msgstr "No Malkovich to Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "No permission to create tickets in the queue '%1'"
-msgstr "No Malkovich to Malkovich in the Malkovich '%1'"
-
-#: lib/RT/User_Overlay.pm:157
-msgid "No permission to create users"
-msgstr "No Malkovich to Malkovich"
-
-#: html/SelfService/Display.html:125
-msgid "No permission to display that ticket"
-msgstr "No Malkovich to Malkovich Malkovich"
-
-#: html/SelfService/Update.html:68
-msgid "No permission to view update ticket"
-msgstr "No Malkovich to Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:716 lib/RT/Ticket_Overlay.pm:1581
-msgid "No principal specified"
-msgstr "No Malkovich Malkovich"
-
-#: html/Admin/Queues/People.html:153 html/Admin/Queues/People.html:163
-msgid "No principals selected."
-msgstr "No Malkovich Malkovich."
-
-#: html/Admin/Queues/index.html:34
-msgid "No queues matching search criteria found."
-msgstr "No Malkovich Malkovich Malkovich Malkovich."
-
-#: html/Admin/Elements/SelectRights:81
-msgid "No rights found"
-msgstr "No Malkovich"
-
-#: html/Admin/Elements/SelectRights:32
-msgid "No rights granted."
-msgstr "No Malkovich."
-
-#: html/Search/Bulk.html:162
-msgid "No search to operate on."
-msgstr "No Malkovich to Malkovich on."
-
-#: lib/RT/Transaction_Overlay.pm:455 lib/RT/Transaction_Overlay.pm:493
-msgid "No transaction type specified"
-msgstr "No Malkovich Malkovich Malkovich"
-
-#: html/Admin/Users/index.html:35
-msgid "No users matching search criteria found."
-msgstr "No Malkovich Malkovich Malkovich Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
-msgstr "No Malkovich RT Malkovich. RT Malkovich Malkovich. Malkovich Malkovich RT Malkovich.\\n"
-
-#: lib/RT/Record.pm:741
-msgid "No value sent to _Set!\\n"
-msgstr "No Malkovich to _Set!\\n"
-
-#: lib/RT/Record.pm:746
-msgid "Nonexistant field?"
-msgstr "Malkovich Malkovich?"
-
-#: html/Elements/Header:71
-msgid "Not logged in."
-msgstr "Malkovich in."
-
-#: lib/RT/Date.pm:369
-msgid "Not set"
-msgstr "Malkovich"
-
-#: html/NoAuth/Reminder.html:26
-msgid "Not yet implemented."
-msgstr "Malkovich Malkovich."
-
-#: html/Approvals/Elements/Approve:48
-msgid "Notes"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:776
-msgid "Notification could not be sent"
-msgstr "Malkovich Malkovich be sent"
-
-#: etc/initialdata:101
-msgid "Notify AdminCcs"
-msgstr "Malkovich"
-
-#: etc/initialdata:97
-msgid "Notify AdminCcs as Comment"
-msgstr "Malkovich as Malkovich"
-
-#: etc/initialdata:128
-msgid "Notify Other Recipients"
-msgstr "Malkovich Malkovich"
-
-#: etc/initialdata:124
-msgid "Notify Other Recipients as Comment"
-msgstr "Malkovich Malkovich as Malkovich"
-
-#: etc/initialdata:85
-msgid "Notify Owner"
-msgstr "Malkovich"
-
-#: etc/initialdata:81
-msgid "Notify Owner as Comment"
-msgstr "Malkovich as Malkovich"
-
-#: etc/initialdata:376
-msgid "Notify Owner of their rejected ticket"
-msgstr "Malkovich of Malkovich Malkovich"
-
-#: etc/initialdata:365
-msgid "Notify Owner of their ticket has been approved by all approvers"
-msgstr "Malkovich of Malkovich Malkovich Malkovich by Malkovich"
-
-#: etc/initialdata:353
-msgid "Notify Owner of their ticket has been approved by some approver"
-msgstr "Malkovich of Malkovich Malkovich Malkovich by Malkovich"
-
-#: etc/initialdata:334
-msgid "Notify Owners and AdminCcs of new items pending their approval"
-msgstr "Malkovich and Malkovich of Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:77
-msgid "Notify Requestors"
-msgstr "Malkovich Malkovich"
-
-#: etc/initialdata:111
-msgid "Notify Requestors and Ccs"
-msgstr "Malkovich Malkovich and Ccs"
-
-#: etc/initialdata:106
-msgid "Notify Requestors and Ccs as Comment"
-msgstr "Malkovich Malkovich and Ccs as Malkovich"
-
-#: etc/initialdata:120
-msgid "Notify Requestors, Ccs and AdminCcs"
-msgstr "Malkovich Malkovich, Ccs and Malkovich"
-
-#: etc/initialdata:116
-msgid "Notify Requestors, Ccs and AdminCcs as Comment"
-msgstr "Malkovich Malkovich, Ccs and Malkovich as Malkovich"
-
-#: lib/RT/Date.pm:421
-msgid "Nov."
-msgstr "Nov."
-
-#: lib/RT/Record.pm:200
-msgid "Object could not be created"
-msgstr "Malkovich Malkovich be Malkovich"
-
-#: lib/RT/Record.pm:219
-msgid "Object created"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:420
-msgid "Oct."
-msgstr "Oct."
-
-#: html/Elements/SelectDateRelation:34
-msgid "On"
-msgstr "On"
-
-#: etc/initialdata:163
-msgid "On Comment"
-msgstr "On Malkovich"
-
-#: etc/initialdata:156
-msgid "On Correspond"
-msgstr "On Malkovich"
-
-#: etc/initialdata:145
-msgid "On Create"
-msgstr "On Malkovich"
-
-#: etc/initialdata:184
-msgid "On Owner Change"
-msgstr "On Malkovich"
-
-#: etc/initialdata:192
-msgid "On Queue Change"
-msgstr "On Malkovich"
-
-#: etc/initialdata:198
-msgid "On Resolve"
-msgstr "On Malkovich"
-
-#: etc/initialdata:169
-msgid "On Status Change"
-msgstr "On Malkovich"
-
-#: etc/initialdata:150
-msgid "On Transaction"
-msgstr "On Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:49
-#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter'>")
-msgid "Only show approvals for requests created after %1"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich %1"
-
-#: html/Approvals/Elements/PendingMyApproval:47
-#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore'>")
-msgid "Only show approvals for requests created before %1"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich %1"
-
-#: html/Elements/Quicksearch:30
-msgid "Open"
-msgstr "Open"
-
-#: html/Ticket/Elements/Tabs:137
-msgid "Open it"
-msgstr "Open it"
-
-#: html/SelfService/Elements/Tabs:41
-msgid "Open tickets"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Open tickets (from listing) in a new window"
-msgstr "Malkovich (Malkovich) in a Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Open tickets (from listing) in another window"
-msgstr "Malkovich (Malkovich) in Malkovich"
-
-#: etc/initialdata:140
-msgid "Open tickets on correspondence"
-msgstr "Malkovich on Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Ordering and sorting"
-msgstr "Malkovich and Malkovich"
-
-#: html/Admin/Users/Modify.html:114 html/Elements/SelectUsers:28 html/User/Prefs.html:107
-msgid "Organization"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/Approve:32
-#. ($approving->Id, $approving->Subject)
-msgid "Originating ticket: #%1"
-msgstr "Malkovich Malkovich: #%1"
-
-#: html/Admin/Queues/Modify.html:68
-msgid "Over time, priority moves toward"
-msgstr "Malkovich, Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:89
-msgid "Own tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:89
-msgid "OwnTicket"
-msgstr "Malkovich"
-
-#: etc/initialdata:38 html/Elements/QuickCreate:13 html/Search/Elements/PickBasics:114 html/SelfService/Elements/MyRequests:29 html/Ticket/Create.html:47 html/Ticket/Elements/EditPeople:42 html/Ticket/Elements/EditPeople:43 html/Ticket/Elements/ShowPeople:26 html/Ticket/Update.html:40 lib/RT/ACE_Overlay.pm:85 lib/RT/Tickets_Overlay.pm:1306
-msgid "Owner"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:575
-#. ($Old->Name , $New->Name)
-msgid "Owner forcibly changed from %1 to %2"
-msgstr "Malkovich Malkovich Malkovich %1 to %2"
-
-#: NOT FOUND IN SOURCE
-msgid "Owner is"
-msgstr "Malkovich is"
-
-#: html/Admin/Users/Modify.html:171 html/User/Prefs.html:75
-msgid "Pager"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "PagerPhone"
-msgstr "Malkovich"
-
-#: html/Elements/EditLinks:117 html/Elements/EditLinks:54 html/Elements/ShowLinks:46 html/Ticket/Create.html:182 html/Ticket/Elements/BulkLinks:38
-msgid "Parents"
-msgstr "Malkovich"
-
-#: html/Elements/Login:52 html/User/Prefs.html:83
-msgid "Password"
-msgstr "Malkovich"
-
-#: html/NoAuth/Reminder.html:24
-msgid "Password Reminder"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:185 lib/RT/User_Overlay.pm:994
-msgid "Password too short"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:316 html/User/Prefs.html:209
-#. (loc_fuzzy($msg))
-msgid "Password: %1"
-msgstr "Malkovich: %1"
-
-#: html/Admin/Users/Modify.html:318
-msgid "Passwords do not match."
-msgstr "Malkovich do Malkovich."
-
-#: html/User/Prefs.html:211
-msgid "Passwords do not match. Your password has not been changed"
-msgstr "Malkovich do Malkovich. Malkovich Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowSummary:44 html/Ticket/Elements/Tabs:97 html/Ticket/ModifyAll.html:50
-msgid "People"
-msgstr "Malkovich"
-
-#: etc/initialdata:133
-msgid "Perform a user-defined action"
-msgstr "Malkovich a user-Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:230 lib/RT/ACE_Overlay.pm:236 lib/RT/ACE_Overlay.pm:562 lib/RT/ACE_Overlay.pm:572 lib/RT/ACE_Overlay.pm:582 lib/RT/ACE_Overlay.pm:647 lib/RT/Attribute_Overlay.pm:135 lib/RT/Attribute_Overlay.pm:141 lib/RT/Attribute_Overlay.pm:379 lib/RT/Attribute_Overlay.pm:388 lib/RT/Attribute_Overlay.pm:401 lib/RT/CurrentUser.pm:103 lib/RT/CurrentUser.pm:94 lib/RT/CustomField_Overlay.pm:100 lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:239 lib/RT/CustomField_Overlay.pm:517 lib/RT/CustomField_Overlay.pm:90 lib/RT/Group_Overlay.pm:1091 lib/RT/Group_Overlay.pm:1095 lib/RT/Group_Overlay.pm:1104 lib/RT/Group_Overlay.pm:1155 lib/RT/Group_Overlay.pm:1159 lib/RT/Group_Overlay.pm:1165 lib/RT/Group_Overlay.pm:426 lib/RT/Group_Overlay.pm:518 lib/RT/Group_Overlay.pm:596 lib/RT/Group_Overlay.pm:604 lib/RT/Group_Overlay.pm:701 lib/RT/Group_Overlay.pm:705 lib/RT/Group_Overlay.pm:711 lib/RT/Group_Overlay.pm:896 lib/RT/Group_Overlay.pm:900 lib/RT/Group_Overlay.pm:913 lib/RT/Queue_Overlay.pm:117 lib/RT/Queue_Overlay.pm:135 lib/RT/Queue_Overlay.pm:578 lib/RT/Queue_Overlay.pm:588 lib/RT/Queue_Overlay.pm:602 lib/RT/Queue_Overlay.pm:740 lib/RT/Queue_Overlay.pm:749 lib/RT/Queue_Overlay.pm:762 lib/RT/Queue_Overlay.pm:975 lib/RT/Scrip_Overlay.pm:125 lib/RT/Scrip_Overlay.pm:136 lib/RT/Scrip_Overlay.pm:201 lib/RT/Scrip_Overlay.pm:473 lib/RT/Template_Overlay.pm:284 lib/RT/Template_Overlay.pm:87 lib/RT/Template_Overlay.pm:93 lib/RT/Ticket_Overlay.pm:1453 lib/RT/Ticket_Overlay.pm:1463 lib/RT/Ticket_Overlay.pm:1477 lib/RT/Ticket_Overlay.pm:1614 lib/RT/Ticket_Overlay.pm:1624 lib/RT/Ticket_Overlay.pm:1638 lib/RT/Ticket_Overlay.pm:1755 lib/RT/Ticket_Overlay.pm:2075 lib/RT/Ticket_Overlay.pm:2213 lib/RT/Ticket_Overlay.pm:2381 lib/RT/Ticket_Overlay.pm:2428 lib/RT/Ticket_Overlay.pm:2582 lib/RT/Ticket_Overlay.pm:2640 lib/RT/Ticket_Overlay.pm:2691 lib/RT/Ticket_Overlay.pm:2706 lib/RT/Ticket_Overlay.pm:2905 lib/RT/Ticket_Overlay.pm:2915 lib/RT/Ticket_Overlay.pm:2920 lib/RT/Ticket_Overlay.pm:3143 lib/RT/Ticket_Overlay.pm:3147 lib/RT/Ticket_Overlay.pm:3350 lib/RT/Ticket_Overlay.pm:3512 lib/RT/Ticket_Overlay.pm:3564 lib/RT/Ticket_Overlay.pm:3791 lib/RT/Transaction_Overlay.pm:443 lib/RT/Transaction_Overlay.pm:450 lib/RT/Transaction_Overlay.pm:479 lib/RT/Transaction_Overlay.pm:486 lib/RT/User_Overlay.pm:1088 lib/RT/User_Overlay.pm:1536 lib/RT/User_Overlay.pm:335 lib/RT/User_Overlay.pm:696 lib/RT/User_Overlay.pm:731 lib/RT/User_Overlay.pm:987
-msgid "Permission Denied"
-msgstr "Malkovich Malkovich"
-
-#: html/User/Elements/Tabs:34
-msgid "Personal Groups"
-msgstr "Malkovich"
-
-#: html/User/Groups/index.html:29 html/User/Groups/index.html:39
-msgid "Personal groups"
-msgstr "Malkovich"
-
-#: html/User/Elements/DelegateRights:36
-msgid "Personal groups:"
-msgstr "Malkovich:"
-
-#: html/Admin/Users/Modify.html:153 html/User/Prefs.html:60
-msgid "Phone numbers"
-msgstr "Malkovich"
-
-#: html/Elements/Header:63 html/Elements/Tabs:55 html/SelfService/Elements/Tabs:50 html/SelfService/Prefs.html:24 html/User/Prefs.html:24 html/User/Prefs.html:27
-msgid "Preferences"
-msgstr "Malkovich"
-
-#: lib/RT/Action/Generic.pm:169
-msgid "Prepare Stubbed"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:62
-msgid "Prev"
-msgstr "Prev"
-
-#: lib/RT/ACE_Overlay.pm:132 lib/RT/ACE_Overlay.pm:207 lib/RT/ACE_Overlay.pm:551
-#. ($args{'PrincipalId'})
-msgid "Principal %1 not found."
-msgstr "Malkovich %1 Malkovich."
-
-#: html/Search/Elements/PickBasics:58 html/Ticket/Create.html:153 html/Ticket/Elements/EditBasics:52 html/Ticket/Elements/ShowBasics:50 lib/RT/Tickets_Overlay.pm:1104
-msgid "Priority"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Modify.html:64
-msgid "Priority starts at"
-msgstr "Malkovich at"
-
-#: etc/initialdata:25
-msgid "Privileged"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:296 html/User/Prefs.html:200
-#. (loc_fuzzy($msg))
-msgid "Privileged status: %1"
-msgstr "Malkovich Malkovich: %1"
-
-#: html/Admin/Users/index.html:61
-msgid "Privileged users"
-msgstr "Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Projects"
-msgstr "Malkovich"
-
-#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
-msgid "Pseudogroup for internal use"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Elements/QuickCreate:10 html/Elements/Quicksearch:28 html/Search/Elements/PickBasics:94 html/SelfService/Create.html:32 html/Ticket/Create.html:37 html/Ticket/Elements/EditBasics:35 html/Ticket/Elements/ShowBasics:54 html/User/Elements/DelegateRights:79 lib/RT/Tickets_Overlay.pm:945
-msgid "Queue"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/CustomField.html:41 html/Admin/Queues/Scrip.html:49 html/Admin/Queues/Scrips.html:47 html/Admin/Queues/Templates.html:43
-#. ($Queue)
-#. ($id)
-msgid "Queue %1 not found"
-msgstr "Malkovich %1 Malkovich"
-
-#: html/Admin/Queues/Modify.html:42
-msgid "Queue Name"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:300
-msgid "Queue already exists"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:309 lib/RT/Queue_Overlay.pm:315
-msgid "Queue could not be created"
-msgstr "Malkovich not be Malkovich"
-
-#: html/Ticket/Create.html:208
-msgid "Queue could not be loaded."
-msgstr "Malkovich be Malkovich."
-
-#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:319 lib/RT/StyleGuide.pod:789
-msgid "Queue created"
-msgstr "Malkovich"
-
-#: html/SelfService/Display.html:72 lib/RT/CustomField_Overlay.pm:97
-msgid "Queue not found"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/Tabs:37 html/Admin/index.html:34
-msgid "Queues"
-msgstr "Malkovich"
-
-#: html/Elements/Quicksearch:24
-msgid "Quick search"
-msgstr "Malkovich"
-
-#: html/Elements/Login:44
-#. ($RT::VERSION)
-msgid "RT %1"
-msgstr "RT %1"
-
-#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:776
-#. ($RT::VERSION, $RT::rtname)
-msgid "RT %1 for %2"
-msgstr "RT %1 for %2"
-
-#: NOT FOUND IN SOURCE
-msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
-msgstr "RT %1 from <a href=\"http://Malkovich.com\">Malkovich Malkovich, LLC</a>."
-
-#: html/Admin/index.html:24 html/Admin/index.html:25
-msgid "RT Administration"
-msgstr "RT Malkovich"
-
-#: html/Elements/Error:41 html/SelfService/Error.html:40
-msgid "RT Error"
-msgstr "RT Malkovich"
-
-#: html/index.html:50 html/index.html:53
-msgid "RT at a glance"
-msgstr "RT at a Malkovich"
-
-#: html/Elements/PageLayout:85
-#. ($RT::rtname)
-msgid "RT for %1"
-msgstr "RT for %1"
-
-#: NOT FOUND IN SOURCE
-msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent <jesse@bestpractical.com>. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
-msgstr "RT is &copy; Malkovich 1996-%1 Malkovich <Malkovich@Malkovich.com>. It is Malkovich Malkovich <a href=\"http://www.gnu.org/copyleft/gpl.html\">Malkovich 2 of the Malkovich Malkovich Malkovich.</a>"
-
-#: html/Admin/Users/Modify.html:57 html/User/Prefs.html:47
-msgid "Real Name"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "RealName"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:628
-#. ($value)
-msgid "Reference by %1 added"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:668
-#. ($value)
-msgid "Reference by %1 deleted"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:625
-#. ($value)
-msgid "Reference to %1 added"
-msgstr "Malkovich to %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:665
-#. ($value)
-msgid "Reference to %1 deleted"
-msgstr "Malkovich to %1 Malkovich"
-
-#: html/Elements/EditLinks:129 html/Elements/EditLinks:81 html/Elements/ShowLinks:70 html/Ticket/Create.html:185 html/Ticket/Elements/BulkLinks:50
-msgid "Referred to by"
-msgstr "Malkovich to by"
-
-#: html/Elements/EditLinks:125 html/Elements/EditLinks:72 html/Elements/SelectLinkType:27 html/Elements/ShowLinks:60 html/Ticket/Create.html:184 html/Ticket/Elements/BulkLinks:46
-msgid "Refers to"
-msgstr "Malkovich to"
-
-#: NOT FOUND IN SOURCE
-msgid "Refine search"
-msgstr "Malkovich"
-
-#: html/Elements/Refresh:35
-#. ($value/60)
-msgid "Refresh this page every %1 minutes."
-msgstr "Malkovich Malkovich %1 Malkovich."
-
-#: html/Search/Bulk.html:95
-msgid "Remove AdminCc"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:91
-msgid "Remove Cc"
-msgstr "Malkovich Cc"
-
-#: html/Search/Bulk.html:87
-msgid "Remove Requestor"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowTransaction:142 html/Ticket/Elements/Tabs:123
-msgid "Reply"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:87
-msgid "Reply to tickets"
-msgstr "Malkovich to Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:87
-msgid "ReplyToTicket"
-msgstr "Malkovich"
-
-#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:86
-msgid "Requestor"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Requestor email address"
-msgstr "Malkovich Malkovich"
-
-#: html/SelfService/Create.html:40 html/Ticket/Create.html:55 html/Ticket/Elements/EditPeople:47 html/Ticket/Elements/ShowPeople:30
-msgid "Requestors"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Modify.html:74
-msgid "Requests should be due in"
-msgstr "Malkovich be due in"
-
-#: html/Elements/Submit:61
-msgid "Reset"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:63
-msgid "Residence"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:133
-msgid "Resolve"
-msgstr "Malkovich"
-
-#: html/Ticket/Update.html:119
-#. ($TicketObj->id, $TicketObj->Subject)
-msgid "Resolve ticket #%1 (%2)"
-msgstr "Malkovich #%1 (%2)"
-
-#: etc/initialdata:323 html/Elements/SelectDateType:27 lib/RT/Ticket_Overlay.pm:1282
-msgid "Resolved"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Response to requestors"
-msgstr "Malkovich to Malkovich"
-
-#: html/Elements/ListActions:25 html/Search/Elements/NewListActions:25
-msgid "Results"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Results per page"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:99 html/User/Prefs.html:94
-msgid "Retype Password"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:612
-msgid "Right Delegated"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:302
-msgid "Right Granted"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:160
-msgid "Right Loaded"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:677 lib/RT/ACE_Overlay.pm:692
-msgid "Right could not be revoked"
-msgstr "Malkovich be Malkovich"
-
-#: html/User/Delegation.html:63
-msgid "Right not found"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:542 lib/RT/ACE_Overlay.pm:637
-msgid "Right not loaded."
-msgstr "Malkovich Malkovich."
-
-#: lib/RT/ACE_Overlay.pm:688
-msgid "Right revoked"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Rights"
-msgstr "Malkovich"
-
-#: lib/RT/Interface/Web.pm:869
-#. ($object_type)
-msgid "Rights could not be granted for %1"
-msgstr "Malkovich Malkovich be Malkovich %1"
-
-#: lib/RT/Interface/Web.pm:899
-#. ($object_type)
-msgid "Rights could not be revoked for %1"
-msgstr "Malkovich Malkovich be Malkovich %1"
-
-#: html/Admin/Global/GroupRights.html:50 html/Admin/Queues/GroupRights.html:52
-msgid "Roles"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:393
-msgid "Sat."
-msgstr "Sat."
-
-#: html/Admin/Global/Template.html:45 html/Admin/Queues/Modify.html:89 html/Admin/Queues/People.html:104 html/Admin/Users/Modify.html:198 html/SelfService/Prefs.html:36 html/Ticket/Modify.html:38 html/Ticket/ModifyAll.html:93 html/Ticket/ModifyDates.html:38 html/Ticket/ModifyLinks.html:38 html/Ticket/ModifyPeople.html:37
-msgid "Save Changes"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/PreviewScrips:79
-msgid "Save changes"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:48 html/Admin/Queues/Scrip.html:54
-#. ($id)
-#. ($ARGS{'id'})
-msgid "Scrip #%1"
-msgstr "Malkovich #%1"
-
-#: lib/RT/Scrip_Overlay.pm:180
-msgid "Scrip Created"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrips:85
-msgid "Scrip deleted"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/QueueTabs:45 html/Admin/Elements/SystemTabs:32 html/Admin/Global/index.html:40
-msgid "Scrips"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Scrips.html:33
-msgid "Scrips which apply to all queues"
-msgstr "Malkovich Malkovich to Malkovich"
-
-#: html/Elements/SimpleSearch:26 html/Search/Elements/DisplayOptions:73
-msgid "Search"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:38
-msgid "Search for approvals"
-msgstr "Malkovich Malkovich"
-
-#: bin/rt-crontool:184
-msgid "Security:"
-msgstr "Malkovich:"
-
-#: lib/RT/Queue_Overlay.pm:68
-msgid "SeeQueue"
-msgstr "Malkovich"
-
-#: html/Admin/Groups/index.html:50
-msgid "Select a group"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Users/index.html:24 html/Admin/Users/index.html:27
-msgid "Select a user"
-msgstr "Malkovich a user"
-
-#: html/Admin/Global/CustomField.html:37 html/Admin/Global/CustomFields.html:35
-msgid "Select custom field"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/GroupTabs:51 html/User/Elements/GroupTabs:49
-msgid "Select group"
-msgstr "Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:427
-msgid "Select multiple values"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:424
-msgid "Select one value"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/QueueTabs:66
-msgid "Select queue"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:36 html/Admin/Global/Scrips.html:35 html/Admin/Queues/Scrip.html:39 html/Admin/Queues/Scrips.html:51
-msgid "Select scrip"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Template.html:56 html/Admin/Global/Templates.html:35 html/Admin/Queues/Template.html:54 html/Admin/Queues/Templates.html:46
-msgid "Select template"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/UserTabs:46
-msgid "Select user"
-msgstr "Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:35
-msgid "SelectMultiple"
-msgstr "Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:34
-msgid "SelectSingle"
-msgstr "Malkovich"
-
-#: etc/initialdata:121
-msgid "Send mail to all watchers"
-msgstr "Malkovich to Malkovich"
-
-#: etc/initialdata:117
-msgid "Send mail to all watchers as a \"comment\""
-msgstr "Malkovich to Malkovich as a \"Malkovich\""
-
-#: etc/initialdata:112
-msgid "Send mail to requestors and Ccs"
-msgstr "Malkovich to Malkovich and Ccs"
-
-#: etc/initialdata:107
-msgid "Send mail to requestors and Ccs as a comment"
-msgstr "Malkovich to Malkovich and Ccs as a Malkovich"
-
-#: etc/initialdata:78
-msgid "Sends a message to the requestors"
-msgstr "Malkovich a Malkovich to the Malkovich"
-
-#: etc/initialdata:125 etc/initialdata:129
-msgid "Sends mail to explicitly listed Ccs and Bccs"
-msgstr "Malkovich to Malkovich Malkovich and Bccs"
-
-#: etc/initialdata:102
-msgid "Sends mail to the administrative Ccs"
-msgstr "Malkovich to the Malkovich Malkovich"
-
-#: etc/initialdata:98
-msgid "Sends mail to the administrative Ccs as a comment"
-msgstr "Malkovich to the Malkovich Malkovich as a Malkovich"
-
-#: etc/initialdata:82 etc/initialdata:86
-msgid "Sends mail to the owner"
-msgstr "Malkovich to the Malkovich"
-
-#: lib/RT/Date.pm:419
-msgid "Sep."
-msgstr "Sep."
-
-#: html/Approvals/Elements/PendingMyApproval:43
-msgid "Show approved requests"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Create.html:143 html/Ticket/Create.html:33
-msgid "Show basics"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:44
-msgid "Show denied requests"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Create.html:143 html/Ticket/Create.html:33
-msgid "Show details"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:42
-msgid "Show pending requests"
-msgstr "Malkovich Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:45
-msgid "Show requests awaiting other approvals"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Show ticket private commentary"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Show ticket summaries"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:70
-msgid "ShowACL"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:79
-msgid "ShowScrips"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:76
-msgid "ShowTemplate"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:80
-msgid "ShowTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:81
-msgid "ShowTicketComments"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:84
-msgid "Sign up as a ticket Requestor or ticket or queue Cc"
-msgstr "Sign up as a Malkovich Malkovich or Malkovich or Malkovich Cc"
-
-#: lib/RT/Queue_Overlay.pm:85
-msgid "Sign up as a ticket or queue AdminCc"
-msgstr "Sign up as a Malkovich or Malkovich"
-
-#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:145
-msgid "Signature"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectSingleOrMultiple:25
-msgid "Single"
-msgstr "Malkovich"
-
-#: html/Elements/Header:62
-msgid "Skip Menu"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:27
-msgid "Sort"
-msgstr "Sort"
-
-#: NOT FOUND IN SOURCE
-msgid "Sort results by"
-msgstr "Malkovich by"
-
-#: NOT FOUND IN SOURCE
-msgid "Squelched message recipients"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrip:65
-msgid "Stage"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:26 html/Ticket/Elements/EditDates:31 html/Ticket/Elements/ShowDates:35
-msgid "Started"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:30 html/Ticket/Create.html:165 html/Ticket/Elements/EditDates:26 html/Ticket/Elements/ShowDates:31
-msgid "Starts"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:135 html/User/Prefs.html:123
-msgid "State"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:77 html/SelfService/Elements/MyRequests:28 html/SelfService/Update.html:30 html/Ticket/Create.html:41 html/Ticket/Elements/EditBasics:31 html/Ticket/Elements/ShowBasics:30 html/Ticket/Update.html:37 lib/RT/Ticket_Overlay.pm:1276 lib/RT/Tickets_Overlay.pm:970
-msgid "Status"
-msgstr "Malkovich"
-
-#: etc/initialdata:309
-msgid "Status Change"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:505
-#. ($self->loc($self->OldValue), $self->loc($self->NewValue))
-msgid "Status changed from %1 to %2"
-msgstr "Malkovich Malkovich %1 to %2"
-
-#: html/Ticket/Elements/Tabs:148
-msgid "Steal"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:94
-msgid "Steal tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:94
-msgid "StealTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:581
-#. ($Old->Name)
-msgid "Stolen from %1 "
-msgstr "Malkovich %1 "
-
-#: html/Elements/QuickCreate:7 html/Elements/SelectAttachmentField:25 html/Search/Bulk.html:133 html/SelfService/Create.html:56 html/SelfService/Elements/MyRequests:27 html/SelfService/Update.html:31 html/Ticket/Create.html:83 html/Ticket/Elements/EditBasics:26 html/Ticket/ModifyAll.html:78 html/Ticket/Update.html:58 lib/RT/Ticket_Overlay.pm:1272 lib/RT/Tickets_Overlay.pm:1049
-msgid "Subject"
-msgstr "Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:795 lib/RT/Transaction_Overlay.pm:603
-#. ($self->Data)
-msgid "Subject changed to %1"
-msgstr "Malkovich to %1"
-
-#: html/Elements/Submit:58
-msgid "Submit"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:749
-msgid "Succeeded"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:394
-msgid "Sun."
-msgstr "Sun."
-
-#: lib/RT/System.pm:53
-msgid "SuperUser"
-msgstr "Malkovich"
-
-#: html/User/Elements/DelegateRights:76
-msgid "System"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectRights:81 lib/RT/ACE_Overlay.pm:566 lib/RT/Interface/Web.pm:868 lib/RT/Interface/Web.pm:898
-msgid "System Error"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:615
-msgid "System error. Right not delegated."
-msgstr "Malkovich. Malkovich Malkovich."
-
-#: lib/RT/ACE_Overlay.pm:145 lib/RT/ACE_Overlay.pm:222 lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:897
-msgid "System error. Right not granted."
-msgstr "Malkovich. Malkovich Malkovich."
-
-#: html/Admin/Global/GroupRights.html:34 html/Admin/Groups/GroupRights.html:36 html/Admin/Queues/GroupRights.html:35
-msgid "System groups"
-msgstr "Malkovich"
-
-#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
-msgid "SystemRolegroup for internal use"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/CurrentUser.pm:334
-msgid "TEST_STRING"
-msgstr "TEST_MALKOVICH"
-
-#: html/Elements/MyRequests:27 html/Ticket/Elements/Tabs:144
-msgid "Take"
-msgstr "Take"
-
-#: lib/RT/Queue_Overlay.pm:92
-msgid "Take tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:92
-msgid "TakeTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:566
-msgid "Taken"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:57 html/Tools/Offline.html:56
-msgid "Template"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:89
-#. ($TemplateObj->Id())
-msgid "Template #%1"
-msgstr "Malkovich #%1"
-
-#: html/Admin/Elements/EditTemplates:88
-msgid "Template deleted"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Scrip_Overlay.pm:156
-msgid "Template not found"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Template_Overlay.pm:348
-msgid "Template parsed"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/QueueTabs:48 html/Admin/Elements/SystemTabs:35 html/Admin/Global/index.html:44
-msgid "Templates"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:740
-msgid "That is already the current value"
-msgstr "That is Malkovich the Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:248
-msgid "That is not a value for this custom field"
-msgstr "That is not a Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2086
-msgid "That is the same value"
-msgstr "That is the Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:287 lib/RT/ACE_Overlay.pm:596
-msgid "That principal already has that right"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:674
-#. ($args{'Type'})
-msgid "That principal is already a %1 for this queue"
-msgstr "Malkovich is Malkovich a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1527
-#. ($self->loc($args{'Type'}))
-msgid "That principal is already a %1 for this ticket"
-msgstr "Malkovich is Malkovich a %1 Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:773
-#. ($args{'Type'})
-msgid "That principal is not a %1 for this queue"
-msgstr "That Malkovich is not a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2082
-msgid "That queue does not exist"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3152
-msgid "That ticket has unresolved dependencies"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2956
-msgid "That user already owns that ticket"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2928
-msgid "That user does not exist"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:355
-msgid "That user is already privileged"
-msgstr "Malkovich is Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:376
-msgid "That user is already unprivileged"
-msgstr "Malkovich is Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:368
-msgid "That user is now privileged"
-msgstr "Malkovich is Malkovich"
-
-#: lib/RT/User_Overlay.pm:389
-msgid "That user is now unprivileged"
-msgstr "Malkovich is Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2949
-msgid "That user may not own tickets in that queue"
-msgstr "Malkovich Malkovich Malkovich in Malkovich"
-
-#: lib/RT/Link_Overlay.pm:200
-msgid "That's not a numerical id"
-msgstr "That's not a Malkovich id"
-
-#: html/SelfService/Display.html:31 html/Ticket/Create.html:149 html/Ticket/Elements/ShowSummary:27
-msgid "The Basics"
-msgstr "The Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:87
-msgid "The CC of a ticket"
-msgstr "The CC of a Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:88
-msgid "The administrative CC of a ticket"
-msgstr "The Malkovich CC of a Malkovich"
-
-#: bin/rt-crontool:194
-msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
-msgstr "The Malkovich Malkovich Malkovich Malkovich Malkovich in the Malkovich 'Malkovich' and Malkovich Malkovich to 99 if they haven't Malkovich in 4 Malkovich:"
-
-#: NOT FOUND IN SOURCE
-msgid "The following commands were not proccessed:\\n\\n"
-msgstr "The Malkovich Malkovich Malkovich Malkovich:\\n\\n"
-
-#: lib/RT/Record.pm:743
-msgid "The new value has been set."
-msgstr "The Malkovich Malkovich."
-
-#: lib/RT/ACE_Overlay.pm:85
-msgid "The owner of a ticket"
-msgstr "The Malkovich of a Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:86
-msgid "The requestor of a ticket"
-msgstr "The Malkovich of a Malkovich"
-
-#: html/Admin/Elements/EditUserComments:25
-msgid "These comments aren't generally visible to the user"
-msgstr "Malkovich aren't Malkovich Malkovich to the user"
-
-#: bin/rt-crontool:185
-msgid "This tool allows the user to run arbitrary perl modules from within RT."
-msgstr "Malkovich Malkovich the user to Malkovich Malkovich Malkovich Malkovich RT."
-
-#: lib/RT/Transaction_Overlay.pm:226
-msgid "This transaction appears to have no content"
-msgstr "Malkovich Malkovich to have no Malkovich"
-
-#: html/Ticket/Elements/ShowRequestor:48
-#. ($rows)
-msgid "This user's %1 highest priority tickets"
-msgstr "Malkovich's %1 Malkovich Malkovich"
-
-#: lib/RT/Date.pm:391
-msgid "Thu."
-msgstr "Thu."
-
-#: html/Ticket/ModifyAll.html:24 html/Ticket/ModifyAll.html:28
-#. ($Ticket->Id, $Ticket->Subject)
-msgid "Ticket #%1 Jumbo update: %2"
-msgstr "Malkovich #%1 Malkovich: %2"
-
-#: html/Approvals/Elements/ShowDependency:45
-#. ($link->BaseObj->Id, $link->BaseObj->Subject)
-msgid "Ticket #%1: %2"
-msgstr "Malkovich #%1: %2"
-
-#: lib/RT/Ticket_Overlay.pm:696 lib/RT/Ticket_Overlay.pm:720
-#. ($self->Id, $QueueObj->Name)
-msgid "Ticket %1 created in queue '%2'"
-msgstr "Malkovich %1 Malkovich in Malkovich '%2'"
-
-#: NOT FOUND IN SOURCE
-msgid "Ticket %1 loaded\\n"
-msgstr "Malkovich %1 Malkovich\\n"
-
-#: html/Search/Bulk.html:216
-#. ($Ticket->Id,$_)
-msgid "Ticket %1: %2"
-msgstr "Malkovich %1: %2"
-
-#: html/Ticket/History.html:24 html/Ticket/History.html:27
-#. ($Ticket->Id, $Ticket->Subject)
-msgid "Ticket History # %1 %2"
-msgstr "Malkovich # %1 %2"
-
-#: etc/initialdata:324
-msgid "Ticket Resolved"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Ticket attachment"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1228
-msgid "Ticket content"
-msgstr "Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1274
-msgid "Ticket content type"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:565 lib/RT/Ticket_Overlay.pm:579 lib/RT/Ticket_Overlay.pm:590 lib/RT/Ticket_Overlay.pm:707
-msgid "Ticket could not be created due to an internal error"
-msgstr "Malkovich Malkovich be Malkovich to a Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:497
-msgid "Ticket created"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:502
-msgid "Ticket deleted"
-msgstr "Malkovich"
-
-#: etc/initialdata:310
-msgid "Ticket status changed"
-msgstr "Malkovich Malkovich"
-
-#: html/Elements/Tabs:46
-msgid "Tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1452
-#. ($self->loc($args{'TYPE'}), ($args{'BASE'} || $args{'TICKET'}))
-msgid "Tickets %1 %2"
-msgstr "Malkovich %1 %2"
-
-#: lib/RT/Tickets_Overlay.pm:1410
-#. ($self->loc($args{'TYPE'}), ($args{'TARGET'} || $args{'TICKET'}))
-msgid "Tickets %1 by %2"
-msgstr "Malkovich %1 by %2"
-
-#: NOT FOUND IN SOURCE
-msgid "Tickets from %1"
-msgstr "Malkovich %1"
-
-#: html/Approvals/Elements/ShowDependency:26
-msgid "Tickets which depend on this approval:"
-msgstr "Malkovich Malkovich on Malkovich:"
-
-#: html/Search/Elements/PickBasics:70 html/Ticket/Create.html:156 html/Ticket/Elements/EditBasics:47
-msgid "Time Left"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:68 html/Ticket/Create.html:155 html/Ticket/Elements/EditBasics:43
-msgid "Time Worked"
-msgstr "Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1201
-msgid "Time left"
-msgstr "Malkovich"
-
-#: html/Elements/Footer:44
-msgid "Time to display"
-msgstr "Time to Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1177
-msgid "Time worked"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1277
-msgid "TimeWorked"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "To generate a diff of this commit:"
-msgstr "To Malkovich a diff of Malkovich:"
-
-#: NOT FOUND IN SOURCE
-msgid "To generate a diff of this commit:\\n"
-msgstr "To Malkovich a diff of Malkovich:\\n"
-
-#: lib/RT/Ticket_Overlay.pm:1280
-msgid "Told"
-msgstr "Told"
-
-#: etc/initialdata:252
-msgid "Transaction"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:705
-#. ($self->Data)
-msgid "Transaction %1 purged"
-msgstr "Malkovich %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:136
-msgid "Transaction Created"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:92
-msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
-msgstr "Malkovich->Malkovich couldn't, as you didn't Malkovich a Malkovich id"
-
-#: lib/RT/Transaction_Overlay.pm:760
-msgid "Transactions are immutable"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Date.pm:389
-msgid "Tue."
-msgstr "Tue."
-
-#: html/Admin/Elements/EditCustomField:43 html/Ticket/Elements/AddWatchers:32 html/Ticket/Elements/AddWatchers:43 html/Ticket/Elements/AddWatchers:53 lib/RT/Ticket_Overlay.pm:1278 lib/RT/Tickets_Overlay.pm:1021
-msgid "Type"
-msgstr "Type"
-
-#: lib/RT/ScripCondition_Overlay.pm:103
-msgid "Unimplemented"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:67
-msgid "Unix login"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "UnixUsername"
-msgstr "Malkovich"
-
-#: lib/RT/Attachment_Overlay.pm:233 lib/RT/Attachment_Overlay.pm:265
-#. ($self->ContentEncoding)
-msgid "Unknown ContentEncoding %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Elements/SelectResultsPerPage:36
-msgid "Unlimited"
-msgstr "Malkovich"
-
-#: etc/initialdata:32
-msgid "Unprivileged"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:562
-msgid "Untaken"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:32
-msgid "Update"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update ID"
-msgstr "Malkovich ID"
-
-#: html/Search/Bulk.html:127 html/Ticket/ModifyAll.html:65 html/Ticket/Update.html:48
-msgid "Update Type"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update all these tickets at once"
-msgstr "Malkovich Malkovich at once"
-
-#: NOT FOUND IN SOURCE
-msgid "Update email"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update name"
-msgstr "Malkovich"
-
-#: lib/RT/Action/CreateTickets.pm:655 lib/RT/Interface/Web.pm:479
-msgid "Update not recorded."
-msgstr "Malkovich Malkovich."
-
-#: html/Search/Bulk.html:78
-msgid "Update selected tickets"
-msgstr "Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update signature"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/ModifyAll.html:62
-msgid "Update ticket"
-msgstr "Malkovich"
-
-#: html/SelfService/Update.html:24 html/SelfService/Update.html:63
-#. ($Ticket->id)
-msgid "Update ticket #%1"
-msgstr "Malkovich #%1"
-
-#: html/Ticket/Update.html:121
-#. ($TicketObj->id, $TicketObj->Subject)
-msgid "Update ticket #%1 (%2)"
-msgstr "Malkovich #%1 (%2)"
-
-#: lib/RT/Action/CreateTickets.pm:653 lib/RT/Interface/Web.pm:477
-msgid "Update type was neither correspondence nor comment."
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich."
-
-#: html/Elements/SelectDateType:32 html/Ticket/Elements/ShowDates:51 lib/RT/Ticket_Overlay.pm:1281
-msgid "Updated"
-msgstr "Malkovich"
-
-#: etc/initialdata:132 etc/initialdata:206
-msgid "User Defined"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "User ID"
-msgstr "User ID"
-
-#: html/Elements/SelectUsers:25
-msgid "User Id"
-msgstr "User Id"
-
-#: html/Admin/Elements/GroupTabs:46 html/Admin/Elements/QueueTabs:59 html/Admin/Elements/SystemTabs:46 html/Admin/Global/index.html:58
-msgid "User Rights"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:252
-#. ($msg)
-msgid "User could not be created: %1"
-msgstr "Malkovich be Malkovich: %1"
-
-#: lib/RT/User_Overlay.pm:296
-msgid "User created"
-msgstr "Malkovich"
-
-#: html/Admin/Global/GroupRights.html:66 html/Admin/Groups/GroupRights.html:53 html/Admin/Queues/GroupRights.html:68
-msgid "User defined groups"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:558 lib/RT/User_Overlay.pm:575
-msgid "User loaded"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "User view"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:47 html/Elements/Login:51 html/Ticket/Elements/AddWatchers:34
-msgid "Username"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectNewGroupMembers:25 html/Admin/Elements/Tabs:31 html/Admin/Groups/Members.html:54 html/Admin/Queues/People.html:67 html/Admin/index.html:28 html/User/Groups/Members.html:57
-msgid "Users"
-msgstr "Malkovich"
-
-#: html/Admin/Users/index.html:64
-msgid "Users matching search criteria"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay_SQL.pm:494
-msgid "Valid Query"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:56
-msgid "Values"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:84
-msgid "Watch"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:85
-msgid "WatchAsAdminCc"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/QueueTabs:41
-msgid "Watchers"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "WebEncoding"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:390
-msgid "Wed."
-msgstr "Wed."
-
-#: etc/initialdata:521
-msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
-msgstr "When a Malkovich Malkovich by Malkovich, Malkovich Malkovich to the Malkovich"
-
-#: etc/initialdata:485
-msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
-msgstr "When a Malkovich Malkovich by Malkovich, Malkovich Malkovich to the Malkovich"
-
-#: etc/initialdata:146
-msgid "When a ticket is created"
-msgstr "When a Malkovich is Malkovich"
-
-#: etc/initialdata:418
-msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
-msgstr "When a Malkovich is Malkovich, Malkovich the Malkovich and Malkovich of the Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:151
-msgid "When anything happens"
-msgstr "Malkovich Malkovich"
-
-#: etc/initialdata:199
-msgid "Whenever a ticket is resolved"
-msgstr "Malkovich a Malkovich is Malkovich"
-
-#: etc/initialdata:185
-msgid "Whenever a ticket's owner changes"
-msgstr "Malkovich a Malkovich's Malkovich"
-
-#: etc/initialdata:193
-msgid "Whenever a ticket's queue changes"
-msgstr "Malkovich a Malkovich's Malkovich"
-
-#: etc/initialdata:170
-msgid "Whenever a ticket's status changes"
-msgstr "Malkovich a Malkovich's Malkovich"
-
-#: etc/initialdata:207
-msgid "Whenever a user-defined condition occurs"
-msgstr "Malkovich a user-Malkovich Malkovich"
-
-#: etc/initialdata:164
-msgid "Whenever comments come in"
-msgstr "Malkovich Malkovich in"
-
-#: etc/initialdata:157
-msgid "Whenever correspondence comes in"
-msgstr "Malkovich Malkovich Malkovich in"
-
-#: html/Admin/Users/Modify.html:161 html/User/Prefs.html:67
-msgid "Work"
-msgstr "Work"
-
-#: NOT FOUND IN SOURCE
-msgid "WorkPhone"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/ShowBasics:41 html/Ticket/Update.html:42
-msgid "Worked"
-msgstr "Malkovich"
-
-#: html/autohandler:150
-msgid "XXX CHANGEME You are not an authorized user"
-msgstr "MALKOVICH Malkovich a Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3059
-msgid "You already own this ticket"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/autohandler:142
-msgid "You are not an authorized user"
-msgstr "Malkovich a Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "You can access it with the Download button on the right."
-msgstr "Malkovich it with the Malkovich on the Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:2941
-msgid "You can only reassign tickets that you own or that are unowned"
-msgstr "Malkovich Malkovich Malkovich Malkovich or Malkovich Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:760
-#. ($num, $queue)
-msgid "You found %1 tickets in queue %2"
-msgstr "Malkovich %1 Malkovich in Malkovich %2"
-
-#: html/NoAuth/Logout.html:30
-msgid "You have been logged out of RT."
-msgstr "Malkovich Malkovich of RT."
-
-#: html/SelfService/Display.html:79
-msgid "You have no permission to create tickets in that queue."
-msgstr "Malkovich no Malkovich to Malkovich in that Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:2095
-msgid "You may not create requests in that queue."
-msgstr "Malkovich Malkovich Malkovich in Malkovich."
-
-#: html/NoAuth/Logout.html:34
-msgid "You're welcome to login again"
-msgstr "You're Malkovich to Malkovich"
-
-#: etc/initialdata:502
-msgid "Your request has been approved by %1. Other approvals may still be pending."
-msgstr "Malkovich Malkovich Malkovich by %1. Malkovich Malkovich be Malkovich."
-
-#: etc/initialdata:540
-msgid "Your request has been approved."
-msgstr "Malkovich Malkovich Malkovich."
-
-#: etc/initialdata:445
-msgid "Your request was rejected."
-msgstr "Malkovich Malkovich."
-
-#: html/autohandler:177
-msgid "Your username or password is incorrect"
-msgstr "Malkovich or Malkovich is Malkovich"
-
-#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:127
-msgid "Zip"
-msgstr "Zip"
-
-#: html/User/Elements/DelegateRights:58
-#. ($right->PrincipalObj->Object->SelfDescription)
-msgid "as granted to %1"
-msgstr "as Malkovich to %1"
-
-#: html/SelfService/Closed.html:27
-msgid "closed"
-msgstr "Malkovich"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:33
-msgid "contains"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "content"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "content-type"
-msgstr "Malkovich-type"
-
-#: html/Admin/Queues/Modify.html:76 lib/RT/Date.pm:319
-msgid "days"
-msgstr "days"
-
-#: lib/RT/Queue_Overlay.pm:64
-msgid "deleted"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:33
-msgid "does not match"
-msgstr "Malkovich"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:34
-msgid "doesn't contain"
-msgstr "doesn't Malkovich"
-
-#: html/Elements/SelectEqualityOperator:37
-msgid "equal to"
-msgstr "Malkovich to"
-
-#: NOT FOUND IN SOURCE
-msgid "filename"
-msgstr "Malkovich"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectEqualityOperator:37
-msgid "greater than"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:196
-#. ($self->Name)
-msgid "group '%1'"
-msgstr "Malkovich '%1'"
-
-#: lib/RT/Date.pm:315
-msgid "hours"
-msgstr "Malkovich"
-
-#: html/Elements/SelectBoolean:31 html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:35 html/Search/Elements/PickBasics:49 html/Search/Elements/PickBasics:80 html/Search/Elements/PickBasics:97 html/Search/Elements/PickCFs:37
-msgid "is"
-msgstr "is"
-
-#: html/Elements/SelectBoolean:35 html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:36 html/Search/Elements/PickBasics:50 html/Search/Elements/PickBasics:81 html/Search/Elements/PickBasics:98 html/Search/Elements/PickCFs:38
-msgid "isn't"
-msgstr "isn't"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectEqualityOperator:37
-msgid "less than"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:32
-msgid "matches"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:311
-msgid "min"
-msgstr "min"
-
-#: html/Ticket/Update.html:42
-msgid "minutes"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "modifications\\n\\n"
-msgstr "Malkovich\\n\\n"
-
-#: lib/RT/Date.pm:327
-msgid "months"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:59
-msgid "new"
-msgstr "new"
-
-#: html/Admin/Elements/EditCustomFields:42
-msgid "no name"
-msgstr "no name"
-
-#: html/Admin/Elements/EditScrips:42
-msgid "no value"
-msgstr "no Malkovich"
-
-#: html/Admin/Elements/EditQueueWatchers:26 html/Ticket/Elements/EditWatchers:27
-msgid "none"
-msgstr "none"
-
-#: html/Elements/SelectEqualityOperator:37
-msgid "not equal to"
-msgstr "Malkovich to"
-
-#: html/SelfService/Elements/MyRequests:61 lib/RT/Queue_Overlay.pm:60
-msgid "open"
-msgstr "open"
-
-#: lib/RT/Group_Overlay.pm:201
-#. ($self->Name, $user->Name)
-msgid "personal group '%1' for user '%2'"
-msgstr "Malkovich '%1' Malkovich '%2'"
-
-#: lib/RT/Group_Overlay.pm:209
-#. ($queue->Name, $self->Type)
-msgid "queue %1 %2"
-msgstr "Malkovich %1 %2"
-
-#: lib/RT/Queue_Overlay.pm:63
-msgid "rejected"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:62
-msgid "resolved"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:307
-msgid "sec"
-msgstr "sec"
-
-#: lib/RT/Queue_Overlay.pm:61
-msgid "stalled"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:204
-#. ($self->Type)
-msgid "system %1"
-msgstr "Malkovich %1"
-
-#: lib/RT/Group_Overlay.pm:215
-#. ($self->Type)
-msgid "system group '%1'"
-msgstr "Malkovich '%1'"
-
-#: html/Elements/Error:42 html/SelfService/Error.html:41
-msgid "the calling component did not specify why"
-msgstr "the Malkovich Malkovich Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "ticket #%1"
-msgstr "Malkovich #%1"
-
-#: lib/RT/Group_Overlay.pm:212
-#. ($self->Instance, $self->Type)
-msgid "ticket #%1 %2"
-msgstr "Malkovich #%1 %2"
-
-#: lib/RT/Group_Overlay.pm:218
-#. ($self->Id)
-msgid "undescribed group %1"
-msgstr "Malkovich Malkovich %1"
-
-#: lib/RT/Group_Overlay.pm:193
-#. ($user->Object->Name)
-msgid "user %1"
-msgstr "user %1"
-
-#: lib/RT/Date.pm:323
-msgid "weeks"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:331
-msgid "years"
-msgstr "Malkovich"
-
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm
index a910fb48a..ec0e877b4 100644
--- a/rt/lib/RT/Interface/CLI.pm
+++ b/rt/lib/RT/Interface/CLI.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.)
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
use strict;
use RT;
@@ -54,12 +29,14 @@ package RT::Interface::CLI;
BEGIN {
- use base 'Exporter';
- use vars qw ($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
+ use Exporter ();
+ use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
# set the version for version checking
- $VERSION = do { my @r = (q$Revision: 1.1.1.8 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
-
+ $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
+
+ @ISA = qw(Exporter);
+
# your exported package globals go here,
# as well as any optionally exported functions
@EXPORT_OK = qw(&CleanEnv
@@ -99,6 +76,11 @@ BEGIN {
=head1 METHODS
+=begin testing
+
+ok(require RT::Interface::CLI);
+
+=end testing
=cut
@@ -220,7 +202,7 @@ sub GetMessageContent {
if ($edit) {
unless ($ENV{'EDITOR'}) {
- $RT::Logger->crit('No $EDITOR variable defined');
+ $RT::Logger->crit('No $EDITOR variable defined'. "\n");
return undef;
}
system ($ENV{'EDITOR'}, $filename);
@@ -243,7 +225,7 @@ sub debug {
my $val = shift;
my ($debug);
if ($val) {
- $RT::Logger->debug($val);
+ $RT::Logger->debug($val."\n");
if ($debug) {
print STDERR "$val\n";
}
diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm
index b669b5b2f..7eec0502f 100755
--- a/rt/lib/RT/Interface/Email.pm
+++ b/rt/lib/RT/Interface/Email.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,72 +14,50 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
package RT::Interface::Email;
use strict;
-use warnings;
-
-use Email::Address;
+use Mail::Address;
use MIME::Entity;
use RT::EmailParser;
-use File::Temp;
-use UNIVERSAL::require;
-use Mail::Mailer ();
-BEGIN {
- use base 'Exporter';
- use vars qw ( @EXPORT_OK);
+BEGIN {
+ use Exporter ();
+ use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
# set the version for version checking
- our $VERSION = 2.0;
-
+ $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
+
+ @ISA = qw(Exporter);
+
# your exported package globals go here,
# as well as any optionally exported functions
- @EXPORT_OK = qw(
- &CreateUser
- &GetMessageContent
- &CheckForLoops
- &CheckForSuspiciousSender
- &CheckForAutoGenerated
- &CheckForBounce
- &MailError
- &ParseCcAddressesFromHead
- &ParseSenderAddressFromHead
- &ParseErrorsToAddressFromHead
- &ParseAddressFromHeader
- &Gateway);
+ @EXPORT_OK = qw(
+ &CreateUser
+ &GetMessageContent
+ &CheckForLoops
+ &CheckForSuspiciousSender
+ &CheckForAutoGenerated
+ &MailError
+ &ParseCcAddressesFromHead
+ &ParseSenderAddressFromHead
+ &ParseErrorsToAddressFromHead
+ &ParseAddressFromHeader
+ &Gateway);
}
=head1 NAME
- RT::Interface::Email - helper functions for parsing email sent to RT
+ RT::Interface::CLI - helper functions for creating a commandline RT interface
=head1 SYNOPSIS
@@ -97,1605 +69,425 @@ BEGIN {
=head1 DESCRIPTION
+=begin testing
+ok(require RT::Interface::Email);
-=head1 METHODS
+=end testing
-=head2 CheckForLoops HEAD
-Takes a HEAD object of L<MIME::Head> class and returns true if the
-message's been sent by this RT instance. Uses "X-RT-Loop-Prevention"
-field of the head for test.
+=head1 METHODS
=cut
-sub CheckForLoops {
- my $head = shift;
- # If this instance of RT sent it our, we don't want to take it in
+# {{{ sub CheckForLoops
+
+sub CheckForLoops {
+ my $head = shift;
+
+ #If this instance of RT sent it our, we don't want to take it in
my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
- chomp ($RTLoop); # remove that newline
- if ( $RTLoop eq RT->Config->Get('rtname') ) {
- return 1;
+ chomp ($RTLoop); #remove that newline
+ if ($RTLoop eq "$RT::rtname") {
+ return (1);
}
-
+
# TODO: We might not trap the case where RT instance A sends a mail
# to RT instance B which sends a mail to ...
- return undef;
+ return (undef);
}
-=head2 CheckForSuspiciousSender HEAD
+# }}}
-Takes a HEAD object of L<MIME::Head> class and returns true if sender
-is suspicious. Suspicious means mailer daemon.
-
-See also L</ParseSenderAddressFromHead>.
-
-=cut
+# {{{ sub CheckForSuspiciousSender
sub CheckForSuspiciousSender {
my $head = shift;
#if it's from a postmaster or mailer daemon, it's likely a bounce.
-
+
#TODO: better algorithms needed here - there is no standards for
#bounces, so it's very difficult to separate them from anything
#else. At the other hand, the Return-To address is only ment to be
#used as an error channel, we might want to put up a separate
#Return-To address which is treated differently.
-
+
#TODO: search through the whole email and find the right Ticket ID.
- my ( $From, $junk ) = ParseSenderAddressFromHead($head);
-
- if ( ( $From =~ /^mailer-daemon\@/i )
- or ( $From =~ /^postmaster\@/i )
- or ( $From eq "" ))
- {
- return (1);
-
+ my ($From, $junk) = ParseSenderAddressFromHead($head);
+
+ if (($From =~ /^mailer-daemon/i) or
+ ($From =~ /^postmaster/i)){
+ return (1);
+
}
+
+ return (undef);
- return undef;
}
-=head2 CheckForAutoGenerated HEAD
-
-Takes a HEAD object of L<MIME::Head> class and returns true if message
-is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
-fields of the head in tests.
-
-=cut
+# }}}
+# {{{ sub CheckForAutoGenerated
sub CheckForAutoGenerated {
my $head = shift;
-
- my $Precedence = $head->get("Precedence") || "";
- if ( $Precedence =~ /^(bulk|junk)/i ) {
- return (1);
- }
-
- # Per RFC3834, any Auto-Submitted header which is not "no" means
- # it is auto-generated.
- my $AutoSubmitted = $head->get("Auto-Submitted") || "";
- if ( length $AutoSubmitted and $AutoSubmitted ne "no" ) {
- return (1);
+
+ my $Precedence = $head->get("Precedence") || "" ;
+ if ($Precedence =~ /^(bulk|junk)/i) {
+ return (1);
}
-
- # First Class mailer uses this as a clue.
- my $FCJunk = $head->get("X-FC-Machinegenerated") || "";
- if ( $FCJunk =~ /^true/i ) {
- return (1);
+ else {
+ return (0);
}
-
- return (0);
}
+# }}}
-sub CheckForBounce {
- my $head = shift;
-
- my $ReturnPath = $head->get("Return-path") || "";
- return ( $ReturnPath =~ /<>/ );
-}
-
-
-=head2 MailError PARAM HASH
-
-Sends an error message. Takes a param hash:
-
-=over 4
-
-=item From - sender's address, by default is 'CorrespondAddress';
-
-=item To - recipient, by default is 'OwnerEmail';
-
-=item Bcc - optional Bcc recipients;
-
-=item Subject - subject of the message, default is 'There has been an error';
-
-=item Explanation - main content of the error, default value is 'Unexplained error';
-
-=item MIMEObj - optional MIME entity that's attached to the error mail, as well we
-add 'In-Reply-To' field to the error that points to this message.
-
-=item Attach - optional text that attached to the error as 'message/rfc822' part.
-
-=item LogLevel - log level under which we should write explanation message into the
-log, by default we log it as critical.
-
-=back
-
-=cut
+# {{{ sub MailError
sub MailError {
- my %args = (
- To => RT->Config->Get('OwnerEmail'),
- Bcc => undef,
- From => RT->Config->Get('CorrespondAddress'),
- Subject => 'There has been an error',
- Explanation => 'Unexplained error',
- MIMEObj => undef,
- Attach => undef,
- LogLevel => 'crit',
- @_
- );
-
- $RT::Logger->log(
- level => $args{'LogLevel'},
- message => $args{'Explanation'}
- ) if $args{'LogLevel'};
- # the colons are necessary to make ->build include non-standard headers
- my $entity = MIME::Entity->build(
- Type => "multipart/mixed",
- From => $args{'From'},
- Bcc => $args{'Bcc'},
- To => $args{'To'},
- Subject => $args{'Subject'},
- 'Precedence:' => 'bulk',
- 'X-RT-Loop-Prevention:' => RT->Config->Get('rtname'),
- );
- SetInReplyTo( Message => $entity, InReplyTo => $args{'MIMEObj'} );
-
- $entity->attach( Data => $args{'Explanation'} . "\n" );
-
- if ( $args{'MIMEObj'} ) {
- $args{'MIMEObj'}->sync_headers;
- $entity->add_part( $args{'MIMEObj'} );
- }
-
- if ( $args{'Attach'} ) {
- $entity->attach( Data => $args{'Attach'}, Type => 'message/rfc822' );
-
- }
-
- SendEmail( Entity => $entity, Bounce => 1 );
-}
-
-
-=head2 SendEmail Entity => undef, [ Bounce => 0, Ticket => undef, Transaction => undef ]
-
-Sends an email (passed as a L<MIME::Entity> object C<ENTITY>) using
-RT's outgoing mail configuration. If C<BOUNCE> is passed, and is a
-true value, the message will be marked as an autogenerated error, if
-possible. Sets Date field of the head to now if it's not set.
-
-Ticket and Transaction arguments are optional. If Transaction is
-specified and Ticket is not then ticket of the transaction is
-used, but only if the transaction belongs to a ticket.
-
-Returns 1 on success, 0 on error or -1 if message has no recipients
-and hasn't been sent.
-
-=head3 Signing and Encrypting
-
-This function as well signs and/or encrypts the message according to
-headers of a transaction's attachment or properties of a ticket's queue.
-To get full access to the configuration Ticket and/or Transaction
-arguments must be provided, but you can force behaviour using Sign
-and/or Encrypt arguments.
-
-The following precedence of arguments are used to figure out if
-the message should be encrypted and/or signed:
-
-* if Sign or Encrypt argument is defined then its value is used
-
-* else if Transaction's first attachment has X-RT-Sign or X-RT-Encrypt
-header field then it's value is used
-
-* else properties of a queue of the Ticket are used.
-
-=cut
-
-sub SendEmail {
- my (%args) = (
- Entity => undef,
- Bounce => 0,
- Ticket => undef,
- Transaction => undef,
- @_,
- );
-
- my $TicketObj = $args{'Ticket'};
- my $TransactionObj = $args{'Transaction'};
-
- foreach my $arg( qw(Entity Bounce) ) {
- next unless defined $args{ lc $arg };
-
- $RT::Logger->warning("'". lc($arg) ."' argument is deprecated, use '$arg' instead");
- $args{ $arg } = delete $args{ lc $arg };
- }
-
- unless ( $args{'Entity'} ) {
- $RT::Logger->crit( "Could not send mail without 'Entity' object" );
- return 0;
- }
-
- my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
- chomp $msgid;
+ my %args = (To => $RT::OwnerEmail,
+ Bcc => undef,
+ From => $RT::CorrespondAddress,
+ Subject => 'There has been an error',
+ Explanation => 'Unexplained error',
+ MIMEObj => undef,
+ LogLevel => 'crit',
+ @_);
+
+
+ $RT::Logger->log(level => $args{'LogLevel'},
+ message => $args{'Explanation'}
+ );
+ my $entity = MIME::Entity->build( Type =>"multipart/mixed",
+ From => $args{'From'},
+ Bcc => $args{'Bcc'},
+ To => $args{'To'},
+ Subject => $args{'Subject'},
+ 'X-RT-Loop-Prevention' => $RT::rtname,
+ );
+
+ $entity->attach( Data => $args{'Explanation'}."\n");
- # If we don't have any recipients to send to, don't send a message;
- unless ( $args{'Entity'}->head->get('To')
- || $args{'Entity'}->head->get('Cc')
- || $args{'Entity'}->head->get('Bcc') )
- {
- $RT::Logger->info( $msgid . " No recipients found. Not sending." );
- return -1;
- }
-
- if ( $TransactionObj && !$TicketObj
- && $TransactionObj->ObjectType eq 'RT::Ticket' )
- {
- $TicketObj = $TransactionObj->Object;
- }
-
- if ( RT->Config->Get('GnuPG')->{'Enable'} ) {
- my %crypt;
-
- my $attachment;
- $attachment = $TransactionObj->Attachments->First
- if $TransactionObj;
-
- foreach my $argument ( qw(Sign Encrypt) ) {
- next if defined $args{ $argument };
-
- if ( $attachment && defined $attachment->GetHeader("X-RT-$argument") ) {
- $crypt{$argument} = $attachment->GetHeader("X-RT-$argument");
- } elsif ( $TicketObj ) {
- $crypt{$argument} = $TicketObj->QueueObj->$argument();
- }
- }
-
- my $res = SignEncrypt( %args, %crypt );
- return $res unless $res > 0;
- }
-
- unless ( $args{'Entity'}->head->get('Date') ) {
- require RT::Date;
- my $date = RT::Date->new( $RT::SystemUser );
- $date->SetToNow;
- $args{'Entity'}->head->set( 'Date', $date->RFC2822( Timezone => 'server' ) );
- }
-
- my $mail_command = RT->Config->Get('MailCommand');
-
- if ($mail_command eq 'testfile') {
- $Mail::Mailer::testfile::config{outfile} = File::Temp->new;
- }
-
- # if it is a sub routine, we just return it;
- return $mail_command->($args{'Entity'}) if UNIVERSAL::isa( $mail_command, 'CODE' );
-
- if ( $mail_command eq 'sendmailpipe' ) {
- my $path = RT->Config->Get('SendmailPath');
- my $args = RT->Config->Get('SendmailArguments');
-
- # SetOutgoingMailFrom
- if ( RT->Config->Get('SetOutgoingMailFrom') ) {
- my $OutgoingMailAddress;
-
- if ($TicketObj) {
- my $QueueName = $TicketObj->QueueObj->Name;
- my $QueueAddressOverride = RT->Config->Get('OverrideOutgoingMailFrom')->{$QueueName};
-
- if ($QueueAddressOverride) {
- $OutgoingMailAddress = $QueueAddressOverride;
- } else {
- $OutgoingMailAddress = $TicketObj->QueueObj->CorrespondAddress;
- }
- }
-
- $OutgoingMailAddress ||= RT->Config->Get('OverrideOutgoingMailFrom')->{'Default'};
-
- $args .= " -f $OutgoingMailAddress"
- if $OutgoingMailAddress;
- }
-
- # Set Bounce Arguments
- $args .= ' '. RT->Config->Get('SendmailBounceArguments') if $args{'Bounce'};
-
- # VERP
- if ( $TransactionObj and
- my $prefix = RT->Config->Get('VERPPrefix') and
- my $domain = RT->Config->Get('VERPDomain') )
- {
- my $from = $TransactionObj->CreatorObj->EmailAddress;
- $from =~ s/@/=/g;
- $from =~ s/\s//g;
- $args .= " -f $prefix$from\@$domain";
- }
-
- eval {
- # don't ignore CHLD signal to get proper exit code
- local $SIG{'CHLD'} = 'DEFAULT';
-
- open my $mail, "|$path $args" or die "couldn't execute program: $!";
-
- # if something wrong with $mail->print we will get PIPE signal, handle it
- local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
- $args{'Entity'}->print($mail);
-
- unless ( close $mail ) {
- die "close pipe failed: $!" if $!; # system error
- # sendmail exit statuses mostly errors with data not software
- # TODO: status parsing: core dump, exit on signal or EX_*
- my $msg = "$msgid: `$path $args` exitted with code ". ($?>>8);
- $msg = ", interrupted by signal ". ($?&127) if $?&127;
- $RT::Logger->error( $msg );
- }
- };
- if ( $@ ) {
- $RT::Logger->crit( "$msgid: Could not send mail with command `$path $args`: " . $@ );
- return 0;
- }
+ my $mimeobj = $args{'MIMEObj'};
+ if ($mimeobj) {
+ $mimeobj->sync_headers();
+ $entity->add_part($mimeobj);
}
- elsif ( $mail_command eq 'smtp' ) {
- require Net::SMTP;
- my $smtp = do { local $@; eval { Net::SMTP->new(
- Host => RT->Config->Get('SMTPServer'),
- Debug => RT->Config->Get('SMTPDebug'),
- ) } };
- unless ( $smtp ) {
- $RT::Logger->crit( "Could not connect to SMTP server.");
- return 0;
- }
-
- # duplicate head as we want drop Bcc field
- my $head = $args{'Entity'}->head->dup;
- my @recipients = map $_->address, map
- Email::Address->parse($head->get($_)), qw(To Cc Bcc);
- $head->delete('Bcc');
-
- my $sender = RT->Config->Get('SMTPFrom')
- || $args{'Entity'}->head->get('From');
- chomp $sender;
-
- my $status = $smtp->mail( $sender )
- && $smtp->recipient( @recipients );
-
- if ( $status ) {
- $smtp->data;
- my $fh = $smtp->tied_fh;
- $head->print( $fh );
- print $fh "\n";
- $args{'Entity'}->print_body( $fh );
- $smtp->dataend;
- }
- $smtp->quit;
-
- unless ( $status ) {
- $RT::Logger->crit( "$msgid: Could not send mail via SMTP." );
- return 0;
- }
+
+ if ($RT::MailCommand eq 'sendmailpipe') {
+ open (MAIL, "|$RT::SendmailPath $RT::SendmailArguments") || return(0);
+ print MAIL $entity->as_string;
+ close(MAIL);
}
else {
- local ($ENV{'MAILADDRESS'}, $ENV{'PERL_MAILERS'});
-
- my @mailer_args = ($mail_command);
- if ( $mail_command eq 'sendmail' ) {
- $ENV{'PERL_MAILERS'} = RT->Config->Get('SendmailPath');
- push @mailer_args, split(/\s+/, RT->Config->Get('SendmailArguments'));
- }
- else {
- push @mailer_args, RT->Config->Get('MailParams');
- }
-
- unless ( $args{'Entity'}->send( @mailer_args ) ) {
- $RT::Logger->crit( "$msgid: Could not send mail." );
- return 0;
- }
- }
- return 1;
-}
-
-=head2 PrepareEmailUsingTemplate Template => '', Arguments => {}
-
-Loads a template. Parses it using arguments if it's not empty.
-Returns a tuple (L<RT::Template> object, error message).
-
-Note that even if a template object is returned MIMEObj method
-may return undef for empty templates.
-
-=cut
-
-sub PrepareEmailUsingTemplate {
- my %args = (
- Template => '',
- Arguments => {},
- @_
- );
-
- my $template = RT::Template->new( $RT::SystemUser );
- $template->LoadGlobalTemplate( $args{'Template'} );
- unless ( $template->id ) {
- return (undef, "Couldn't load template '". $args{'Template'} ."'");
- }
- return $template if $template->IsEmpty;
-
- my ($status, $msg) = $template->Parse( %{ $args{'Arguments'} } );
- return (undef, $msg) unless $status;
-
- return $template;
-}
-
-=head2 SendEmailUsingTemplate Template => '', Arguments => {}, From => CorrespondAddress, To => '', Cc => '', Bcc => ''
-
-Sends email using a template, takes name of template, arguments for it and recipients.
-
-=cut
-
-sub SendEmailUsingTemplate {
- my %args = (
- Template => '',
- Arguments => {},
- To => undef,
- Cc => undef,
- Bcc => undef,
- From => RT->Config->Get('CorrespondAddress'),
- InReplyTo => undef,
- @_
- );
-
- my ($template, $msg) = PrepareEmailUsingTemplate( %args );
- return (0, $msg) unless $template;
-
- my $mail = $template->MIMEObj;
- unless ( $mail ) {
- $RT::Logger->info("Message is not sent as template #". $template->id ." is empty");
- return -1;
+ $entity->send($RT::MailCommand, $RT::MailParams);
}
-
- $mail->head->set( $_ => $args{ $_ } )
- foreach grep defined $args{$_}, qw(To Cc Bcc From);
-
- SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
-
- return SendEmail( Entity => $mail );
}
-=head2 ForwardTransaction TRANSACTION, To => '', Cc => '', Bcc => ''
-
-Forwards transaction with all attachments as 'message/rfc822'.
-
-=cut
+# }}}
-sub ForwardTransaction {
- my $txn = shift;
- my %args = ( To => '', Cc => '', Bcc => '', @_ );
-
- my $entity = $txn->ContentAsMIME;
-
- return SendForward( %args, Entity => $entity, Transaction => $txn );
-}
-
-=head2 ForwardTicket TICKET, To => '', Cc => '', Bcc => ''
-
-Forwards a ticket's Create and Correspond Transactions and their Attachments as 'message/rfc822'.
-
-=cut
-
-sub ForwardTicket {
- my $ticket = shift;
- my %args = ( To => '', Cc => '', Bcc => '', @_ );
-
- my $txns = $ticket->Transactions;
- $txns->Limit(
- FIELD => 'Type',
- VALUE => $_,
- ) for qw(Create Correspond);
-
- my $entity = MIME::Entity->build(
- Type => 'multipart/mixed',
- );
- $entity->add_part( $_ ) foreach
- map $_->ContentAsMIME,
- @{ $txns->ItemsArrayRef };
-
- return SendForward( %args, Entity => $entity, Ticket => $ticket, Template => 'Forward Ticket' );
-}
-
-=head2 SendForward Entity => undef, Ticket => undef, Transaction => undef, Template => undef, To => '', Cc => '', Bcc => ''
-
-Forwards an Entity representing Ticket or Transaction as 'message/rfc822'. Entity is wrapped into Template.
-
-=cut
-
-sub SendForward {
- my (%args) = (
- Entity => undef,
- Ticket => undef,
- Transaction => undef,
- Template => 'Forward',
- To => '', Cc => '', Bcc => '',
- @_
- );
-
- my $txn = $args{'Transaction'};
- my $ticket = $args{'Ticket'};
- $ticket ||= $txn->Object if $txn;
-
- my $entity = $args{'Entity'};
- unless ( $entity ) {
- require Carp;
- $RT::Logger->error(Carp::longmess("No entity provided"));
- return (0, $ticket->loc("Couldn't send email"));
- }
-
- my ($template, $msg) = PrepareEmailUsingTemplate(
- Template => $args{'Template'},
- Arguments => {
- Ticket => $ticket,
- Transaction => $txn,
- },
- );
-
- my $mail;
- if ( $template ) {
- $mail = $template->MIMEObj;
- } else {
- $RT::Logger->warning($msg);
- }
- unless ( $mail ) {
- $RT::Logger->warning("Couldn't generate email using template '$args{Template}'");
-
- my $description;
- unless ( $args{'Transaction'} ) {
- $description = 'This is forward of ticket #'. $ticket->id;
- } else {
- $description = 'This is forward of transaction #'
- . $txn->id ." of a ticket #". $txn->ObjectId;
- }
- $mail = MIME::Entity->build(
- Type => 'text/plain',
- Data => $description,
- );
- }
-
- $mail->head->set( $_ => EncodeToMIME( String => $args{$_} ) )
- foreach grep defined $args{$_}, qw(To Cc Bcc);
-
- $mail->attach(
- Type => 'message/rfc822',
- Disposition => 'attachment',
- Description => 'forwarded message',
- Data => $entity->as_string,
- );
-
- my $from;
- my $subject = '';
- $subject = $txn->Subject if $txn;
- $subject ||= $ticket->Subject if $ticket;
- if ( RT->Config->Get('ForwardFromUser') ) {
- $from = ($txn || $ticket)->CurrentUser->UserObj->EmailAddress;
- } else {
- # XXX: what if want to forward txn of other object than ticket?
- $subject = AddSubjectTag( $subject, $ticket );
- $from = $ticket->QueueObj->CorrespondAddress
- || RT->Config->Get('CorrespondAddress');
- }
- $mail->head->set( Subject => EncodeToMIME( String => "Fwd: $subject" ) );
- $mail->head->set( From => EncodeToMIME( String => $from ) );
-
- my $status = RT->Config->Get('ForwardFromUser')
- # never sign if we forward from User
- ? SendEmail( %args, Entity => $mail, Sign => 0 )
- : SendEmail( %args, Entity => $mail );
- return (0, $ticket->loc("Couldn't send email")) unless $status;
- return (1, $ticket->loc("Send email successfully"));
-}
-
-=head2 SignEncrypt Entity => undef, Sign => 0, Encrypt => 0
-
-Signs and encrypts message using L<RT::Crypt::GnuPG>, but as well
-handle errors with users' keys.
-
-If a recipient has no key or has other problems with it, then the
-unction sends a error to him using 'Error: public key' template.
-Also, notifies RT's owner using template 'Error to RT owner: public key'
-to inform that there are problems with users' keys. Then we filter
-all bad recipients and retry.
-
-Returns 1 on success, 0 on error and -1 if all recipients are bad and
-had been filtered out.
-
-=cut
-
-sub SignEncrypt {
- my %args = (
- Entity => undef,
- Sign => 0,
- Encrypt => 0,
- @_
- );
- return 1 unless $args{'Sign'} || $args{'Encrypt'};
-
- my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
- chomp $msgid;
-
- $RT::Logger->debug("$msgid Signing message") if $args{'Sign'};
- $RT::Logger->debug("$msgid Encrypting message") if $args{'Encrypt'};
-
- require RT::Crypt::GnuPG;
- my %res = RT::Crypt::GnuPG::SignEncrypt( %args );
- return 1 unless $res{'exit_code'};
-
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
-
- my @bad_recipients;
- foreach my $line ( @status ) {
- # if the passphrase fails, either you have a bad passphrase
- # or gpg-agent has died. That should get caught in Create and
- # Update, but at least throw an error here
- if (($line->{'Operation'}||'') eq 'PassphraseCheck'
- && $line->{'Status'} =~ /^(?:BAD|MISSING)$/ ) {
- $RT::Logger->error( "$line->{'Status'} PASSPHRASE: $line->{'Message'}" );
- return 0;
- }
- next unless ($line->{'Operation'}||'') eq 'RecipientsCheck';
- next if $line->{'Status'} eq 'DONE';
- $RT::Logger->error( $line->{'Message'} );
- push @bad_recipients, $line;
- }
- return 0 unless @bad_recipients;
-
- $_->{'AddressObj'} = (Email::Address->parse( $_->{'Recipient'} ))[0]
- foreach @bad_recipients;
-
- foreach my $recipient ( @bad_recipients ) {
- my $status = SendEmailUsingTemplate(
- To => $recipient->{'AddressObj'}->address,
- Template => 'Error: public key',
- Arguments => {
- %$recipient,
- TicketObj => $args{'Ticket'},
- TransactionObj => $args{'Transaction'},
- },
- );
- unless ( $status ) {
- $RT::Logger->error("Couldn't send 'Error: public key'");
- }
- }
-
- my $status = SendEmailUsingTemplate(
- To => RT->Config->Get('OwnerEmail'),
- Template => 'Error to RT owner: public key',
- Arguments => {
- BadRecipients => \@bad_recipients,
- TicketObj => $args{'Ticket'},
- TransactionObj => $args{'Transaction'},
- },
- );
- unless ( $status ) {
- $RT::Logger->error("Couldn't send 'Error to RT owner: public key'");
- }
-
- DeleteRecipientsFromHead(
- $args{'Entity'}->head,
- map $_->{'AddressObj'}->address, @bad_recipients
- );
-
- unless ( $args{'Entity'}->head->get('To')
- || $args{'Entity'}->head->get('Cc')
- || $args{'Entity'}->head->get('Bcc') )
- {
- $RT::Logger->debug("$msgid No recipients that have public key, not sending");
- return -1;
- }
-
- # redo without broken recipients
- %res = RT::Crypt::GnuPG::SignEncrypt( %args );
- return 0 if $res{'exit_code'};
-
- return 1;
-}
-
-use MIME::Words ();
-
-=head2 EncodeToMIME
-
-Takes a hash with a String and a Charset. Returns the string encoded
-according to RFC2047, using B (base64 based) encoding.
-
-String must be a perl string, octets are returned.
-
-If Charset is not provided then $EmailOutputEncoding config option
-is used, or "latin-1" if that is not set.
-
-=cut
-
-sub EncodeToMIME {
- my %args = (
- String => undef,
- Charset => undef,
- @_
- );
- my $value = $args{'String'};
- return $value unless $value; # 0 is perfect ascii
- my $charset = $args{'Charset'} || RT->Config->Get('EmailOutputEncoding');
- my $encoding = 'B';
-
- # using RFC2047 notation, sec 2.
- # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
-
- # An 'encoded-word' may not be more than 75 characters long
- #
- # MIME encoding increases 4/3*(number of bytes), and always in multiples
- # of 4. Thus we have to find the best available value of bytes available
- # for each chunk.
- #
- # First we get the integer max which max*4/3 would fit on space.
- # Then we find the greater multiple of 3 lower or equal than $max.
- my $max = int(
- ( ( 75 - length( '=?' . $charset . '?' . $encoding . '?' . '?=' ) )
- * 3
- ) / 4
- );
- $max = int( $max / 3 ) * 3;
-
- chomp $value;
-
- if ( $max <= 0 ) {
-
- # gives an error...
- $RT::Logger->crit("Can't encode! Charset or encoding too big.");
- return ($value);
- }
-
- return ($value) unless $value =~ /[^\x20-\x7e]/;
-
- $value =~ s/\s+$//;
-
- # we need perl string to split thing char by char
- Encode::_utf8_on($value) unless Encode::is_utf8($value);
-
- my ( $tmp, @chunks ) = ( '', () );
- while ( length $value ) {
- my $char = substr( $value, 0, 1, '' );
- my $octets = Encode::encode( $charset, $char );
- if ( length($tmp) + length($octets) > $max ) {
- push @chunks, $tmp;
- $tmp = '';
- }
- $tmp .= $octets;
- }
- push @chunks, $tmp if length $tmp;
-
- # encode an join chuncks
- $value = join "\n ",
- map MIME::Words::encode_mimeword( $_, $encoding, $charset ),
- @chunks;
- return ($value);
-}
+# {{{ Create User
sub CreateUser {
- my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;
-
- my $NewUser = RT::User->new( $RT::SystemUser );
-
- my ( $Val, $Message ) = $NewUser->Create(
- Name => ( $Username || $Address ),
- EmailAddress => $Address,
- RealName => $Name,
- Password => undef,
- Privileged => 0,
- Comments => 'Autocreated on ticket submission',
- );
-
+ my ($Username, $Address, $Name, $ErrorsTo, $entity) = @_;
+ my $NewUser = RT::User->new($RT::SystemUser);
+
+ # This data is tainted by some Very Broken mailers.
+ # (Sometimes they send raw ISO 8859-1 data here. fear that.
+ require Encode;
+ $Username = Encode::encode(utf8 => $Username, Encode::FB_PERLQQ()) if defined $Username;
+ $Name = Encode::encode(utf8 => $Name, Encode::FB_PERLQQ()) if defined $Name;
+
+ my ($Val, $Message) =
+ $NewUser->Create(Name => ($Username || $Address),
+ EmailAddress => $Address,
+ RealName => $Name,
+ Password => undef,
+ Privileged => 0,
+ Comments => 'Autocreated on ticket submission'
+ );
+
unless ($Val) {
-
+
# Deal with the race condition of two account creations at once
+ #
if ($Username) {
$NewUser->LoadByName($Username);
}
-
- unless ( $NewUser->Id ) {
+
+ unless ($NewUser->Id) {
$NewUser->LoadByEmail($Address);
}
-
- unless ( $NewUser->Id ) {
- MailError(
- To => $ErrorsTo,
- Subject => "User could not be created",
- Explanation =>
- "User creation failed in mailgateway: $Message",
- MIMEObj => $entity,
- LogLevel => 'crit',
- );
+
+ unless ($NewUser->Id) {
+ MailError( To => $ErrorsTo,
+ Subject => "User could not be created",
+ Explanation => "User creation failed in mailgateway: $Message",
+ MIMEObj => $entity,
+ LogLevel => 'crit'
+ );
}
}
#Load the new user object
- my $CurrentUser = new RT::CurrentUser;
- $CurrentUser->LoadByEmail( $Address );
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByEmail($Address);
- unless ( $CurrentUser->id ) {
- $RT::Logger->warning(
- "Couldn't load user '$Address'." . "giving up" );
- MailError(
- To => $ErrorsTo,
- Subject => "User could not be loaded",
- Explanation =>
- "User '$Address' could not be loaded in the mail gateway",
- MIMEObj => $entity,
- LogLevel => 'crit'
- );
+ unless ($CurrentUser->id) {
+ $RT::Logger->warning("Couldn't load user '$Address'.". "giving up");
+ MailError( To => $ErrorsTo,
+ Subject => "User could not be loaded",
+ Explanation => "User '$Address' could not be loaded in the mail gateway",
+ MIMEObj => $entity,
+ LogLevel => 'crit'
+ );
}
return $CurrentUser;
}
+# }}}
+# {{{ ParseCcAddressesFromHead
+=head2 ParseCcAddressesFromHead HASHREF
-
-=head2 ParseCcAddressesFromHead HASH
-
-Takes a hash containing QueueObj, Head and CurrentUser objects.
-Returns a list of all email addresses in the To and Cc
-headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
+Takes a hashref object containing QueueObj, Head and CurrentUser objects.
+Returns a list of all email addresses in the To and Cc
+headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
email address and anything that the configuration sub RT::IsRTAddress matches.
=cut
-
+
sub ParseCcAddressesFromHead {
- my %args = (
- Head => undef,
- QueueObj => undef,
- CurrentUser => undef,
- @_
- );
-
- my @recipients =
- map lc $_->address,
- map Email::Address->parse( $args{'Head'}->get( $_ ) ),
- qw(To Cc);
-
- my @res;
- foreach my $address ( @recipients ) {
- $address = $args{'CurrentUser'}->UserObj->CanonicalizeEmailAddress( $address );
- next if lc $args{'CurrentUser'}->EmailAddress eq $address;
- next if lc $args{'QueueObj'}->CorrespondAddress eq $address;
- next if lc $args{'QueueObj'}->CommentAddress eq $address;
- next if RT::EmailParser->IsRTAddress( $address );
-
- push @res, $address;
- }
- return @res;
+ my %args = ( Head => undef,
+ QueueObj => undef,
+ CurrentUser => undef,
+ @_ );
+
+ my (@Addresses);
+
+ my @ToObjs = Mail::Address->parse($args{'Head'}->get('To'));
+ my @CcObjs = Mail::Address->parse($args{'Head'}->get('Cc'));
+
+ foreach my $AddrObj (@ToObjs, @CcObjs) {
+ my $Address = $AddrObj->address;
+ $Address = $args{'CurrentUser'}->UserObj->CanonicalizeEmailAddress($Address);
+ next if ($args{'CurrentUser'}->EmailAddress =~ /^$Address$/i);
+ next if ($args{'QueueObj'}->CorrespondAddress =~ /^$Address$/i);
+ next if ($args{'QueueObj'}->CommentAddress =~ /^$Address$/i);
+ next if (RT::EmailParser::IsRTAddress(undef, $Address));
+
+ push (@Addresses, $Address);
+ }
+ return (@Addresses);
}
+# }}}
+
+# {{{ ParseSenderAdddressFromHead
-=head2 ParseSenderAddressFromHead HEAD
+=head2 ParseSenderAddressFromHead
-Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
+Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
of the From (evaluated in order of Reply-To:, From:, Sender)
=cut
sub ParseSenderAddressFromHead {
my $head = shift;
-
#Figure out who's sending this message.
- foreach my $header ('Reply-To', 'From', 'Sender') {
- my $addr_line = $head->get($header) || next;
- my ($addr, $name) = ParseAddressFromHeader( $addr_line );
- # only return if the address is not empty
- return ($addr, $name) if $addr;
- }
-
- return (undef, undef);
+ my $From = $head->get('Reply-To') ||
+ $head->get('From') ||
+ $head->get('Sender');
+ return (ParseAddressFromHeader($From));
}
+# }}}
+
+# {{{ ParseErrorsToAdddressFromHead
-=head2 ParseErrorsToAddressFromHead HEAD
+=head2 ParseErrorsToAddressFromHead
Takes a MIME::Header object. Return a single value : user@host
-of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
-From:, Sender)
+of the From (evaluated in order of Errors-To:,Reply-To:, From:, Sender)
=cut
sub ParseErrorsToAddressFromHead {
my $head = shift;
-
#Figure out who's sending this message.
- foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
-
- # If there's a header of that name
- my $headerobj = $head->get($header);
- if ($headerobj) {
- my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
-
- # If it's got actual useful content...
- return ($addr) if ($addr);
- }
+ foreach my $header ('Errors-To' , 'Reply-To', 'From', 'Sender' ) {
+ # If there's a header of that name
+ my $headerobj = $head->get($header);
+ if ($headerobj) {
+ my ($addr, $name ) = ParseAddressFromHeader($headerobj);
+ # If it's got actual useful content...
+ return ($addr) if ($addr);
+ }
}
}
+# }}}
-
+# {{{ ParseAddressFromHeader
=head2 ParseAddressFromHeader ADDRESS
-Takes an address from C<$head->get('Line')> and returns a tuple: user@host, friendly name
+Takes an address from $head->get('Line') and returns a tuple: user@host, friendly name
=cut
-sub ParseAddressFromHeader {
- my $Addr = shift;
- # Some broken mailers send: ""Vincent, Jesse"" <jesse@fsck.com>. Hate
- $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
- my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
+sub ParseAddressFromHeader{
+ my $Addr = shift;
+
+ my @Addresses = Mail::Address->parse($Addr);
+
+ my $AddrObj = $Addresses[0];
- my ($AddrObj) = grep ref $_, @Addresses;
- unless ( $AddrObj ) {
- return ( undef, undef );
+ unless (ref($AddrObj)) {
+ return(undef,undef);
}
-
- my $Name = ( $AddrObj->name || $AddrObj->phrase || $AddrObj->comment || $AddrObj->address );
-
+
+ my $Name = ($AddrObj->phrase || $AddrObj->comment || $AddrObj->address);
+
#Lets take the from and load a user object.
my $Address = $AddrObj->address;
- return ( $Address, $Name );
-}
-
-=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
-
-Gets a head object and list of addresses.
-Deletes addresses from To, Cc or Bcc fields.
-
-=cut
-
-sub DeleteRecipientsFromHead {
- my $head = shift;
- my %skip = map { lc $_ => 1 } @_;
-
- foreach my $field ( qw(To Cc Bcc) ) {
- $head->set( $field =>
- join ', ', map $_->format, grep !$skip{ lc $_->address },
- Email::Address->parse( $head->get( $field ) )
- );
- }
-}
-
-sub GenMessageId {
- my %args = (
- Ticket => undef,
- Scrip => undef,
- ScripAction => undef,
- @_
- );
- my $org = RT->Config->Get('Organization');
- my $ticket_id = ( ref $args{'Ticket'}? $args{'Ticket'}->id : $args{'Ticket'} ) || 0;
- my $scrip_id = ( ref $args{'Scrip'}? $args{'Scrip'}->id : $args{'Scrip'} ) || 0;
- my $sent = ( ref $args{'ScripAction'}? $args{'ScripAction'}->{'_Message_ID'} : 0 ) || 0;
-
- return "<rt-". $RT::VERSION ."-". $$ ."-". CORE::time() ."-". int(rand(2000)) .'.'
- . $ticket_id ."-". $scrip_id ."-". $sent ."@". $org .">" ;
+ return ($Address, $Name);
}
+# }}}
-sub SetInReplyTo {
- my %args = (
- Message => undef,
- InReplyTo => undef,
- Ticket => undef,
- @_
- );
- return unless $args{'Message'} && $args{'InReplyTo'};
-
- my $get_header = sub {
- my @res;
- if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
- @res = $args{'InReplyTo'}->head->get( shift );
- } else {
- @res = $args{'InReplyTo'}->GetHeader( shift ) || '';
- }
- return grep length, map { split /\s+/m, $_ } grep defined, @res;
- };
-
- my @id = $get_header->('Message-ID');
- #XXX: custom header should begin with X- otherwise is violation of the standard
- my @rtid = $get_header->('RT-Message-ID');
- my @references = $get_header->('References');
- unless ( @references ) {
- @references = $get_header->('In-Reply-To');
- }
- push @references, @id, @rtid;
- if ( $args{'Ticket'} ) {
- my $pseudo_ref = '<RT-Ticket-'. $args{'Ticket'}->id .'@'. RT->Config->Get('Organization') .'>';
- push @references, $pseudo_ref unless grep $_ eq $pseudo_ref, @references;
- }
- @references = splice @references, 4, -6
- if @references > 10;
-
- my $mail = $args{'Message'};
- $mail->head->set( 'In-Reply-To' => join ' ', @rtid? (@rtid) : (@id) ) if @id || @rtid;
- $mail->head->set( 'References' => join ' ', @references );
-}
-
-sub ParseTicketId {
- my $Subject = shift;
- my $rtname = RT->Config->Get('rtname');
- my $test_name = RT->Config->Get('EmailSubjectTagRegex') || qr/\Q$rtname\E/i;
-
- my $id;
- if ( $Subject =~ s/\[$test_name\s+\#(\d+)\s*\]//i ) {
- $id = $1;
- } else {
- foreach my $tag ( RT->System->SubjectTag ) {
- next unless $Subject =~ s/\[\Q$tag\E\s+\#(\d+)\s*\]//i;
- $id = $1;
- last;
- }
- }
- return undef unless $id;
-
- $RT::Logger->debug("Found a ticket ID. It's $id");
- return $id;
-}
-
-sub AddSubjectTag {
- my $subject = shift;
- my $ticket = shift;
- unless ( ref $ticket ) {
- my $tmp = RT::Ticket->new( $RT::SystemUser );
- $tmp->Load( $ticket );
- $ticket = $tmp;
- }
- my $id = $ticket->id;
- my $queue_tag = $ticket->QueueObj->SubjectTag;
-
- my $tag_re = RT->Config->Get('EmailSubjectTagRegex');
- unless ( $tag_re ) {
- my $tag = $queue_tag || RT->Config->Get('rtname');
- $tag_re = qr/\Q$tag\E/;
- } elsif ( $queue_tag ) {
- $tag_re = qr/$tag_re|\Q$queue_tag\E/;
- }
- return $subject if $subject =~ /\[$tag_re\s+#$id\]/;
-
- $subject =~ s/(\r\n|\n|\s)/ /gi;
- chomp $subject;
- return "[". ($queue_tag || RT->Config->Get('rtname')) ." #$id] $subject";
-}
-
-
-=head2 Gateway ARGSREF
-
-
-Takes parameters:
-
- action
- queue
- message
+=head2 Gateway
This performs all the "guts" of the mail rt-mailgate program, and is
designed to be called from the web interface with a message, user
object, and so on.
-Can also take an optional 'ticket' parameter; this ticket id overrides
-any ticket id found in the subject.
-
-Returns:
-
- An array of:
-
- (status code, message, optional ticket object)
-
- status code is a numeric value.
-
- for temporary failures, the status code should be -75
-
- for permanent failures which are handled by RT, the status code
- should be 0
-
- for succces, the status code should be 1
-
-
-
=cut
-sub _LoadPlugins {
- my @mail_plugins = @_;
-
- my @res;
- foreach my $plugin (@mail_plugins) {
- if ( ref($plugin) eq "CODE" ) {
- push @res, $plugin;
- } elsif ( !ref $plugin ) {
- my $Class = $plugin;
- $Class = "RT::Interface::Email::" . $Class
- unless $Class =~ /^RT::Interface::Email::/;
- $Class->require or
- do { $RT::Logger->error("Couldn't load $Class: $@"); next };
-
- no strict 'refs';
- unless ( defined *{ $Class . "::GetCurrentUser" }{CODE} ) {
- $RT::Logger->crit( "No GetCurrentUser code found in $Class module");
- next;
- }
- push @res, $Class;
- } else {
- $RT::Logger->crit( "$plugin - is not class name or code reference");
- }
- }
- return @res;
-}
-
sub Gateway {
- my $argsref = shift;
- my %args = (
- action => 'correspond',
- queue => '1',
- ticket => undef,
- message => undef,
- %$argsref
- );
-
- my $SystemTicket;
- my $Right;
+ my %args = ( message => undef,
+ queue => 1,
+ action => 'correspond',
+ ticket => undef,
+ @_ );
# Validate the action
- my ( $status, @actions ) = IsCorrectAction( $args{'action'} );
- unless ($status) {
- return (
- -75,
- "Invalid 'action' parameter "
- . $actions[0]
- . " for queue "
- . $args{'queue'},
- undef
- );
+ unless ( $args{'action'} =~ /^(comment|correspond|action)$/ ) {
+
+ # Can't safely loc this. What object do we loc around?
+ return ( 0, "Invalid 'action' parameter", undef );
}
my $parser = RT::EmailParser->new();
- $parser->SmartParseMIMEEntityFromScalar(
- Message => $args{'message'},
- Decode => 0,
- Exact => 1,
- );
+ $parser->ParseMIMEEntityFromScalar( $args{'message'} );
my $Message = $parser->Entity();
- unless ($Message) {
- MailError(
- Subject => "RT Bounce: Unparseable message",
- Explanation => "RT couldn't process the message below",
- Attach => $args{'message'}
- );
-
- return ( 0,
- "Failed to parse this message. Something is likely badly wrong with the message"
- );
- }
-
- my @mail_plugins = grep $_, RT->Config->Get('MailPlugins');
- push @mail_plugins, "Auth::MailFrom" unless @mail_plugins;
- @mail_plugins = _LoadPlugins( @mail_plugins );
-
- my %skip_plugin;
- foreach my $class( grep !ref, @mail_plugins ) {
- # check if we should apply filter before decoding
- my $check_cb = do {
- no strict 'refs';
- *{ $class . "::ApplyBeforeDecode" }{CODE};
- };
- next unless defined $check_cb;
- next unless $check_cb->(
- Message => $Message,
- RawMessageRef => \$args{'message'},
- );
-
- $skip_plugin{ $class }++;
+ my $head = $Message->head;
- my $Code = do {
- no strict 'refs';
- *{ $class . "::GetCurrentUser" }{CODE};
- };
- my ($status, $msg) = $Code->(
- Message => $Message,
- RawMessageRef => \$args{'message'},
- );
- next if $status > 0;
-
- if ( $status == -2 ) {
- return (1, $msg, undef);
- } elsif ( $status == -1 ) {
- return (0, $msg, undef);
- }
- }
- @mail_plugins = grep !$skip_plugin{"$_"}, @mail_plugins;
- $parser->_DecodeBodies;
- $parser->_PostProcessNewEntity;
+ my ( $CurrentUser, $AuthStat, $status, $error );
- my $head = $Message->head;
- my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
+ my $ErrorsTo = ParseErrorsToAddressFromHead($head);
- my $MessageId = $head->get('Message-ID')
- || "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
+ my $MessageId = $head->get('Message-Id')
+ || "<no-message-id-" . time . rand(2000) . "\@.$RT::Organization>";
#Pull apart the subject line
my $Subject = $head->get('Subject') || '';
chomp $Subject;
-
- # {{{ Lets check for mail loops of various sorts.
- my ($should_store_machine_generated_message, $IsALoop, $result);
- ( $should_store_machine_generated_message, $ErrorsTo, $result, $IsALoop ) =
- _HandleMachineGeneratedMail(
- Message => $Message,
- ErrorsTo => $ErrorsTo,
- Subject => $Subject,
- MessageId => $MessageId
- );
-
- # Do not pass loop messages to MailPlugins, to make sure the loop
- # is broken, unless $RT::StoreLoops is set.
- if ($IsALoop && !$should_store_machine_generated_message) {
- return ( 0, $result, undef );
- }
- # }}}
- $args{'ticket'} ||= ParseTicketId( $Subject );
- $SystemTicket = RT::Ticket->new( $RT::SystemUser );
- $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
- if ( $SystemTicket->id ) {
- $Right = 'ReplyToTicket';
- } else {
- $Right = 'CreateTicket';
+ $args{'ticket'} ||= $parser->ParseTicketId($Subject);
+
+ my $SystemTicket;
+ if ($args{'ticket'} ) {
+ $SystemTicket = RT::Ticket->new($RT::SystemUser);
+ $SystemTicket->Load($args{'ticket'});
}
#Set up a queue object
- my $SystemQueueObj = RT::Queue->new( $RT::SystemUser );
+ my $SystemQueueObj = RT::Queue->new($RT::SystemUser);
$SystemQueueObj->Load( $args{'queue'} );
- # We can safely have no queue of we have a known-good ticket
- unless ( $SystemTicket->id || $SystemQueueObj->id ) {
- return ( -75, "RT couldn't find the queue: " . $args{'queue'}, undef );
- }
-
- my ($AuthStat, $CurrentUser, $error) = GetAuthenticationLevel(
- MailPlugins => \@mail_plugins,
- Actions => \@actions,
- Message => $Message,
- RawMessageRef => \$args{message},
- SystemTicket => $SystemTicket,
- SystemQueue => $SystemQueueObj,
- );
-
- # {{{ If authentication fails and no new user was created, get out.
- if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
-
- # If the plugins refused to create one, they lose.
- unless ( $AuthStat == -1 ) {
- _NoAuthorizedUserFound(
- Right => $Right,
- Message => $Message,
- Requestor => $ErrorsTo,
- Queue => $args{'queue'}
- );
- }
- return ( 0, "Could not load a valid user", undef );
- }
-
- # If we got a user, but they don't have the right to say things
- if ( $AuthStat == 0 ) {
+ # We can safely have no queue of we have a known-good ticket
+ unless ( $args{'ticket'} || $SystemQueueObj->id ) {
MailError(
- To => $ErrorsTo,
- Subject => "Permission Denied",
- Explanation =>
- "You do not have permission to communicate with RT",
- MIMEObj => $Message
- );
- return (
- 0,
- "$ErrorsTo tried to submit a message to "
- . $args{'Queue'}
- . " without permission.",
- undef
- );
- }
-
-
- unless ($should_store_machine_generated_message) {
- return ( 0, $result, undef );
+ To => $RT::OwnerEmail,
+ Subject => "RT Bounce: $Subject",
+ Explanation => "RT couldn't find the queue: " . $args{'queue'},
+ MIMEObj => $Message );
+ return ( 0, "RT couldn't find the queue: " . $args{'queue'}, undef );
}
- # if plugin's updated SystemTicket then update arguments
- $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
-
- my $Ticket = RT::Ticket->new($CurrentUser);
-
- if ( !$args{'ticket'} && grep /^(comment|correspond)$/, @actions )
- {
-
- my @Cc;
- my @Requestors = ( $CurrentUser->id );
-
- if (RT->Config->Get('ParseNewMessageForTicketCcs')) {
- @Cc = ParseCcAddressesFromHead(
- Head => $head,
- CurrentUser => $CurrentUser,
- QueueObj => $SystemQueueObj
- );
- }
-
- my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
- Queue => $SystemQueueObj->Id,
- Subject => $Subject,
- Requestor => \@Requestors,
- Cc => \@Cc,
- MIMEObj => $Message
- );
- if ( $id == 0 ) {
- MailError(
- To => $ErrorsTo,
- Subject => "Ticket creation failed: $Subject",
- Explanation => $ErrStr,
- MIMEObj => $Message
- );
- return ( 0, "Ticket creation failed: $ErrStr", $Ticket );
- }
-
- # strip comments&corresponds from the actions we don't need
- # to record them if we've created the ticket just now
- @actions = grep !/^(comment|correspond)$/, @actions;
- $args{'ticket'} = $id;
-
- } elsif ( $args{'ticket'} ) {
-
- $Ticket->Load( $args{'ticket'} );
- unless ( $Ticket->Id ) {
- my $error = "Could not find a ticket with id " . $args{'ticket'};
- MailError(
- To => $ErrorsTo,
- Subject => "Message not recorded: $Subject",
- Explanation => $error,
- MIMEObj => $Message
- );
-
- return ( 0, $error );
- }
- $args{'ticket'} = $Ticket->id;
- } else {
- return ( 1, "Success", $Ticket );
- }
-
- # }}}
-
- my $unsafe_actions = RT->Config->Get('UnsafeEmailCommands');
- foreach my $action (@actions) {
-
- # If the action is comment, add a comment.
- if ( $action =~ /^(?:comment|correspond)$/i ) {
- my $method = ucfirst lc $action;
- my ( $status, $msg ) = $Ticket->$method( MIMEObj => $Message );
- unless ($status) {
-
- #Warn the sender that we couldn't actually submit the comment.
- MailError(
- To => $ErrorsTo,
- Subject => "Message not recorded: $Subject",
- Explanation => $msg,
- MIMEObj => $Message
- );
- return ( 0, "Message not recorded: $msg", $Ticket );
- }
- } elsif ($unsafe_actions) {
- my ( $status, $msg ) = _RunUnsafeAction(
- Action => $action,
- ErrorsTo => $ErrorsTo,
- Message => $Message,
- Ticket => $Ticket,
- CurrentUser => $CurrentUser,
- );
- return ($status, $msg, $Ticket) unless $status == 1;
- }
- }
- return ( 1, "Success", $Ticket );
-}
-
-=head2 GetAuthenticationLevel
-
# Authentication Level
- # -1 - Get out. this user has been explicitly declined
+ # -1 - Get out. this user has been explicitly declined
# 0 - User may not do anything (Not used at the moment)
# 1 - Normal user
# 2 - User is allowed to specify status updates etc. a la enhanced-mailgate
-=cut
-
-sub GetAuthenticationLevel {
- my %args = (
- MailPlugins => [],
- Actions => [],
- Message => undef,
- RawMessageRef => undef,
- SystemTicket => undef,
- SystemQueue => undef,
- @_,
- );
-
- my ( $CurrentUser, $AuthStat, $error );
-
- # Initalize AuthStat so comparisons work correctly
- $AuthStat = -9999999;
-
- # if plugin returns AuthStat -2 we skip action
- # NOTE: this is experimental API and it would be changed
- my %skip_action = ();
-
+ push @RT::MailPlugins, "Auth::MailFrom" unless @RT::MailPlugins;
# Since this needs loading, no matter what
- foreach (@{ $args{MailPlugins} }) {
- my ($Code, $NewAuthStat);
+
+ for (@RT::MailPlugins) {
+ my $Code;
+ my $NewAuthStat;
if ( ref($_) eq "CODE" ) {
$Code = $_;
- } else {
- no strict 'refs';
- $Code = *{ $_ . "::GetCurrentUser" }{CODE};
}
-
- foreach my $action (@{ $args{Actions} }) {
- ( $CurrentUser, $NewAuthStat ) = $Code->(
- Message => $args{Message},
- RawMessageRef => $args{RawMessageRef},
- CurrentUser => $CurrentUser,
- AuthLevel => $AuthStat,
- Action => $action,
- Ticket => $args{SystemTicket},
- Queue => $args{SystemQueue},
- );
-
-# You get the highest level of authentication you were assigned, unless you get the magic -1
-# If a module returns a "-1" then we discard the ticket, so.
- $AuthStat = $NewAuthStat
- if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
-
- last if $AuthStat == -1;
- $skip_action{$action}++ if $AuthStat == -2;
+ else {
+ $_ = "RT::Interface::Email::$_" unless /^RT::Interface::Email::/;
+ eval "require $_;";
+ if ($@) {
+ die ("Couldn't load module $_: $@");
+ next;
+ }
+ no strict 'refs';
+ if ( !defined( $Code = *{ $_ . "::GetCurrentUser" }{CODE} ) ) {
+ die ("No GetCurrentUser code found in $_ module");
+ next;
+ }
}
- # strip actions we should skip
- @{$args{Actions}} = grep !$skip_action{$_}, @{$args{Actions}}
- if $AuthStat == -2;
- last unless @{$args{Actions}};
+ ( $CurrentUser, $NewAuthStat ) = $Code->( Message => $Message,
+ CurrentUser => $CurrentUser,
+ AuthLevel => $AuthStat,
+ Action => $args{'action'},
+ Ticket => $SystemTicket,
+ Queue => $SystemQueueObj );
+ # You get the highest level of authentication you were assigned.
last if $AuthStat == -1;
+ $AuthStat = $NewAuthStat if $NewAuthStat > $AuthStat;
}
- return $AuthStat if !wantarray;
-
- return ($AuthStat, $CurrentUser, $error);
-}
-
-sub _RunUnsafeAction {
- my %args = (
- Action => undef,
- ErrorsTo => undef,
- Message => undef,
- Ticket => undef,
- CurrentUser => undef,
- @_
- );
-
- if ( $args{'Action'} =~ /^take$/i ) {
- my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
- unless ($status) {
- MailError(
- To => $args{'ErrorsTo'},
- Subject => "Ticket not taken",
- Explanation => $msg,
- MIMEObj => $args{'Message'}
- );
- return ( 0, "Ticket not taken" );
- }
- } elsif ( $args{'Action'} =~ /^resolve$/i ) {
- my ( $status, $msg ) = $args{'Ticket'}->SetStatus('resolved');
- unless ($status) {
-
- #Warn the sender that we couldn't actually submit the comment.
- MailError(
- To => $args{'ErrorsTo'},
- Subject => "Ticket not resolved",
- Explanation => $msg,
- MIMEObj => $args{'Message'}
- );
- return ( 0, "Ticket not resolved" );
- }
- } else {
- return ( 0, "Not supported unsafe action $args{'Action'}", $args{'Ticket'} );
- }
- return ( 1, "Success" );
-}
-
-=head2 _NoAuthorizedUserFound
-
-Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
-
-=cut
-
-sub _NoAuthorizedUserFound {
- my %args = (
- Right => undef,
- Message => undef,
- Requestor => undef,
- Queue => undef,
- @_
- );
-
- # Notify the RT Admin of the failure.
- MailError(
- To => RT->Config->Get('OwnerEmail'),
- Subject => "Could not load a valid user",
- Explanation => <<EOT,
-RT could not load a valid user, and RT's configuration does not allow
-for the creation of a new user for this email (@{[$args{Requestor}]}).
-
-You might need to grant 'Everyone' the right '@{[$args{Right}]}' for the
-queue @{[$args{'Queue'}]}.
+ # {{{ If authentication fails and no new user was created, get out.
+ if ( !$CurrentUser or !$CurrentUser->Id or $AuthStat == -1 ) {
-EOT
- MIMEObj => $args{'Message'},
- LogLevel => 'error'
- );
-
- # Also notify the requestor that his request has been dropped.
- if ($args{'Requestor'} ne RT->Config->Get('OwnerEmail')) {
- MailError(
- To => $args{'Requestor'},
- Subject => "Could not load a valid user",
- Explanation => <<EOT,
+ # If the plugins refused to create one, they lose.
+ MailError(
+ Subject => "Could not load a valid user",
+ Explanation => <<EOT,
RT could not load a valid user, and RT's configuration does not allow
for the creation of a new user for your email.
+Your RT administrator needs to grant 'Everyone' the right 'CreateTicket'
+for this queue.
+
EOT
- MIMEObj => $args{'Message'},
- LogLevel => 'error'
- );
+ MIMEObj => $Message,
+ LogLevel => 'error' )
+ unless $AuthStat == -1;
+ return ( 0, "Could not load a valid user", undef );
}
-}
-=head2 _HandleMachineGeneratedMail
-
-Takes named params:
- Message
- ErrorsTo
- Subject
-
-Checks the message to see if it's a bounce, if it looks like a loop, if it's autogenerated, etc.
-Returns a triple of ("Should we continue (boolean)", "New value for $ErrorsTo", "Status message",
-"This message appears to be a loop (boolean)" );
-
-=cut
-
-sub _HandleMachineGeneratedMail {
- my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
- my $head = $args{'Message'}->head;
- my $ErrorsTo = $args{'ErrorsTo'};
-
- my $IsBounce = CheckForBounce($head);
+ # }}}
+ # {{{ Lets check for mail loops of various sorts.
my $IsAutoGenerated = CheckForAutoGenerated($head);
my $IsSuspiciousSender = CheckForSuspiciousSender($head);
@@ -1704,77 +496,153 @@ sub _HandleMachineGeneratedMail {
my $SquelchReplies = 0;
- my $owner_mail = RT->Config->Get('OwnerEmail');
-
#If the message is autogenerated, we need to know, so we can not
# send mail to the sender
- if ( $IsBounce || $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
+ if ( $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
$SquelchReplies = 1;
- $ErrorsTo = $owner_mail;
+ $ErrorsTo = $RT::OwnerEmail;
+ }
+
+ # }}}
+
+ # {{{ Drop it if it's disallowed
+ if ( $AuthStat == 0 ) {
+ MailError(
+ To => $ErrorsTo,
+ Subject => "Permission Denied",
+ Explanation => "You do not have permission to communicate with RT",
+ MIMEObj => $Message );
}
+ # }}}
+ # {{{ Warn someone if it's a loop
+
# Warn someone if it's a loop, before we drop it on the ground
if ($IsALoop) {
- $RT::Logger->crit("RT Received mail (".$args{MessageId}.") from itself.");
+ $RT::Logger->crit("RT Recieved mail ($MessageId) from itself.");
#Should we mail it to RTOwner?
- if ( RT->Config->Get('LoopsToRTOwner') ) {
- MailError(
- To => $owner_mail,
- Subject => "RT Bounce: ".$args{'Subject'},
- Explanation => "RT thinks this message may be a bounce",
- MIMEObj => $args{Message}
- );
- }
+ if ($RT::LoopsToRTOwner) {
+ MailError( To => $RT::OwnerEmail,
+ Subject => "RT Bounce: $Subject",
+ Explanation => "RT thinks this message may be a bounce",
+ MIMEObj => $Message );
- #Do we actually want to store it?
- return ( 0, $ErrorsTo, "Message Bounced", $IsALoop )
- unless RT->Config->Get('StoreLoops');
+ #Do we actually want to store it?
+ return ( 0, "Message Bounced", undef ) unless ($RT::StoreLoops);
+ }
}
- # Squelch replies if necessary
+ # }}}
+
+ # {{{ Squelch replies if necessary
# Don't let the user stuff the RT-Squelch-Replies-To header.
if ( $head->get('RT-Squelch-Replies-To') ) {
- $head->add(
- 'RT-Relocated-Squelch-Replies-To',
- $head->get('RT-Squelch-Replies-To')
- );
+ $head->add( 'RT-Relocated-Squelch-Replies-To',
+ $head->get('RT-Squelch-Replies-To') );
$head->delete('RT-Squelch-Replies-To');
}
if ($SquelchReplies) {
+ ## TODO: This is a hack. It should be some other way to
+ ## indicate that the transaction should be "silent".
- # Squelch replies to the sender, and also leave a clue to
- # allow us to squelch ALL outbound messages. This way we
- # can punt the logic of "what to do when we get a bounce"
- # to the scrip. We might want to notify nobody. Or just
- # the RT Owner. Or maybe all Privileged watchers.
my ( $Sender, $junk ) = ParseSenderAddressFromHead($head);
- $head->add( 'RT-Squelch-Replies-To', $Sender );
- $head->add( 'RT-DetectedAutoGenerated', 'true' );
+ $head->add( 'RT-Squelch-Replies-To', $Sender );
}
- return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
-}
-=head2 IsCorrectAction
+ # }}}
+
+ my $Ticket = RT::Ticket->new($CurrentUser);
-Returns a list of valid actions we've found for this message
+ # {{{ If we don't have a ticket Id, we're creating a new ticket
+ if ( !$args{'ticket'} ) {
-=cut
+ # {{{ Create a new ticket
+
+ my @Cc;
+ my @Requestors = ( $CurrentUser->id );
-sub IsCorrectAction {
- my $action = shift;
- my @actions = grep $_, split /-/, $action;
- return ( 0, '(no value)' ) unless @actions;
- foreach ( @actions ) {
- return ( 0, $_ ) unless /^(?:comment|correspond|take|resolve)$/;
+ if ($RT::ParseNewMessageForTicketCcs) {
+ @Cc = ParseCcAddressesFromHead( Head => $head,
+ CurrentUser => $CurrentUser,
+ QueueObj => $SystemQueueObj );
+ }
+
+ my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
+ Queue => $SystemQueueObj->Id,
+ Subject => $Subject,
+ Requestor => \@Requestors,
+ Cc => \@Cc,
+ MIMEObj => $Message );
+ if ( $id == 0 ) {
+ MailError( To => $ErrorsTo,
+ Subject => "Ticket creation failed",
+ Explanation => $ErrStr,
+ MIMEObj => $Message );
+ $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr ");
+ return ( 0, "Ticket creation failed", $Ticket );
+ }
+
+ # }}}
+ }
+
+ # }}}
+
+ # If the action is comment, add a comment.
+ elsif ( $args{'action'} =~ /^(comment|correspond)$/i ) {
+ $Ticket->Load($args{'ticket'});
+ unless ( $Ticket->Id ) {
+ my $message = "Could not find a ticket with id ".$args{'ticket'};
+ MailError( To => $ErrorsTo,
+ Subject => "Message not recorded",
+ Explanation => $message,
+ MIMEObj => $Message );
+
+ return ( 0, $message);
+ }
+
+ my ( $status, $msg );
+ if ( $args{'action'} =~ /^correspond$/ ) {
+ ( $status, $msg ) = $Ticket->Correspond( MIMEObj => $Message );
+ }
+ else {
+ ( $status, $msg ) = $Ticket->Comment( MIMEObj => $Message );
+ }
+ unless ($status) {
+
+ #Warn the sender that we couldn't actually submit the comment.
+ MailError( To => $ErrorsTo,
+ Subject => "Message not recorded",
+ Explanation => $msg,
+ MIMEObj => $Message );
+ return ( 0, "Message not recorded", $Ticket );
+ }
}
- return ( 1, @actions );
+
+ else {
+
+ #Return mail to the sender with an error
+ MailError( To => $ErrorsTo,
+ Subject => "RT Configuration error",
+ Explanation => "'"
+ . $args{'action'}
+ . "' not a recognized action."
+ . " Your RT administrator has misconfigured "
+ . "the mail aliases which invoke RT",
+ MIMEObj => $Message );
+ $RT::Logger->crit( $args{'action'} . " type unknown for $MessageId" );
+ return ( 0, "Configuration error: " . $args{'action'} . " not a recognized action", $Ticket );
+
+ }
+
+
+return ( 1, "Success", $Ticket );
}
eval "require RT::Interface::Email_Vendor";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Email_Vendor.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email_Vendor.pm});
eval "require RT::Interface::Email_Local";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Email_Local.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email_Local.pm});
1;
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
index b4279fb4b..5097f54a4 100644
--- a/rt/lib/RT/Interface/Web.pm
+++ b/rt/lib/RT/Interface/Web.pm
@@ -1,734 +1,148 @@
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
# 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 }}}
-
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
## Portions Copyright 2000 Tobias Brox <tobix@fsck.com>
## This is a library of static subs to be used by the Mason web
## interface to RT
+
=head1 NAME
RT::Interface::Web
+=begin testing
-=cut
-
-use strict;
-use warnings;
-
-package RT::Interface::Web;
-
-use RT::SavedSearches;
-use URI qw();
-use RT::Interface::Web::Session;
-use Digest::MD5 ();
-use Encode qw();
-
-# {{{ EscapeUTF8
-
-=head2 EscapeUTF8 SCALARREF
+use_ok(RT::Interface::Web);
-does a css-busting but minimalist escaping of whatever html you're passing in.
+=end testing
=cut
-sub EscapeUTF8 {
- my $ref = shift;
- return unless defined $$ref;
-
- $$ref =~ s/&/&#38;/g;
- $$ref =~ s/</&lt;/g;
- $$ref =~ s/>/&gt;/g;
- $$ref =~ s/\(/&#40;/g;
- $$ref =~ s/\)/&#41;/g;
- $$ref =~ s/"/&#34;/g;
- $$ref =~ s/'/&#39;/g;
-}
-
-# }}}
-
-# {{{ EscapeURI
-
-=head2 EscapeURI SCALARREF
-Escapes URI component according to RFC2396
-
-=cut
-
-sub EscapeURI {
- my $ref = shift;
- return unless defined $$ref;
-
- use bytes;
- $$ref =~ s/([^a-zA-Z0-9_.!~*'()-])/uc sprintf("%%%02X", ord($1))/eg;
-}
-
-# }}}
+package RT::Interface::Web;
+use strict;
-# {{{ WebCanonicalizeInfo
-=head2 WebCanonicalizeInfo();
-Different web servers set different environmental varibles. This
-function must return something suitable for REMOTE_USER. By default,
-just downcase $ENV{'REMOTE_USER'}
-=cut
-sub WebCanonicalizeInfo {
- return $ENV{'REMOTE_USER'} ? lc $ENV{'REMOTE_USER'} : $ENV{'REMOTE_USER'};
-}
+# {{{ sub NewApacheHandler
-# }}}
+=head2 NewApacheHandler
-# {{{ WebExternalAutoInfo
-
-=head2 WebExternalAutoInfo($user);
-
-Returns a hash of user attributes, used when WebExternalAuto is set.
+ Takes extra options to pass to HTML::Mason::ApacheHandler->new
+ Returns a new Mason::ApacheHandler object
=cut
-sub WebExternalAutoInfo {
- my $user = shift;
-
- my %user_info;
-
- # default to making Privileged users, even if they specify
- # some other default Attributes
- if ( !$RT::AutoCreate
- || ( ref($RT::AutoCreate) && not exists $RT::AutoCreate->{Privileged} ) )
- {
- $user_info{'Privileged'} = 1;
- }
-
- if ( $^O !~ /^(?:riscos|MacOS|MSWin32|dos|os2)$/ ) {
-
- # Populate fields with information from Unix /etc/passwd
-
- my ( $comments, $realname ) = ( getpwnam($user) )[ 5, 6 ];
- $user_info{'Comments'} = $comments if defined $comments;
- $user_info{'RealName'} = $realname if defined $realname;
- } elsif ( $^O eq 'MSWin32' and eval 'use Net::AdminMisc; 1' ) {
-
- # Populate fields with information from NT domain controller
- }
+sub NewApacheHandler {
+ require HTML::Mason::ApacheHandler;
+ my $ah = new HTML::Mason::ApacheHandler(
+
+ comp_root => [
+ [ local => $RT::MasonLocalComponentRoot ],
+ [ standard => $RT::MasonComponentRoot ]
+ ],
+ args_method => "CGI",
+ default_escape_flags => 'h',
+ allow_globals => [qw(%session)],
+ data_dir => "$RT::MasonDataDir",
+ @_
+ );
- # and return the wad of stuff
- return {%user_info};
+ $ah->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
+
+ return ($ah);
}
# }}}
-sub HandleRequest {
- my $ARGS = shift;
-
- $HTML::Mason::Commands::r->content_type("text/html; charset=utf-8");
-
- $HTML::Mason::Commands::m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];
-
- # Roll back any dangling transactions from a previous failed connection
- $RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth;
-
- MaybeEnableSQLStatementLog();
-
- # avoid reentrancy, as suggested by masonbook
- local *HTML::Mason::Commands::session unless $HTML::Mason::Commands::m->is_subrequest;
-
- $HTML::Mason::Commands::m->autoflush( $HTML::Mason::Commands::m->request_comp->attr('AutoFlush') )
- if ( $HTML::Mason::Commands::m->request_comp->attr_exists('AutoFlush') );
-
- DecodeARGS($ARGS);
- PreprocessTimeUpdates($ARGS);
-
- MaybeShowInstallModePage();
-
- $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
- SendSessionCookie();
- $HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new() unless _UserLoggedIn();
-
- MaybeShowNoAuthPage($ARGS);
-
- AttemptExternalAuth($ARGS) if RT->Config->Get('WebExternalAuthContinuous') or not _UserLoggedIn();
-
- _ForceLogout() unless _UserLoggedIn();
-
- # Process per-page authentication callbacks
- $HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Auth', CallbackPage => '/autohandler' );
-
- unless ( _UserLoggedIn() ) {
- _ForceLogout();
-
- # If the user is logging in, let's authenticate
- if ( defined $ARGS->{user} && defined $ARGS->{pass} ) {
- AttemptPasswordAuthentication($ARGS);
- } else {
- # if no credentials then show him login page
- $HTML::Mason::Commands::m->comp( '/Elements/Login', %$ARGS );
- $HTML::Mason::Commands::m->abort;
- }
- }
-
- # now it applies not only to home page, but any dashboard that can be used as a workspace
- $HTML::Mason::Commands::session{'home_refresh_interval'} = $ARGS->{'HomeRefreshInterval'}
- if ( $ARGS->{'HomeRefreshInterval'} );
-
- # Process per-page global callbacks
- $HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Default', CallbackPage => '/autohandler' );
-
- ShowRequestedPage($ARGS);
- LogRecordedSQLStatements();
-}
-
-sub _ForceLogout {
-
- delete $HTML::Mason::Commands::session{'CurrentUser'};
-}
-
-sub _UserLoggedIn {
- if ( $HTML::Mason::Commands::session{CurrentUser} && $HTML::Mason::Commands::session{'CurrentUser'}->id ) {
- return 1;
- } else {
- return undef;
- }
-
-}
-
-=head2 MaybeShowInstallModePage
-
-This function, called exclusively by RT's autohandler, dispatches
-a request to RT's Installation workflow, only if Install Mode is enabled in the configuration file.
-
-If it serves a page, it stops mason processing. Otherwise, mason just keeps running through the autohandler
-
-=cut
-
-sub MaybeShowInstallModePage {
- return unless RT->InstallMode;
-
- my $m = $HTML::Mason::Commands::m;
- if ( $m->base_comp->path =~ RT->Config->Get('WebNoAuthRegex') ) {
- $m->call_next();
- } elsif ( $m->request_comp->path !~ '^(/+)Install/' ) {
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "Install/index.html" );
- } else {
- $m->call_next();
- }
- $m->abort();
-}
-
-=head2 MaybeShowNoAuthPage \%ARGS
-
-This function, called exclusively by RT's autohandler, dispatches
-a request to the page a user requested (but only if it matches the "noauth" regex.
-
-If it serves a page, it stops mason processing. Otherwise, mason just keeps running through the autohandler
-
-=cut
-
-sub MaybeShowNoAuthPage {
- my $ARGS = shift;
-
- my $m = $HTML::Mason::Commands::m;
-
- return unless $m->base_comp->path =~ RT->Config->Get('WebNoAuthRegex');
-
- # If it's a noauth file, don't ask for auth.
- SendSessionCookie();
- $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
- $m->abort;
-}
-
-=head2 ShowRequestedPage \%ARGS
-
-This function, called exclusively by RT's autohandler, dispatches
-a request to the page a user requested (making sure that unpriviled users
-can only see self-service pages.
-
-=cut
-
-sub ShowRequestedPage {
- my $ARGS = shift;
-
- my $m = $HTML::Mason::Commands::m;
-
- SendSessionCookie();
-
- # If the user isn't privileged, they can only see SelfService
- unless ( $HTML::Mason::Commands::session{'CurrentUser'}->Privileged ) {
-
- # if the user is trying to access a ticket, redirect them
- if ( $m->request_comp->path =~ '^(/+)Ticket/Display.html' && $ARGS->{'id'} ) {
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "SelfService/Display.html?id=" . $ARGS->{'id'} );
- }
-
- # otherwise, drop the user at the SelfService default page
- elsif ( $m->base_comp->path !~ RT->Config->Get('SelfServiceRegex') ) {
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "SelfService/" );
- }
-
- # if user is in SelfService dir let him do anything
- else {
- $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
- }
- } else {
- $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
- }
-
-}
-
-sub AttemptExternalAuth {
- my $ARGS = shift;
-
- return unless ( RT->Config->Get('WebExternalAuth') );
-
- my $user = $ARGS->{user};
- my $m = $HTML::Mason::Commands::m;
-
- # If RT is configured for external auth, let's go through and get REMOTE_USER
-
- # do we actually have a REMOTE_USER equivlent?
- if ( RT::Interface::Web::WebCanonicalizeInfo() ) {
- my $orig_user = $user;
-
- $user = RT::Interface::Web::WebCanonicalizeInfo();
- my $load_method = RT->Config->Get('WebExternalGecos') ? 'LoadByGecos' : 'Load';
-
- if ( $^O eq 'MSWin32' and RT->Config->Get('WebExternalGecos') ) {
- my $NodeName = Win32::NodeName();
- $user =~ s/^\Q$NodeName\E\\//i;
- }
-
- InstantiateNewSession() unless _UserLoggedIn;
- $HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new();
- $HTML::Mason::Commands::session{'CurrentUser'}->$load_method($user);
-
- if ( RT->Config->Get('WebExternalAuto') and not _UserLoggedIn() ) {
-
- # Create users on-the-fly
- my $UserObj = RT::User->new($RT::SystemUser);
- my ( $val, $msg ) = $UserObj->Create(
- %{ ref RT->Config->Get('AutoCreate') ? RT->Config->Get('AutoCreate') : {} },
- Name => $user,
- Gecos => $user,
- );
-
- if ($val) {
-
- # now get user specific information, to better create our user.
- my $new_user_info = RT::Interface::Web::WebExternalAutoInfo($user);
-
- # set the attributes that have been defined.
- foreach my $attribute ( $UserObj->WritableAttributes ) {
- $m->callback(
- Attribute => $attribute,
- User => $user,
- UserInfo => $new_user_info,
- CallbackName => 'NewUser',
- CallbackPage => '/autohandler'
- );
- my $method = "Set$attribute";
- $UserObj->$method( $new_user_info->{$attribute} ) if defined $new_user_info->{$attribute};
- }
- $HTML::Mason::Commands::session{'CurrentUser'}->Load($user);
- } else {
-
- # we failed to successfully create the user. abort abort abort.
- delete $HTML::Mason::Commands::session{'CurrentUser'};
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc( 'Cannot create user: [_1]', $msg ) )
- if RT->Config->Get('WebFallbackToInternalAuth');;
- $m->abort();
- }
- }
-
- if ( _UserLoggedIn() ) {
- $m->callback( %$ARGS, CallbackName => 'ExternalAuthSuccessfulLogin', CallbackPage => '/autohandler' );
- } else {
- delete $HTML::Mason::Commands::session{'CurrentUser'};
- $user = $orig_user;
-
- if ( RT->Config->Get('WebExternalOnly') ) {
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc('You are not an authorized user') );
- $m->abort();
- }
- }
- } elsif ( RT->Config->Get('WebFallbackToInternalAuth') ) {
- unless ( defined $HTML::Mason::Commands::session{'CurrentUser'} ) {
- # XXX unreachable due to prior defaulting in HandleRequest (check c34d108)
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc('You are not an authorized user') );
- $m->abort();
- }
- } else {
-
- # WebExternalAuth is set, but we don't have a REMOTE_USER. abort
- # XXX: we must return AUTH_REQUIRED status or we fallback to
- # internal auth here too.
- delete $HTML::Mason::Commands::session{'CurrentUser'}
- if defined $HTML::Mason::Commands::session{'CurrentUser'};
- }
-}
+# {{{ sub NewCGIHandler
-sub AttemptPasswordAuthentication {
- my $ARGS = shift;
- my $user_obj = RT::CurrentUser->new();
- $user_obj->Load( $ARGS->{user} );
+=head2 NewCGIHandler
- my $m = $HTML::Mason::Commands::m;
-
- unless ( $user_obj->id && $user_obj->IsPassword( $ARGS->{pass} ) ) {
- $RT::Logger->error("FAILED LOGIN for @{[$ARGS->{user}]} from $ENV{'REMOTE_ADDR'}");
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc('Your username or password is incorrect'), );
- $m->callback( %$ARGS, CallbackName => 'FailedLogin', CallbackPage => '/autohandler' );
- $m->abort;
- }
-
- $RT::Logger->info("Successful login for @{[$ARGS->{user}]} from $ENV{'REMOTE_ADDR'}");
- InstantiateNewSession();
- $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
- $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler' );
-}
-
-=head2 LoadSessionFromCookie
-
-Load or setup a session cookie for the current user.
+ Returns a new Mason::CGIHandler object
=cut
-sub _SessionCookieName {
- my $cookiename = "RT_SID_" . RT->Config->Get('rtname');
- $cookiename .= "." . $ENV{'SERVER_PORT'} if $ENV{'SERVER_PORT'};
- return $cookiename;
-}
-
-sub LoadSessionFromCookie {
-
- my %cookies = CGI::Cookie->fetch;
- my $cookiename = _SessionCookieName();
- my $SessionCookie = ( $cookies{$cookiename} ? $cookies{$cookiename}->value : undef );
- tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', $SessionCookie;
- unless ( $SessionCookie && $HTML::Mason::Commands::session{'_session_id'} eq $SessionCookie ) {
- undef $cookies{$cookiename};
- }
- if ( int RT->Config->Get('AutoLogoff') ) {
- my $now = int( time / 60 );
- my $last_update = $HTML::Mason::Commands::session{'_session_last_update'} || 0;
-
- if ( $last_update && ( $now - $last_update - RT->Config->Get('AutoLogoff') ) > 0 ) {
- InstantiateNewSession();
- }
-
- # save session on each request when AutoLogoff is turned on
- $HTML::Mason::Commands::session{'_session_last_update'} = $now if $now != $last_update;
- }
-}
-
-sub InstantiateNewSession {
- tied(%HTML::Mason::Commands::session)->delete if tied(%HTML::Mason::Commands::session);
- tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', undef;
-}
-
-sub SendSessionCookie {
- my $cookie = CGI::Cookie->new(
- -name => _SessionCookieName(),
- -value => $HTML::Mason::Commands::session{_session_id},
- -path => RT->Config->Get('WebPath'),
- -secure => ( RT->Config->Get('WebSecureCookies') ? 1 : 0 )
+sub NewCGIHandler {
+ my %args = (
+ @_
);
- $HTML::Mason::Commands::r->err_headers_out->{'Set-Cookie'} = $cookie->as_string;
-}
-
-=head2 Redirect URL
-
-This routine ells the current user's browser to redirect to URL.
-Additionally, it unties the user's currently active session, helping to avoid
-A bug in Apache::Session 1.81 and earlier which clobbers sessions if we try to use
-a cached DBI statement handle twice at the same time.
-
-=cut
-
-sub Redirect {
- my $redir_to = shift;
- untie $HTML::Mason::Commands::session;
- my $uri = URI->new($redir_to);
- my $server_uri = URI->new( RT->Config->Get('WebURL') );
-
- # If the user is coming in via a non-canonical
- # hostname, don't redirect them to the canonical host,
- # it will just upset them (and invalidate their credentials)
- # don't do this if $RT::CanoniaclRedirectURLs is true
- if ( !RT->Config->Get('CanonicalizeRedirectURLs')
- && $uri->host eq $server_uri->host
- && $uri->port eq $server_uri->port )
- {
- if ( defined $ENV{HTTPS} and $ENV{'HTTPS'} eq 'on' ) {
- $uri->scheme('https');
- } else {
- $uri->scheme('http');
- }
-
- # [rt3.fsck.com #12716] Apache recommends use of $SERVER_HOST
- $uri->host( $ENV{'SERVER_HOST'} || $ENV{'HTTP_HOST'} );
- $uri->port( $ENV{'SERVER_PORT'} );
- }
-
- # not sure why, but on some systems without this call mason doesn't
- # set status to 302, but 200 instead and people see blank pages
- $HTML::Mason::Commands::r->status(302);
-
- # Perlbal expects a status message, but Mason's default redirect status
- # doesn't provide one. See also rt.cpan.org #36689.
- $HTML::Mason::Commands::m->redirect( $uri->canonical, "302 Found" );
-
- $HTML::Mason::Commands::m->abort;
-}
-
-=head2 StaticFileHeaders
-
-Send the browser a few headers to try to get it to (somewhat agressively)
-cache RT's static Javascript and CSS files.
-
-This routine could really use _accurate_ heuristics. (XXX TODO)
-
-=cut
+ my $handler = HTML::Mason::CGIHandler->new(
+ comp_root => [
+ [ local => $RT::MasonLocalComponentRoot ],
+ [ standard => $RT::MasonComponentRoot ]
+ ],
+ data_dir => "$RT::MasonDataDir",
+ default_escape_flags => 'h',
+ allow_globals => [qw(%session)]
+ );
+
-sub StaticFileHeaders {
- my $date = RT::Date->new($RT::SystemUser);
+ $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
- # make cache public
- $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = 'max-age=259200, public';
- # Expire things in a month.
- $date->Set( Value => time + 30 * 24 * 60 * 60 );
- $HTML::Mason::Commands::r->headers_out->{'Expires'} = $date->RFC2616;
+ return ($handler);
- # if we set 'Last-Modified' then browser request a comp using 'If-Modified-Since'
- # request, but we don't handle it and generate full reply again
- # Last modified at server start time
- # $date->Set( Value => $^T );
- # $HTML::Mason::Commands::r->headers_out->{'Last-Modified'} = $date->RFC2616;
}
+# }}}
-=head2 SendStaticFile
-Takes a File => path and a Type => Content-type
+# {{{ EscapeUTF8
-If Type isn't provided and File is an image, it will
-figure out a sane Content-type, otherwise it will
-send application/octet-stream
+=head2 EscapeUTF8 SCALARREF
-Will set caching headers using StaticFileHeaders
+does a css-busting but minimalist escaping of whatever html you're passing in.
=cut
-sub SendStaticFile {
- my $self = shift;
- my %args = @_;
- my $file = $args{File};
- my $type = $args{Type};
-
- $self->StaticFileHeaders();
-
- unless ($type) {
- if ( $file =~ /\.(gif|png|jpe?g)$/i ) {
- $type = "image/$1";
- $type =~ s/jpg/jpeg/gi;
- }
- $type ||= "application/octet-stream";
- }
- $HTML::Mason::Commands::r->content_type($type);
- open my $fh, "<$file" or die "couldn't open file: $!";
- binmode($fh);
- {
- local $/ = \16384;
- $HTML::Mason::Commands::m->out($_) while (<$fh>);
- $HTML::Mason::Commands::m->flush_buffer;
- }
- close $fh;
-}
-
-sub StripContent {
- my %args = @_;
- my $content = $args{Content};
- return '' unless $content;
-
- # Make the content have no 'weird' newlines in it
- $content =~ s/\r+\n/\n/g;
-
- my $return_content = $content;
-
- my $html = $args{ContentType} && $args{ContentType} eq "text/html";
- my $sigonly = $args{StripSignature};
-
- # massage content to easily detect if there's any real content
- $content =~ s/\s+//g; # yes! remove all the spaces
- if ( $html ) {
- # remove html version of spaces and newlines
- $content =~ s!&nbsp;!!g;
- $content =~ s!<br/?>!!g;
- }
-
- # Filter empty content when type is text/html
- return '' if $html && $content !~ /\S/;
-
- # If we aren't supposed to strip the sig, just bail now.
- return $return_content unless $sigonly;
-
- # Find the signature
- my $sig = $args{'CurrentUser'}->UserObj->Signature || '';
- $sig =~ s/\s+//g;
-
- # Check for plaintext sig
- return '' if not $html and $content =~ /^(--)?\Q$sig\E$/;
-
- # Check for html-formatted sig
- RT::Interface::Web::EscapeUTF8( \$sig );
- return ''
- if $html
- and $content =~ m{^(?:<p>)?(--)?\Q$sig\E(?:</p>)?$}s;
-
- # Pass it through
- return $return_content;
-}
-
-sub DecodeARGS {
- my $ARGS = shift;
-
- %{$ARGS} = map {
-
- # if they've passed multiple values, they'll be an array. if they've
- # passed just one, a scalar whatever they are, mark them as utf8
- my $type = ref($_);
- ( !$type )
- ? Encode::is_utf8($_)
- ? $_
- : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ )
- : ( $type eq 'ARRAY' )
- ? [ map { ( ref($_) or Encode::is_utf8($_) ) ? $_ : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ ) }
- @$_ ]
- : ( $type eq 'HASH' )
- ? { map { ( ref($_) or Encode::is_utf8($_) ) ? $_ : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ ) }
- %$_ }
- : $_
- } %$ARGS;
-}
-
-sub PreprocessTimeUpdates {
- my $ARGS = shift;
-
- # Later in the code we use
- # $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS );
- # instead of $m->call_next to avoid problems with UTF8 keys in arguments.
- # The call_next method pass through original arguments and if you have
- # an argument with unicode key then in a next component you'll get two
- # records in the args hash: one with key without UTF8 flag and another
- # with the flag, which may result into errors. "{ base_comp => $m->request_comp }"
- # is copied from mason's source to get the same results as we get from
- # call_next method, this feature is not documented, so we just leave it
- # here to avoid possible side effects.
-
- # This code canonicalizes time inputs in hours into minutes
- foreach my $field ( keys %$ARGS ) {
- next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS->{$1};
- my $local = $1;
- $ARGS->{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b}
- {($1 || 0) + $3 ? $2 / $3 : 0}xe;
- if ( $ARGS->{$field} && $ARGS->{$field} =~ /hours/i ) {
- $ARGS->{$local} *= 60;
- }
- delete $ARGS->{$field};
- }
-
-}
-
-sub MaybeEnableSQLStatementLog {
-
- my $log_sql_statements = RT->Config->Get('StatementLog');
-
- if ($log_sql_statements) {
- $RT::Handle->ClearSQLStatementLog;
- $RT::Handle->LogSQLStatements(1);
- }
+sub EscapeUTF8 {
+ my $ref = shift;
+ my $val = $$ref;
+ use bytes;
+ $val =~ s/&/&#38;/g;
+ $val =~ s/</&lt;/g;
+ $val =~ s/>/&gt;/g;
+ $val =~ s/\(/&#40;/g;
+ $val =~ s/\)/&#41;/g;
+ $val =~ s/"/&#34;/g;
+ $val =~ s/'/&#39;/g;
+ $$ref = $val;
+ Encode::_utf8_on($$ref);
}
-sub LogRecordedSQLStatements {
- my $log_sql_statements = RT->Config->Get('StatementLog');
-
- return unless ($log_sql_statements);
-
- my @log = $RT::Handle->SQLStatementLog;
- $RT::Handle->ClearSQLStatementLog;
- for my $stmt (@log) {
- my ( $time, $sql, $bind, $duration ) = @{$stmt};
- my @bind;
- if ( ref $bind ) {
- @bind = @{$bind};
- } else {
-
- # Older DBIx-SB
- $duration = $bind;
- }
- $RT::Logger->log(
- level => $log_sql_statements,
- message => "SQL("
- . sprintf( "%.6f", $duration )
- . "s): $sql;"
- . ( @bind ? " [ bound values: @{[map{qq|'$_'|} @bind]} ]" : "" )
- );
- }
+# }}}
-}
package HTML::Mason::Commands;
-
+use strict;
use vars qw/$r $m %session/;
+
# {{{ loc
=head2 loc ARRAY
@@ -742,26 +156,19 @@ through
sub loc {
- if ( $session{'CurrentUser'}
- && UNIVERSAL::can( $session{'CurrentUser'}, 'loc' ) )
- {
- return ( $session{'CurrentUser'}->loc(@_) );
- } elsif (
- my $u = eval {
- RT::CurrentUser->new();
- }
- )
- {
- return ( $u->loc(@_) );
- } else {
-
- # pathetic case -- SystemUser is gone.
- return $_[0];
+ if ($session{'CurrentUser'} &&
+ UNIVERSAL::can($session{'CurrentUser'}, 'loc')){
+ return($session{'CurrentUser'}->loc(@_));
+ }
+ else {
+ my $u = RT::CurrentUser->new($RT::SystemUser);
+ return ($u->loc(@_));
}
}
# }}}
+
# {{{ loc_fuzzy
=head2 loc_fuzzy STRING
@@ -775,41 +182,40 @@ inside the lexicon file.
=cut
sub loc_fuzzy {
- my $msg = shift;
-
- if ( $session{'CurrentUser'}
- && UNIVERSAL::can( $session{'CurrentUser'}, 'loc' ) )
- {
- return ( $session{'CurrentUser'}->loc_fuzzy($msg) );
- } else {
- my $u = RT::CurrentUser->new( $RT::SystemUser->Id );
- return ( $u->loc_fuzzy($msg) );
+ my $msg = shift;
+
+ if ($session{'CurrentUser'} &&
+ UNIVERSAL::can($session{'CurrentUser'}, 'loc')){
+ return($session{'CurrentUser'}->loc_fuzzy($msg));
+ }
+ else {
+ my $u = RT::CurrentUser->new($RT::SystemUser);
+ return ($u->loc_fuzzy($msg));
}
}
# }}}
+
# {{{ sub Abort
# Error - calls Error and aborts
sub Abort {
- my $why = shift;
- my %args = @_;
- if ( $session{'ErrorDocument'}
- && $session{'ErrorDocumentType'} )
- {
- $r->content_type( $session{'ErrorDocumentType'} );
- $m->comp( $session{'ErrorDocument'}, Why => $why, %args );
+ if ($session{'ErrorDocument'} &&
+ $session{'ErrorDocumentType'}) {
+ $r->content_type($session{'ErrorDocumentType'});
+ $m->comp($session{'ErrorDocument'} , Why => shift);
$m->abort;
- } else {
- $m->comp( "/Elements/Error", Why => $why, %args );
+ }
+ else {
+ $m->comp("/Elements/Error" , Why => shift);
$m->abort;
}
}
# }}}
-# {{{ sub CreateTicket
+# {{{ sub CreateTicket
=head2 CreateTicket ARGS
@@ -833,160 +239,80 @@ sub CreateTicket {
Abort('You have no permission to create tickets in that queue.');
}
- my $due;
- if ( defined $ARGS{'Due'} and $ARGS{'Due'} =~ /\S/ ) {
- $due = new RT::Date( $session{'CurrentUser'} );
- $due->Set( Format => 'unknown', Value => $ARGS{'Due'} );
- }
- my $starts;
- if ( defined $ARGS{'Starts'} and $ARGS{'Starts'} =~ /\S/ ) {
- $starts = new RT::Date( $session{'CurrentUser'} );
- $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} );
- }
+ my $due = new RT::Date( $session{'CurrentUser'} );
+ $due->Set( Format => 'unknown', Value => $ARGS{'Due'} );
+ my $starts = new RT::Date( $session{'CurrentUser'} );
+ $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} );
- my $sigless = RT::Interface::Web::StripContent(
- Content => $ARGS{Content},
- ContentType => $ARGS{ContentType},
- StripSignature => 1,
- CurrentUser => $session{'CurrentUser'},
- );
+ my @Requestors = split ( /\s*,\s*/, $ARGS{'Requestors'} );
+ my @Cc = split ( /\s*,\s*/, $ARGS{'Cc'} );
+ my @AdminCc = split ( /\s*,\s*/, $ARGS{'AdminCc'} );
my $MIMEObj = MakeMIMEEntity(
- Subject => $ARGS{'Subject'},
- From => $ARGS{'From'},
- Cc => $ARGS{'Cc'},
- Body => $sigless,
- Type => $ARGS{'ContentType'},
+ Subject => $ARGS{'Subject'},
+ From => $ARGS{'From'},
+ Cc => $ARGS{'Cc'},
+ Body => $ARGS{'Content'},
);
- if ( $ARGS{'Attachments'} ) {
- my $rv = $MIMEObj->make_multipart;
- $RT::Logger->error("Couldn't make multipart message")
- if !$rv || $rv !~ /^(?:DONE|ALREADY)$/;
-
- foreach ( values %{ $ARGS{'Attachments'} } ) {
- unless ($_) {
- $RT::Logger->error("Couldn't add empty attachemnt");
- next;
- }
- $MIMEObj->add_part($_);
- }
- }
-
- foreach my $argument (qw(Encrypt Sign)) {
- $MIMEObj->head->add( "X-RT-$argument" => $ARGS{$argument} ) if defined $ARGS{$argument};
+ if ($ARGS{'Attachments'}) {
+ $MIMEObj->make_multipart;
+ $MIMEObj->add_part($_) foreach values %{$ARGS{'Attachments'}};
}
my %create_args = (
- Type => $ARGS{'Type'} || 'ticket',
- Queue => $ARGS{'Queue'},
- Owner => $ARGS{'Owner'},
-
- # note: name change
- Requestor => $ARGS{'Requestors'},
- Cc => $ARGS{'Cc'},
- AdminCc => $ARGS{'AdminCc'},
+ Queue => $ARGS{'Queue'},
+ Owner => $ARGS{'Owner'},
InitialPriority => $ARGS{'InitialPriority'},
FinalPriority => $ARGS{'FinalPriority'},
TimeLeft => $ARGS{'TimeLeft'},
- TimeEstimated => $ARGS{'TimeEstimated'},
+ TimeEstimated => $ARGS{'TimeEstimated'},
TimeWorked => $ARGS{'TimeWorked'},
+ Requestor => \@Requestors,
+ Cc => \@Cc,
+ AdminCc => \@AdminCc,
Subject => $ARGS{'Subject'},
Status => $ARGS{'Status'},
- Due => $due ? $due->ISO : undef,
- Starts => $starts ? $starts->ISO : undef,
+ Due => $due->ISO,
+ Starts => $starts->ISO,
MIMEObj => $MIMEObj
);
-
- my @temp_squelch;
- foreach my $type (qw(Requestor Cc AdminCc)) {
- push @temp_squelch, map $_->address, Email::Address->parse( $create_args{$type} )
- if grep $_ eq $type || $_ eq ( $type . 's' ), @{ $ARGS{'SkipNotification'} || [] };
-
- }
-
- if (@temp_squelch) {
- require RT::Action::SendEmail;
- RT::Action::SendEmail->SquelchMailTo( RT::Action::SendEmail->SquelchMailTo, @temp_squelch );
+ foreach my $arg (%ARGS) {
+ if ($arg =~ /^CustomField-(\d+)(.*?)$/) {
+ next if ($arg =~ /-Magic$/);
+ $create_args{"CustomField-".$1} = $ARGS{"$arg"};
+ }
}
-
- if ( $ARGS{'AttachTickets'} ) {
- require RT::Action::SendEmail;
- RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
- ref $ARGS{'AttachTickets'}
- ? @{ $ARGS{'AttachTickets'} }
- : ( $ARGS{'AttachTickets'} ) );
+ my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);
+ unless ( $id && $Trans ) {
+ Abort($ErrMsg);
}
+ my @linktypes = qw( DependsOn MemberOf RefersTo );
- foreach my $arg ( keys %ARGS ) {
- next if $arg =~ /-(?:Magic|Category)$/;
-
- if ( $arg =~ /^Object-RT::Transaction--CustomField-/ ) {
- $create_args{$arg} = $ARGS{$arg};
+ foreach my $linktype (@linktypes) {
+ foreach my $luri ( split ( / /, $ARGS{"new-$linktype"} ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ my ( $val, $msg ) = $Ticket->AddLink(
+ Target => $luri,
+ Type => $linktype
+ );
+ push ( @Actions, $msg ) unless ($val);
}
- # Object-RT::Ticket--CustomField-3-Values
- elsif ( $arg =~ /^Object-RT::Ticket--CustomField-(\d+)/ ) {
- my $cfid = $1;
-
- my $cf = RT::CustomField->new( $session{'CurrentUser'} );
- $cf->Load($cfid);
- unless ( $cf->id ) {
- $RT::Logger->error( "Couldn't load custom field #" . $cfid );
- next;
- }
-
- if ( $arg =~ /-Upload$/ ) {
- $create_args{"CustomField-$cfid"} = _UploadedFile($arg);
- next;
- }
-
- my $type = $cf->Type;
-
- my @values = ();
- if ( ref $ARGS{$arg} eq 'ARRAY' ) {
- @values = @{ $ARGS{$arg} };
- } elsif ( $type =~ /text/i ) {
- @values = ( $ARGS{$arg} );
- } else {
- no warnings 'uninitialized';
- @values = split /\r*\n/, $ARGS{$arg};
- }
- @values = grep length, map {
- s/\r+\n/\n/g;
- s/^\s+//;
- s/\s+$//;
- $_;
- }
- grep defined, @values;
+ foreach my $luri ( split ( / /, $ARGS{"$linktype-new"} ) ) {
+ my ( $val, $msg ) = $Ticket->AddLink(
+ Base => $luri,
+ Type => $linktype
+ );
- $create_args{"CustomField-$cfid"} = \@values;
+ push ( @Actions, $msg ) unless ($val);
}
}
- # turn new link lists into arrays, and pass in the proper arguments
- my %map = (
- 'new-DependsOn' => 'DependsOn',
- 'DependsOn-new' => 'DependedOnBy',
- 'new-MemberOf' => 'Parents',
- 'MemberOf-new' => 'Children',
- 'new-RefersTo' => 'RefersTo',
- 'RefersTo-new' => 'ReferredToBy',
- );
- foreach my $key ( keys %map ) {
- next unless $ARGS{$key};
- $create_args{ $map{$key} } = [ grep $_, split ' ', $ARGS{$key} ];
-
- }
-
- my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);
- unless ($id) {
- Abort($ErrMsg);
- }
-
- push( @Actions, split( "\n", $ErrMsg ) );
+ push ( @Actions, split("\n", $ErrMsg) );
unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
- Abort( "No permission to view newly created ticket #" . $Ticket->id . "." );
+ Abort( "No permission to view newly created ticket #"
+ . $Ticket->id . "." );
}
return ( $Ticket, @Actions );
@@ -1028,130 +354,62 @@ sub LoadTicket {
# {{{ sub ProcessUpdateMessage
-=head2 ProcessUpdateMessage
-
-Takes paramhash with fields ARGSRef, TicketObj and SkipSignatureOnly.
-
-Don't write message if it only contains current user's signature and
-SkipSignatureOnly argument is true. Function anyway adds attachments
-and updates time worked field even if skips message. The default value
-is true.
-
-=cut
-
sub ProcessUpdateMessage {
+ #TODO document what else this takes.
my %args = (
- ARGSRef => undef,
- TicketObj => undef,
- SkipSignatureOnly => 1,
+ ARGSRef => undef,
+ Actions => undef,
+ TicketObj => undef,
@_
);
- if ( $args{ARGSRef}->{'UpdateAttachments'}
- && !keys %{ $args{ARGSRef}->{'UpdateAttachments'} } )
- {
- delete $args{ARGSRef}->{'UpdateAttachments'};
- }
+ #Make the update content have no 'weird' newlines in it
+ if ( $args{ARGSRef}->{'UpdateContent'} ) {
- # Strip the signature
- $args{ARGSRef}->{UpdateContent} = RT::Interface::Web::StripContent(
- Content => $args{ARGSRef}->{UpdateContent},
- ContentType => $args{ARGSRef}->{UpdateContentType},
- StripSignature => $args{SkipSignatureOnly},
- CurrentUser => $args{'TicketObj'}->CurrentUser,
- );
-
- # If, after stripping the signature, we have no message, move the
- # UpdateTimeWorked into adjusted TimeWorked, so that a later
- # ProcessBasics can deal -- then bail out.
- if ( not $args{ARGSRef}->{'UpdateAttachments'}
- and not length $args{ARGSRef}->{'UpdateContent'} )
- {
- if ( $args{ARGSRef}->{'UpdateTimeWorked'} ) {
- $args{ARGSRef}->{TimeWorked} = $args{TicketObj}->TimeWorked + delete $args{ARGSRef}->{'UpdateTimeWorked'};
+ if (
+ $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject() )
+ {
+ $args{ARGSRef}->{'UpdateSubject'} = undef;
}
- return;
- }
-
- if ( $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject ) {
- $args{ARGSRef}->{'UpdateSubject'} = undef;
- }
-
- my $Message = MakeMIMEEntity(
- Subject => $args{ARGSRef}->{'UpdateSubject'},
- Body => $args{ARGSRef}->{'UpdateContent'},
- Type => $args{ARGSRef}->{'UpdateContentType'},
- );
- $Message->head->add( 'Message-ID' => RT::Interface::Email::GenMessageId( Ticket => $args{'TicketObj'}, ) );
- my $old_txn = RT::Transaction->new( $session{'CurrentUser'} );
- if ( $args{ARGSRef}->{'QuoteTransaction'} ) {
- $old_txn->Load( $args{ARGSRef}->{'QuoteTransaction'} );
- } else {
- $old_txn = $args{TicketObj}->Transactions->First();
- }
-
- if ( my $msg = $old_txn->Message->First ) {
- RT::Interface::Email::SetInReplyTo(
- Message => $Message,
- InReplyTo => $msg
+ my $Message = MakeMIMEEntity(
+ Subject => $args{ARGSRef}->{'UpdateSubject'},
+ Body => $args{ARGSRef}->{'UpdateContent'},
);
- }
-
- if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
- $Message->make_multipart;
- $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
- }
- if ( $args{ARGSRef}->{'AttachTickets'} ) {
- require RT::Action::SendEmail;
- RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
- ref $args{ARGSRef}->{'AttachTickets'}
- ? @{ $args{ARGSRef}->{'AttachTickets'} }
- : ( $args{ARGSRef}->{'AttachTickets'} ) );
- }
-
- my $bcc = $args{ARGSRef}->{'UpdateBcc'};
- my $cc = $args{ARGSRef}->{'UpdateCc'};
-
- my %message_args = (
- CcMessageTo => $cc,
- BccMessageTo => $bcc,
- Sign => $args{ARGSRef}->{'Sign'},
- Encrypt => $args{ARGSRef}->{'Encrypt'},
- MIMEObj => $Message,
- TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
- );
-
- unless ( $args{'ARGSRef'}->{'UpdateIgnoreAddressCheckboxes'} ) {
- foreach my $key ( keys %{ $args{ARGSRef} } ) {
- next unless $key =~ /^Update(Cc|Bcc)-(.*)$/;
-
- my $var = ucfirst($1) . 'MessageTo';
- my $value = $2;
- if ( $message_args{$var} ) {
- $message_args{$var} .= ", $value";
- } else {
- $message_args{$var} = $value;
- }
+ if ($args{ARGSRef}->{'UpdateAttachments'}) {
+ $Message->make_multipart;
+ $Message->add_part($_) foreach values %{$args{ARGSRef}->{'UpdateAttachments'}};
}
- }
- my @results;
- if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
- my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Comment(%message_args);
- push( @results, $Description );
- $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
- } elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
- my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Correspond(%message_args);
- push( @results, $Description );
- $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
- } else {
- push( @results,
- loc("Update type was neither correspondence nor comment.") . " " . loc("Update not recorded.") );
+ ## TODO: Implement public comments
+ if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
+ my ( $Transaction, $Description ) = $args{TicketObj}->Comment(
+ CcMessageTo => $args{ARGSRef}->{'UpdateCc'},
+ BccMessageTo => $args{ARGSRef}->{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
+ );
+ push ( @{ $args{Actions} }, $Description );
+ }
+ elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
+ my ( $Transaction, $Description ) = $args{TicketObj}->Correspond(
+ CcMessageTo => $args{ARGSRef}->{'UpdateCc'},
+ BccMessageTo => $args{ARGSRef}->{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
+ );
+ push ( @{ $args{Actions} }, $Description );
+ }
+ else {
+ push ( @{ $args{'Actions'} },
+ loc("Update type was neither correspondence nor comment.").
+ " ".
+ loc("Update not recorded.")
+ );
+ }
}
- return @results;
}
# }}}
@@ -1162,8 +420,6 @@ sub ProcessUpdateMessage {
Takes a paramhash Subject, Body and AttachmentFieldName.
-Also takes Form, Cc and Type as optional paramhash keys.
-
Returns a MIME::Entity.
=cut
@@ -1177,66 +433,291 @@ sub MakeMIMEEntity {
Cc => undef,
Body => undef,
AttachmentFieldName => undef,
- Type => undef,
- @_,
- );
- my $Message = MIME::Entity->build(
- Type => 'multipart/mixed',
- Subject => $args{'Subject'} || "",
- From => $args{'From'},
- Cc => $args{'Cc'},
+ map Encode::encode_utf8($_), @_,
);
- if ( defined $args{'Body'} && length $args{'Body'} ) {
-
- # Make the update content have no 'weird' newlines in it
- $args{'Body'} =~ s/\r\n/\n/gs;
+ #Make the update content have no 'weird' newlines in it
+ $args{'Body'} =~ s/\r\n/\n/gs;
+ my $Message;
+ {
# MIME::Head is not happy in utf-8 domain. This only happens
# when processing an incoming email (so far observed).
no utf8;
use bytes;
- $Message->attach(
- Type => $args{'Type'} || 'text/plain',
- Charset => 'UTF-8',
- Data => $args{'Body'},
+ $Message = MIME::Entity->build(
+ Subject => $args{'Subject'} || "",
+ From => $args{'From'},
+ Cc => $args{'Cc'},
+ Data => [ $args{'Body'} ]
+ );
+ }
+
+ my $cgi_object = $m->cgi_object;
+
+ if (my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) {
+
+
+
+ use File::Temp qw(tempfile tempdir);
+
+ #foreach my $filehandle (@filenames) {
+
+ my ( $fh, $temp_file ) = tempfile();
+
+ binmode $fh; #thank you, windows
+ my ($buffer);
+ while ( my $bytesread = read( $filehandle, $buffer, 4096 ) ) {
+ print $fh $buffer;
+ }
+
+ my $uploadinfo = $cgi_object->uploadInfo($filehandle);
+
+ # Prefer the cached name first over CGI.pm stringification.
+ my $filename = $RT::Mason::CGI::Filename;
+ $filename = "$filehandle" unless defined($filename);
+
+ $filename =~ s#^.*[\\/]##;
+
+ $Message->attach(
+ Path => $temp_file,
+ Filename => $filename,
+ Type => $uploadinfo->{'Content-Type'},
+ );
+ close($fh);
+
+ # }
+
+ }
+
+ $Message->make_singlepart();
+ RT::I18N::SetMIMEEntityToUTF8($Message); # convert text parts into utf-8
+
+ return ($Message);
+
+}
+
+# }}}
+
+# {{{ sub ProcessSearchQuery
+
+=head2 ProcessSearchQuery
+
+ Takes a form such as the one filled out in webrt/Search/Elements/PickRestriction and turns it into something that RT::Tickets can understand.
+
+TODO Doc exactly what comes in the paramhash
+
+
+=cut
+
+sub ProcessSearchQuery {
+ my %args = @_;
+
+ ## TODO: The only parameter here is %ARGS. Maybe it would be
+ ## cleaner to load this parameter as $ARGS, and use $ARGS->{...}
+ ## instead of $args{ARGS}->{...} ? :)
+
+ #Searches are sticky.
+ if ( defined $session{'tickets'} ) {
+
+ # Reset the old search
+ $session{'tickets'}->GotoFirstItem;
+ }
+ else {
+
+ # Init a new search
+ $session{'tickets'} = RT::Tickets->new( $session{'CurrentUser'} );
+ }
+
+ #Import a bookmarked search if we have one
+ if ( defined $args{ARGS}->{'Bookmark'} ) {
+ $session{'tickets'}->ThawLimits( $args{ARGS}->{'Bookmark'} );
+ }
+
+ # {{{ Goto next/prev page
+ if ( $args{ARGS}->{'GotoPage'} eq 'Next' ) {
+ $session{'tickets'}->NextPage;
+ }
+ elsif ( $args{ARGS}->{'GotoPage'} eq 'Prev' ) {
+ $session{'tickets'}->PrevPage;
+ }
+ elsif ( $args{ARGS}->{'GotoPage'} > 0 ) {
+ $session{'tickets'}->GotoPage( $args{ARGS}->{GotoPage} - 1 );
+ }
+
+ # }}}
+
+ # {{{ Deal with limiting the search
+
+ if ( $args{ARGS}->{'RefreshSearchInterval'} ) {
+ $session{'tickets_refresh_interval'} =
+ $args{ARGS}->{'RefreshSearchInterval'};
+ }
+
+ if ( $args{ARGS}->{'TicketsSortBy'} ) {
+ $session{'tickets_sort_by'} = $args{ARGS}->{'TicketsSortBy'};
+ $session{'tickets_sort_order'} = $args{ARGS}->{'TicketsSortOrder'};
+ $session{'tickets'}->OrderBy(
+ FIELD => $args{ARGS}->{'TicketsSortBy'},
+ ORDER => $args{ARGS}->{'TicketsSortOrder'}
+ );
+ }
+
+ # }}}
+
+ # {{{ Set the query limit
+ if ( defined $args{ARGS}->{'RowsPerPage'} ) {
+ $RT::Logger->debug(
+ "limiting to " . $args{ARGS}->{'RowsPerPage'} . " rows" );
+
+ $session{'tickets_rows_per_page'} = $args{ARGS}->{'RowsPerPage'};
+ $session{'tickets'}->RowsPerPage( $args{ARGS}->{'RowsPerPage'} );
+ }
+
+ # }}}
+ # {{{ Limit priority
+ if ( $args{ARGS}->{'ValueOfPriority'} ne '' ) {
+ $session{'tickets'}->LimitPriority(
+ VALUE => $args{ARGS}->{'ValueOfPriority'},
+ OPERATOR => $args{ARGS}->{'PriorityOp'}
+ );
+ }
+
+ # }}}
+ # {{{ Limit owner
+ if ( $args{ARGS}->{'ValueOfOwner'} ne '' ) {
+ $session{'tickets'}->LimitOwner(
+ VALUE => $args{ARGS}->{'ValueOfOwner'},
+ OPERATOR => $args{ARGS}->{'OwnerOp'}
);
}
- if ( $args{'AttachmentFieldName'} ) {
+ # }}}
+ # {{{ Limit requestor email
- my $cgi_object = $m->cgi_object;
+ if ( $args{ARGS}->{'ValueOfRequestor'} ne '' ) {
+ my $alias = $session{'tickets'}->LimitRequestor(
+ VALUE => $args{ARGS}->{'ValueOfRequestor'},
+ OPERATOR => $args{ARGS}->{'RequestorOp'},
+ );
- if ( my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) {
+ }
+
+ # }}}
+ # {{{ Limit Queue
+ if ( $args{ARGS}->{'ValueOfQueue'} ne '' ) {
+ $session{'tickets'}->LimitQueue(
+ VALUE => $args{ARGS}->{'ValueOfQueue'},
+ OPERATOR => $args{ARGS}->{'QueueOp'}
+ );
+ }
- my ( @content, $buffer );
- while ( my $bytesread = read( $filehandle, $buffer, 4096 ) ) {
- push @content, $buffer;
+ # }}}
+ # {{{ Limit Status
+ if ( $args{ARGS}->{'ValueOfStatus'} ne '' ) {
+ if ( ref( $args{ARGS}->{'ValueOfStatus'} ) ) {
+ foreach my $value ( @{ $args{ARGS}->{'ValueOfStatus'} } ) {
+ $session{'tickets'}->LimitStatus(
+ VALUE => $value,
+ OPERATOR => $args{ARGS}->{'StatusOp'},
+ );
}
+ }
+ else {
+ $session{'tickets'}->LimitStatus(
+ VALUE => $args{ARGS}->{'ValueOfStatus'},
+ OPERATOR => $args{ARGS}->{'StatusOp'},
+ );
+ }
+
+ }
- my $uploadinfo = $cgi_object->uploadInfo($filehandle);
+ # }}}
+ # {{{ Limit Subject
+ if ( $args{ARGS}->{'ValueOfSubject'} ne '' ) {
+ my $val = $args{ARGS}->{'ValueOfSubject'};
+ if ($args{ARGS}->{'SubjectOp'} =~ /like/) {
+ $val = "%".$val."%";
+ }
+ $session{'tickets'}->LimitSubject(
+ VALUE => $val,
+ OPERATOR => $args{ARGS}->{'SubjectOp'},
+ );
+ }
- # Prefer the cached name first over CGI.pm stringification.
- my $filename = $RT::Mason::CGI::Filename;
- $filename = "$filehandle" unless defined($filename);
- $filename = Encode::decode_utf8($filename);
- $filename =~ s{^.*[\\/]}{};
+ # }}}
+ # {{{ Limit Dates
+ if ( $args{ARGS}->{'ValueOfDate'} ne '' ) {
+ my $date = ParseDateToISO( $args{ARGS}->{'ValueOfDate'} );
+ $args{ARGS}->{'DateType'} =~ s/_Date$//;
- $Message->attach(
- Type => $uploadinfo->{'Content-Type'},
- Filename => $filename,
- Data => \@content,
+ if ( $args{ARGS}->{'DateType'} eq 'Updated' ) {
+ $session{'tickets'}->LimitTransactionDate(
+ VALUE => $date,
+ OPERATOR => $args{ARGS}->{'DateOp'},
);
- if ( !$args{'Subject'} && !( defined $args{'Body'} && length $args{'Body'} ) ) {
- $Message->head->set( 'Subject' => $filename );
+ }
+ else {
+ $session{'tickets'}->LimitDate( FIELD => $args{ARGS}->{'DateType'},
+ VALUE => $date,
+ OPERATOR => $args{ARGS}->{'DateOp'},
+ );
+ }
+ }
+
+ # }}}
+ # {{{ Limit Content
+ if ( $args{ARGS}->{'ValueOfAttachmentField'} ne '' ) {
+ my $val = $args{ARGS}->{'ValueOfAttachmentField'};
+ if ($args{ARGS}->{'AttachmentFieldOp'} =~ /like/) {
+ $val = "%".$val."%";
+ }
+ $session{'tickets'}->Limit(
+ FIELD => $args{ARGS}->{'AttachmentField'},
+ VALUE => $val,
+ OPERATOR => $args{ARGS}->{'AttachmentFieldOp'},
+ );
+ }
+
+ # }}}
+
+ # {{{ Limit CustomFields
+
+ foreach my $arg ( keys %{ $args{ARGS} } ) {
+ my $id;
+ if ( $arg =~ /^CustomField(\d+)$/ ) {
+ $id = $1;
+ }
+ else {
+ next;
+ }
+ next unless ( $args{ARGS}->{$arg} );
+
+ my $form = $args{ARGS}->{$arg};
+ my $oper = $args{ARGS}->{ "CustomFieldOp" . $id };
+ foreach my $value ( ref($form) ? @{$form} : ($form) ) {
+ my $quote = 1;
+ if ($oper =~ /like/i) {
+ $value = "%".$value."%";
}
+ if ( $value =~ /^null$/i ) {
+
+ #Don't quote the string 'null'
+ $quote = 0;
+
+ # Convert the operator to something apropriate for nulls
+ $oper = 'IS' if ( $oper eq '=' );
+ $oper = 'IS NOT' if ( $oper eq '!=' );
+ }
+ $session{'tickets'}->LimitCustomField( CUSTOMFIELD => $id,
+ OPERATOR => $oper,
+ QUOTEVALUE => $quote,
+ VALUE => $value );
}
}
- $Message->make_singlepart;
- RT::I18N::SetMIMEEntityToUTF8($Message); # convert text parts into utf-8
+ # }}}
- return ($Message);
}
@@ -1254,7 +735,7 @@ Returns an ISO date and time in GMT
sub ParseDateToISO {
my $date = shift;
- my $date_obj = RT::Date->new( $session{'CurrentUser'} );
+ my $date_obj = RT::Date->new($session{'CurrentUser'});
$date_obj->Set(
Format => 'unknown',
Value => $date
@@ -1264,56 +745,98 @@ sub ParseDateToISO {
# }}}
+# {{{ sub Config
+# TODO: This might eventually read the cookies, user configuration
+# information from the DB, queue configuration information from the
+# DB, etc.
+
+sub Config {
+ my $args = shift;
+ my $key = shift;
+ return $args->{$key} || $RT::WebOptions{$key};
+}
+
+# }}}
+
# {{{ sub ProcessACLChanges
sub ProcessACLChanges {
my $ARGSref = shift;
- #XXX: why don't we get ARGSref like in other Process* subs?
+ my %ARGS = %$ARGSref;
- my @results;
+ my ( $ACL, @results );
- foreach my $arg ( keys %$ARGSref ) {
- next unless ( $arg =~ /^(GrantRight|RevokeRight)-(\d+)-(.+?)-(\d+)$/ );
- my ( $method, $principal_id, $object_type, $object_id ) = ( $1, $2, $3, $4 );
+ foreach my $arg (keys %ARGS) {
+ if ($arg =~ /GrantRight-(\d+)-(.*?)-(\d+)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $rights = $ARGS{$arg};
- my @rights;
- if ( UNIVERSAL::isa( $ARGSref->{$arg}, 'ARRAY' ) ) {
- @rights = @{ $ARGSref->{$arg} };
- } else {
- @rights = $ARGSref->{$arg};
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+
+ my $obj;
+
+ if ($object_type eq 'RT::Queue') {
+ $obj = RT::Queue->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } elsif ($object_type eq 'RT::Group') {
+ $obj = RT::Group->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+
+ } elsif ($object_type eq 'RT::System') {
+ $obj = $RT::System;
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be granted for [_1]", $object_type));
+ next;
+ }
+
+ my @rights = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg});
+ foreach my $right (@rights) {
+ next unless ($right);
+ my ($val, $msg) = $principal->GrantRight(Object => $obj, Right => $right);
+ push (@results, $msg);
+ }
}
- @rights = grep $_, @rights;
- next unless @rights;
-
- my $principal = RT::Principal->new( $session{'CurrentUser'} );
- $principal->Load($principal_id);
-
- my $obj;
- if ( $object_type eq 'RT::System' ) {
- $obj = $RT::System;
- } elsif ( $RT::ACE::OBJECT_TYPES{$object_type} ) {
- $obj = $object_type->new( $session{'CurrentUser'} );
- $obj->Load($object_id);
- unless ( $obj->id ) {
- $RT::Logger->error("couldn't load $object_type #$object_id");
+ elsif ($arg =~ /RevokeRight-(\d+)-(.*?)-(\d+)-(.*?)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $right = $4;
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ next unless ($right);
+ my $obj;
+
+ if ($object_type eq 'RT::Queue') {
+ $obj = RT::Queue->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } elsif ($object_type eq 'RT::Group') {
+ $obj = RT::Group->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+
+ } elsif ($object_type eq 'RT::System') {
+ $obj = $RT::System;
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be revoked for [_1]", $object_type));
next;
}
- } else {
- $RT::Logger->error("object type '$object_type' is incorrect");
- push( @results, loc("System Error") . ': ' . loc( "Rights could not be granted for [_1]", $object_type ) );
- next;
+ my ($val, $msg) = $principal->RevokeRight(Object => $obj, Right => $right);
+ push (@results, $msg);
}
- foreach my $right (@rights) {
- my ( $val, $msg ) = $principal->$method( Object => $obj, Right => $right );
- push( @results, $msg );
- }
+
}
return (@results);
-}
+
+ }
# }}}
@@ -1329,20 +852,59 @@ Returns an array of success/failure messages
sub UpdateRecordObject {
my %args = (
- ARGSRef => undef,
- AttributesRef => undef,
- Object => undef,
+ ARGSRef => undef,
+ AttributesRef => undef,
+ Object => undef,
AttributePrefix => undef,
@_
);
- my $Object = $args{'Object'};
- my @results = $Object->Update(
- AttributesRef => $args{'AttributesRef'},
- ARGSRef => $args{'ARGSRef'},
- AttributePrefix => $args{'AttributePrefix'},
- );
+ my (@results);
+
+ my $object = $args{'Object'};
+ my $attributes = $args{'AttributesRef'};
+ my $ARGSRef = $args{'ARGSRef'};
+ foreach my $attribute (@$attributes) {
+ my $value;
+ if ( defined $ARGSRef->{$attribute} ) {
+ $value = $ARGSRef->{$attribute};
+ }
+ elsif (
+ defined( $args{'AttributePrefix'} )
+ && defined(
+ $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute }
+ )
+ ) {
+ $value = $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute };
+ } else {
+ next;
+ }
+
+ $value =~ s/\r\n/\n/gs;
+
+ if ($value ne $object->$attribute()){
+
+ my $method = "Set$attribute";
+ my ( $code, $msg ) = $object->$method($value);
+
+ push @results, loc($attribute) . ': ' . loc_fuzzy($msg);
+=for loc
+ "[_1] could not be set to [_2].", # loc
+ "That is already the current value", # loc
+ "No value sent to _Set!\n", # loc
+ "Illegal value for [_1]", # loc
+ "The new value has been set.", # loc
+ "No column specified", # loc
+ "Immutable field", # loc
+ "Nonexistant field?", # loc
+ "Invalid data", # loc
+ "Couldn't find row", # loc
+ "Missing a primary key?: [_1]", # loc
+ "Found Object", # loc
+=cut
+ };
+ }
return (@results);
}
@@ -1360,44 +922,37 @@ sub ProcessCustomFieldUpdates {
my $Object = $args{'CustomFieldObj'};
my $ARGSRef = $args{'ARGSRef'};
- my @attribs = qw(Name Type Description Queue SortOrder);
+ my @attribs = qw( Name Type Description Queue SortOrder);
my @results = UpdateRecordObject(
AttributesRef => \@attribs,
Object => $Object,
ARGSRef => $ARGSRef
);
- my $prefix = "CustomField-" . $Object->Id;
- if ( $ARGSRef->{"$prefix-AddValue-Name"} ) {
+ if ( $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" } ) {
+
my ( $addval, $addmsg ) = $Object->AddValue(
- Name => $ARGSRef->{"$prefix-AddValue-Name"},
- Description => $ARGSRef->{"$prefix-AddValue-Description"},
- SortOrder => $ARGSRef->{"$prefix-AddValue-SortOrder"},
+ Name =>
+ $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" },
+ Description => $ARGSRef->{ "CustomField-"
+ . $Object->Id
+ . "-AddValue-Description" },
+ SortOrder => $ARGSRef->{ "CustomField-"
+ . $Object->Id
+ . "-AddValue-SortOrder" },
);
- push( @results, $addmsg );
+ push ( @results, $addmsg );
}
-
- my @delete_values
- = ( ref $ARGSRef->{"$prefix-DeleteValue"} eq 'ARRAY' )
- ? @{ $ARGSRef->{"$prefix-DeleteValue"} }
- : ( $ARGSRef->{"$prefix-DeleteValue"} );
-
+ my @delete_values = (
+ ref $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } eq
+ 'ARRAY' )
+ ? @{ $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } }
+ : ( $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } );
foreach my $id (@delete_values) {
next unless defined $id;
my ( $err, $msg ) = $Object->DeleteValue($id);
- push( @results, $msg );
+ push ( @results, $msg );
}
-
- my $vals = $Object->Values();
- while ( my $cfv = $vals->Next() ) {
- if ( my $so = $ARGSRef->{ "$prefix-SortOrder" . $cfv->Id } ) {
- if ( $cfv->SortOrder != $so ) {
- my ( $err, $msg ) = $cfv->SetSortOrder($so);
- push( @results, $msg );
- }
- }
- }
-
return (@results);
}
@@ -1422,35 +977,30 @@ sub ProcessTicketBasics {
my $TicketObj = $args{'TicketObj'};
my $ARGSRef = $args{'ARGSRef'};
- # {{{ Set basic fields
+ # {{{ Set basic fields
my @attribs = qw(
- Subject
- FinalPriority
- Priority
- TimeEstimated
- TimeWorked
- TimeLeft
- Type
- Status
- Queue
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Status
+ Queue
);
if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) {
my $tempqueue = RT::Queue->new($RT::SystemUser);
$tempqueue->Load( $ARGSRef->{'Queue'} );
if ( $tempqueue->id ) {
- $ARGSRef->{'Queue'} = $tempqueue->id;
+ $ARGSRef->{'Queue'} = $tempqueue->Id();
}
}
- # Status isn't a field that can be set to a null value.
- # RT core complains if you try
- delete $ARGSRef->{'Status'} unless $ARGSRef->{'Status'};
-
my @results = UpdateRecordObject(
AttributesRef => \@attribs,
Object => $TicketObj,
- ARGSRef => $ARGSRef,
+ ARGSRef => $ARGSRef
);
# We special case owner changing, so we can use ForceOwnerChange
@@ -1458,12 +1008,14 @@ sub ProcessTicketBasics {
my ($ChownType);
if ( $ARGSRef->{'ForceOwnerChange'} ) {
$ChownType = "Force";
- } else {
+ }
+ else {
$ChownType = "Give";
}
- my ( $val, $msg ) = $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
- push( @results, $msg );
+ my ( $val, $msg ) =
+ $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
+ push ( @results, $msg );
}
# }}}
@@ -1473,217 +1025,142 @@ sub ProcessTicketBasics {
# }}}
-sub ProcessTicketCustomFieldUpdates {
- my %args = @_;
- $args{'Object'} = delete $args{'TicketObj'};
- my $ARGSRef = { %{ $args{'ARGSRef'} } };
+# {{{ Sub ProcessTicketCustomFieldUpdates
- # Build up a list of objects that we want to work with
- my %custom_fields_to_mod;
- foreach my $arg ( keys %$ARGSRef ) {
- if ( $arg =~ /^Ticket-(\d+-.*)/ ) {
- $ARGSRef->{"Object-RT::Ticket-$1"} = delete $ARGSRef->{$arg};
- } elsif ( $arg =~ /^CustomField-(\d+-.*)/ ) {
- $ARGSRef->{"Object-RT::Ticket--$1"} = delete $ARGSRef->{$arg};
- }
- }
+sub ProcessTicketCustomFieldUpdates {
+ my %args = (
+ ARGSRef => undef,
+ @_
+ );
- return ProcessObjectCustomFieldUpdates( %args, ARGSRef => $ARGSRef );
-}
+ my @results;
-sub ProcessObjectCustomFieldUpdates {
- my %args = @_;
my $ARGSRef = $args{'ARGSRef'};
- my @results;
- # Build up a list of objects that we want to work with
+ # Build up a list of tickets that we want to work with
+ my %tickets_to_mod;
my %custom_fields_to_mod;
- foreach my $arg ( keys %$ARGSRef ) {
-
- # format: Object-<object class>-<object id>-CustomField-<CF id>-<commands>
- next unless $arg =~ /^Object-([\w:]+)-(\d*)-CustomField-(\d+)-(.*)$/;
+ foreach my $arg ( keys %{$ARGSRef} ) {
+ if ( $arg =~ /^Ticket-(\d+)-CustomField-(\d+)-/ ) {
- # For each of those objects, find out what custom fields we want to work with.
- $custom_fields_to_mod{$1}{ $2 || 0 }{$3}{$4} = $ARGSRef->{$arg};
- }
-
- # For each of those objects
- foreach my $class ( keys %custom_fields_to_mod ) {
- foreach my $id ( keys %{ $custom_fields_to_mod{$class} } ) {
- my $Object = $args{'Object'};
- $Object = $class->new( $session{'CurrentUser'} )
- unless $Object && ref $Object eq $class;
-
- $Object->Load($id) unless ( $Object->id || 0 ) == $id;
- unless ( $Object->id ) {
- $RT::Logger->warning("Couldn't load object $class #$id");
- next;
- }
-
- foreach my $cf ( keys %{ $custom_fields_to_mod{$class}{$id} } ) {
- my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
- $CustomFieldObj->LoadById($cf);
- unless ( $CustomFieldObj->id ) {
- $RT::Logger->warning("Couldn't load custom field #$cf");
- next;
- }
- push @results,
- _ProcessObjectCustomFieldUpdates(
- Prefix => "Object-$class-$id-CustomField-$cf-",
- Object => $Object,
- CustomField => $CustomFieldObj,
- ARGS => $custom_fields_to_mod{$class}{$id}{$cf},
- );
- }
+ # For each of those tickets, find out what custom fields we want to work with.
+ $custom_fields_to_mod{$1}{$2} = 1;
}
}
- return @results;
-}
-
-sub _ProcessObjectCustomFieldUpdates {
- my %args = @_;
- my $cf = $args{'CustomField'};
- my $cf_type = $cf->Type;
-
- # Remove blank Values since the magic field will take care of this. Sometimes
- # the browser gives you a blank value which causes CFs to be processed twice
- if ( defined $args{'ARGS'}->{'Values'}
- && !length $args{'ARGS'}->{'Values'}
- && $args{'ARGS'}->{'Values-Magic'} )
- {
- delete $args{'ARGS'}->{'Values'};
- }
-
- my @results;
- foreach my $arg ( keys %{ $args{'ARGS'} } ) {
-
- # skip category argument
- next if $arg eq 'Category';
- # since http won't pass in a form element with a null value, we need
- # to fake it
- if ( $arg eq 'Values-Magic' ) {
+ # For each of those tickets
+ foreach my $tick ( keys %custom_fields_to_mod ) {
+ my $Ticket = RT::Ticket->new( $session{'CurrentUser'} );
+ $Ticket->Load($tick);
- # We don't care about the magic, if there's really a values element;
- next if defined $args{'ARGS'}->{'Value'} && length $args{'ARGS'}->{'Value'};
- next if defined $args{'ARGS'}->{'Values'} && length $args{'ARGS'}->{'Values'};
+ # For each custom field
+ foreach my $cf ( keys %{ $custom_fields_to_mod{$tick} } ) {
- # "Empty" values does not mean anything for Image and Binary fields
- next if $cf_type =~ /^(?:Image|Binary)$/;
+ my $CustomFieldObj = RT::CustomField->new($session{'CurrentUser'});
+ $CustomFieldObj->LoadById($cf);
- $arg = 'Values';
- $args{'ARGS'}->{'Values'} = undef;
- }
-
- my @values = ();
- if ( ref $args{'ARGS'}->{$arg} eq 'ARRAY' ) {
- @values = @{ $args{'ARGS'}->{$arg} };
- } elsif ( $cf_type =~ /text/i ) { # Both Text and Wikitext
- @values = ( $args{'ARGS'}->{$arg} );
- } else {
- @values = split /\r*\n/, $args{'ARGS'}->{$arg}
- if defined $args{'ARGS'}->{$arg};
- }
- @values = grep length, map {
- s/\r+\n/\n/g;
- s/^\s+//;
- s/\s+$//;
- $_;
- }
- grep defined, @values;
+ foreach my $arg ( keys %{$ARGSRef} ) {
+ # since http won't pass in a form element with a null value, we need
+ # to fake it
+ if ($arg =~ /^(.*?)-Values-Magic$/ ) {
+ # We don't care about the magic, if there's really a values element;
+ next if (exists $ARGSRef->{$1.'-Values'}) ;
- if ( $arg eq 'AddValue' || $arg eq 'Value' ) {
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
- Field => $cf->id,
- Value => $value
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'Upload' ) {
- my $value_hash = _UploadedFile( $args{'Prefix'} . $arg ) or next;
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue( %$value_hash, Field => $cf, );
- push( @results, $msg );
- } elsif ( $arg eq 'DeleteValues' ) {
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
- Field => $cf,
- Value => $value,
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'DeleteValueIds' ) {
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
- Field => $cf,
- ValueId => $value,
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'Values' && !$cf->Repeated ) {
- my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id );
-
- my %values_hash;
- foreach my $value (@values) {
- if ( my $entry = $cf_values->HasEntry($value) ) {
- $values_hash{ $entry->id } = 1;
- next;
+ $arg = $1."-Values";
+ $ARGSRef->{$1."-Values"} = undef;
+
}
-
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
- Field => $cf,
- Value => $value
- );
- push( @results, $msg );
- $values_hash{$val} = 1 if $val;
- }
-
- $cf_values->RedoSearch;
- while ( my $cf_value = $cf_values->Next ) {
- next if $values_hash{ $cf_value->id };
-
- my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
- Field => $cf,
- ValueId => $cf_value->id
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'Values' ) {
- my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id );
-
- # keep everything up to the point of difference, delete the rest
- my $delete_flag;
- foreach my $old_cf ( @{ $cf_values->ItemsArrayRef } ) {
- if ( !$delete_flag and @values and $old_cf->Content eq $values[0] ) {
- shift @values;
- next;
+ next unless ( $arg =~ /^Ticket-$tick-CustomField-$cf-/ );
+ my @values =
+ ( ref( $ARGSRef->{$arg} ) eq 'ARRAY' )
+ ? @{ $ARGSRef->{$arg} }
+ : ( $ARGSRef->{$arg} );
+ if ( ( $arg =~ /-AddValue$/ ) || ( $arg =~ /-Value$/ ) ) {
+ foreach my $value (@values) {
+ next unless ($value);
+ my ( $val, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg =~ /-DeleteValues$/ ) {
+ foreach my $value (@values) {
+ next unless ($value);
+ my ( $val, $msg ) = $Ticket->DeleteCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg =~ /-Values$/ and $CustomFieldObj->Type !~ /Entry/) {
+ my $cf_values = $Ticket->CustomFieldValues($cf);
+
+ my %values_hash;
+ foreach my $value (@values) {
+ next unless ($value);
+
+ # build up a hash of values that the new set has
+ $values_hash{$value} = 1;
+
+ unless ( $cf_values->HasEntry($value) ) {
+ my ( $val, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+
+ }
+ while ( my $cf_value = $cf_values->Next ) {
+ unless ( $values_hash{ $cf_value->Content } == 1 ) {
+ my ( $val, $msg ) = $Ticket->DeleteCustomFieldValue(
+ Field => $cf,
+ Value => $cf_value->Content
+ );
+ push ( @results, $msg);
+
+ }
+
+ }
+ }
+ elsif ( $arg =~ /-Values$/ ) {
+ my $cf_values = $Ticket->CustomFieldValues($cf);
+
+ # keep everything up to the point of difference, delete the rest
+ my $delete_flag;
+ foreach my $old_cf (@{$cf_values->ItemsArrayRef}) {
+ if (!$delete_flag and @values and $old_cf->Content eq $values[0]) {
+ shift @values;
+ next;
+ }
+
+ $delete_flag ||= 1;
+ $old_cf->Delete;
+ }
+
+ # now add/replace extra things, if any
+ foreach my $value (@values) {
+ my ( $val, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ else {
+ push ( @results, "User asked for an unknown update type for custom field " . $cf->Name . " for ticket " . $Ticket->id );
}
-
- $delete_flag ||= 1;
- $old_cf->Delete;
- }
-
- # now add/replace extra things, if any
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
- Field => $cf,
- Value => $value
- );
- push( @results, $msg );
}
- } else {
- push(
- @results,
- loc("User asked for an unknown update type for custom field [_1] for [_2] object #[_3]",
- $cf->Name, ref $args{'Object'},
- $args{'Object'}->id
- )
- );
}
+ return (@results);
}
- return @results;
}
+# }}}
+
# {{{ sub ProcessTicketWatchers
=head2 ProcessTicketWatchers ( TicketObj => $Ticket, ARGSRef => \%ARGS );
@@ -1703,31 +1180,29 @@ sub ProcessTicketWatchers {
my $Ticket = $args{'TicketObj'};
my $ARGSRef = $args{'ARGSRef'};
- # Munge watchers
+ # {{{ Munge watchers
foreach my $key ( keys %$ARGSRef ) {
- # Delete deletable watchers
- if ( $key =~ /^Ticket-DeleteWatcher-Type-(.*)-Principal-(\d+)$/ ) {
- my ( $code, $msg ) = $Ticket->DeleteWatcher(
- PrincipalId => $2,
- Type => $1
- );
+ # {{{ Delete deletable watchers
+ if ( ( $key =~ /^Ticket-DelWatcher-Type-(.*)-Principal-(\d+)$/ ) ) {
+ my ( $code, $msg ) =
+ $Ticket->DeleteWatcher(PrincipalId => $2,
+ Type => $1);
push @results, $msg;
}
# Delete watchers in the simple style demanded by the bulk manipulator
elsif ( $key =~ /^Delete(Requestor|Cc|AdminCc)$/ ) {
- my ( $code, $msg ) = $Ticket->DeleteWatcher(
- Email => $ARGSRef->{$key},
- Type => $1
- );
+ my ( $code, $msg ) = $Ticket->DeleteWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1 );
push @results, $msg;
}
- # Add new wathchers by email address
- elsif ( ( $ARGSRef->{$key} || '' ) =~ /^(?:AdminCc|Cc|Requestor)$/
- and $key =~ /^WatcherTypeEmail(\d*)$/ )
+ # }}}
+
+ # Add new wathchers by email address
+ elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ )
+ and ( $key =~ /^WatcherTypeEmail(\d*)$/ ) )
{
#They're in this order because otherwise $1 gets clobbered :/
@@ -1748,21 +1223,18 @@ sub ProcessTicketWatchers {
}
# Add new watchers by owner
- elsif ( $key =~ /^Ticket-AddWatcher-Principal-(\d*)$/ ) {
- my $principal_id = $1;
- my $form = $ARGSRef->{$key};
- foreach my $value ( ref($form) ? @{$form} : ($form) ) {
- next unless $value =~ /^(?:AdminCc|Cc|Requestor)$/i;
+ elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ )
+ and ( $key =~ /^Ticket-AddWatcher-Principal-(\d*)$/ ) ) {
- my ( $code, $msg ) = $Ticket->AddWatcher(
- Type => $value,
- PrincipalId => $principal_id
- );
- push @results, $msg;
- }
+ #They're in this order because otherwise $1 gets clobbered :/
+ my ( $code, $msg ) =
+ $Ticket->AddWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1 );
+ push @results, $msg;
}
-
}
+
+ # }}}
+
return (@results);
}
@@ -1790,33 +1262,33 @@ sub ProcessTicketDates {
# {{{ Set date fields
my @date_fields = qw(
- Told
- Resolved
- Starts
- Started
- Due
+ Told
+ Resolved
+ Starts
+ Started
+ Due
);
#Run through each field in this list. update the value if apropriate
foreach my $field (@date_fields) {
- next unless exists $ARGSRef->{ $field . '_Date' };
- next if $ARGSRef->{ $field . '_Date' } eq '';
-
my ( $code, $msg );
my $DateObj = RT::Date->new( $session{'CurrentUser'} );
- $DateObj->Set(
- Format => 'unknown',
- Value => $ARGSRef->{ $field . '_Date' }
- );
- my $obj = $field . "Obj";
- if ( ( defined $DateObj->Unix )
- and ( $DateObj->Unix != $Ticket->$obj()->Unix() ) )
- {
- my $method = "Set$field";
- my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
- push @results, "$msg";
+ #If it's something other than just whitespace
+ if ( $ARGSRef->{ $field . '_Date' } ne '' ) {
+ $DateObj->Set(
+ Format => 'unknown',
+ Value => $ARGSRef->{ $field . '_Date' }
+ );
+ my $obj = $field . "Obj";
+ if ( ( defined $DateObj->Unix )
+ and ( $DateObj->Unix ne $Ticket->$obj()->Unix() ) )
+ {
+ my $method = "Set$field";
+ my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
+ push @results, "$msg";
+ }
}
}
@@ -1835,39 +1307,13 @@ Returns an array of results messages.
=cut
sub ProcessTicketLinks {
- my %args = (
- TicketObj => undef,
- ARGSRef => undef,
- @_
- );
+ my %args = ( TicketObj => undef,
+ ARGSRef => undef,
+ @_ );
my $Ticket = $args{'TicketObj'};
my $ARGSRef = $args{'ARGSRef'};
- my (@results) = ProcessRecordLinks( RecordObj => $Ticket, ARGSRef => $ARGSRef );
-
- #Merge if we need to
- if ( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ) {
- $ARGSRef->{ $Ticket->Id . "-MergeInto" } =~ s/\s+//g;
- my ( $val, $msg ) = $Ticket->MergeInto( $ARGSRef->{ $Ticket->Id . "-MergeInto" } );
- push @results, $msg;
- }
-
- return (@results);
-}
-
-# }}}
-
-sub ProcessRecordLinks {
- my %args = (
- RecordObj => undef,
- ARGSRef => undef,
- @_
- );
-
- my $Record = $args{'RecordObj'};
- my $ARGSRef = $args{'ARGSRef'};
-
my (@results);
# Delete links that are gone gone gone.
@@ -1877,11 +1323,11 @@ sub ProcessRecordLinks {
my $type = $2;
my $target = $3;
- my ( $val, $msg ) = $Record->DeleteLink(
- Base => $base,
- Type => $type,
- Target => $target
- );
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
push @results, $msg;
@@ -1892,138 +1338,40 @@ sub ProcessRecordLinks {
my @linktypes = qw( DependsOn MemberOf RefersTo );
foreach my $linktype (@linktypes) {
- if ( $ARGSRef->{ $Record->Id . "-$linktype" } ) {
- $ARGSRef->{ $Record->Id . "-$linktype" } = join( ' ', @{ $ARGSRef->{ $Record->Id . "-$linktype" } } )
- if ref( $ARGSRef->{ $Record->Id . "-$linktype" } );
-
- for my $luri ( split( / /, $ARGSRef->{ $Record->Id . "-$linktype" } ) ) {
- next unless $luri;
- $luri =~ s/\s+$//; # Strip trailing whitespace
- my ( $val, $msg ) = $Record->AddLink(
- Target => $luri,
- Type => $linktype
- );
+ if ( $ARGSRef->{ $Ticket->Id . "-$linktype" } ) {
+ for my $luri ( split ( / /, $ARGSRef->{ $Ticket->Id . "-$linktype" } ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ my ( $val, $msg ) = $Ticket->AddLink( Target => $luri,
+ Type => $linktype );
push @results, $msg;
}
}
- if ( $ARGSRef->{ "$linktype-" . $Record->Id } ) {
- $ARGSRef->{ "$linktype-" . $Record->Id } = join( ' ', @{ $ARGSRef->{ "$linktype-" . $Record->Id } } )
- if ref( $ARGSRef->{ "$linktype-" . $Record->Id } );
-
- for my $luri ( split( / /, $ARGSRef->{ "$linktype-" . $Record->Id } ) ) {
- next unless $luri;
- my ( $val, $msg ) = $Record->AddLink(
- Base => $luri,
- Type => $linktype
- );
+ if ( $ARGSRef->{ "$linktype-" . $Ticket->Id } ) {
+
+ for my $luri ( split ( / /, $ARGSRef->{ "$linktype-" . $Ticket->Id } ) ) {
+ my ( $val, $msg ) = $Ticket->AddLink( Base => $luri,
+ Type => $linktype );
push @results, $msg;
}
- }
+ }
}
- return (@results);
-}
-
-=head2 _UploadedFile ( $arg );
-
-Takes a CGI parameter name; if a file is uploaded under that name,
-return a hash reference suitable for AddCustomFieldValue's use:
-C<( Value => $filename, LargeContent => $content, ContentType => $type )>.
-
-Returns C<undef> if no files were uploaded in the C<$arg> field.
-
-=cut
-
-sub _UploadedFile {
- my $arg = shift;
- my $cgi_object = $m->cgi_object;
- my $fh = $cgi_object->upload($arg) or return undef;
- my $upload_info = $cgi_object->uploadInfo($fh);
-
- my $filename = "$fh";
- $filename =~ s#^.*[\\/]##;
- binmode($fh);
-
- return {
- Value => $filename,
- LargeContent => do { local $/; scalar <$fh> },
- ContentType => $upload_info->{'Content-Type'},
- };
-}
-
-sub GetColumnMapEntry {
- my %args = ( Map => {}, Name => '', Attribute => undef, @_ );
-
- # deal with the simplest thing first
- if ( $args{'Map'}{ $args{'Name'} } ) {
- return $args{'Map'}{ $args{'Name'} }{ $args{'Attribute'} };
- }
-
- # complex things
- elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.{(.+)}$/ ) {
- return undef unless $args{'Map'}->{$mainkey};
- return $args{'Map'}{$mainkey}{ $args{'Attribute'} }
- unless ref $args{'Map'}{$mainkey}{ $args{'Attribute'} } eq 'CODE';
-
- return sub { $args{'Map'}{$mainkey}{ $args{'Attribute'} }->( @_, $subkey ) };
- }
- return undef;
-}
-
-sub ProcessColumnMapValue {
- my $value = shift;
- my %args = ( Arguments => [], Escape => 1, @_ );
-
- if ( ref $value ) {
- if ( UNIVERSAL::isa( $value, 'CODE' ) ) {
- my @tmp = $value->( @{ $args{'Arguments'} } );
- return ProcessColumnMapValue( ( @tmp > 1 ? \@tmp : $tmp[0] ), %args );
- } elsif ( UNIVERSAL::isa( $value, 'ARRAY' ) ) {
- return join '', map ProcessColumnMapValue( $_, %args ), @$value;
- } elsif ( UNIVERSAL::isa( $value, 'SCALAR' ) ) {
- return $$value;
- }
+ #Merge if we need to
+ if ( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ) {
+ my ( $val, $msg ) =
+ $Ticket->MergeInto( $ARGSRef->{ $Ticket->Id . "-MergeInto" } );
+ push @results, $msg;
}
- return $m->interp->apply_escapes( $value, 'h' ) if $args{'Escape'};
- return $value;
-}
-
-=head2 _load_container_object ( $type, $id );
-
-Instantiate container object for saving searches.
-
-=cut
-
-sub _load_container_object {
- my ( $obj_type, $obj_id ) = @_;
- return RT::SavedSearch->new( $session{'CurrentUser'} )->_load_privacy_object( $obj_type, $obj_id );
+ return (@results);
}
-=head2 _parse_saved_search ( $arg );
-
-Given a serialization string for saved search, and returns the
-container object and the search id.
-
-=cut
-
-sub _parse_saved_search {
- my $spec = shift;
- return unless $spec;
- if ( $spec !~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/ ) {
- return;
- }
- my $obj_type = $1;
- my $obj_id = $2;
- my $search_id = $3;
-
- return ( _load_container_object( $obj_type, $obj_id ), $search_id );
-}
+# }}}
eval "require RT::Interface::Web_Vendor";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Web_Vendor.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Vendor.pm});
eval "require RT::Interface::Web_Local";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Web_Local.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Local.pm});
1;
diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm
new file mode 100644
index 000000000..1999096a7
--- /dev/null
+++ b/rt/lib/RT/Interface/Web_Vendor.pm
@@ -0,0 +1,201 @@
+# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+# Copyright (c) 2008 Freeside Internet Services, Inc.
+#
+# 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.
+
+=head1 NAME
+
+RT::Interface::Web_Vendor
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Freeside vendor overlay for RT::Interface::Web.
+
+=begin testing
+
+use_ok(RT::Interface::Web_Vendor);
+
+=end testing
+
+=cut
+
+#package RT::Interface::Web;
+#use strict;
+
+package HTML::Mason::Commands;
+use strict;
+
+=head2 ProcessTicketCustomers
+
+=cut
+
+sub ProcessTicketCustomers {
+ my %args = (
+ TicketObj => undef,
+ ARGSRef => undef,
+ Debug => 0,
+ @_
+ );
+ my @results = ();
+
+ my $Ticket = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+ my $Debug = $args{'Debug'};
+ my $me = 'ProcessTicketCustomers';
+
+ ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+ ###
+
+ ###
+ #find new customers
+ ###
+
+ my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 }
+ grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ keys %$ARGSRef;
+
+ #my @delete_custnums =
+ # map { /^Ticket-AddCustomer-(\d+)$/; $1 }
+ # grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ # keys %$ARGSRef;
+
+ ###
+ #figure out if we're going to auto-link requestors, and find them if so
+ ###
+
+ my $num_cur_cust = $Ticket->Customers->Count;
+ my $num_new_cust = scalar(@custnums);
+ warn "$me: $num_cur_cust current customers / $num_new_cust new customers\n"
+ if $Debug;
+
+ #if we're linking the first ticket to one customer
+ my $link_requestors = ( $num_cur_cust == 0 && $num_new_cust == 1 );
+ warn "$me: adding a single customer to a previously customerless".
+ " ticket, so linking customers to requestor too\n"
+ if $Debug && $link_requestors;
+
+ my @Requestors = ();
+ if ( $link_requestors ) {
+
+ #find any requestors without customers
+ @Requestors =
+ grep { ! $_->Customers->Count }
+ @{ $Ticket->Requestors->UserMembersObj->ItemsArrayRef };
+
+ warn "$me: found ". scalar(@Requestors). " requestors without".
+ " customers; linking them\n"
+ if $Debug;
+
+ }
+
+ ###
+ #link ticket (and requestors) to customers
+ ###
+
+ foreach my $custnum ( @custnums ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Ticket->AddLink(@link);
+ push @results, $msg;
+
+ #add customer links to requestors
+ foreach my $Requestor ( @Requestors ) {
+ my( $val, $msg ) = $Requestor->AddLink(@link);
+ push @results, $msg;
+ warn "$me: linking requestor to custnum $custnum: $msg\n"
+ if $Debug > 1;
+ }
+
+ }
+
+ return @results;
+
+}
+
+#false laziness w/above... eventually it should go away in favor of this
+sub ProcessObjectCustomers {
+ my %args = (
+ Object => undef,
+ ARGSRef => undef,
+ @_
+ );
+ my @results = ();
+
+ my $Object = $args{'Object'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Object->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+ ###
+
+ #my @delete_custnums =
+ # map { /^Object-AddCustomer-(\d+)$/; $1 }
+ # grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ # keys %$ARGSRef;
+
+ my @custnums = map { /^Object-AddCustomer-(\d+)$/; $1 }
+ grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ keys %$ARGSRef;
+
+ foreach my $custnum ( @custnums ) {
+ my( $val, $msg ) =
+ $Object->AddLink( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+ push @results, $msg;
+ }
+
+ return @results;
+
+}
+
+1;
+
diff --git a/rt/lib/RT/Link.pm b/rt/lib/RT/Link.pm
index 0cab23790..962c378a8 100644
--- a/rt/lib/RT/Link.pm
+++ b/rt/lib/RT/Link.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -123,7 +98,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -132,14 +107,14 @@ Returns the current value of id.
=cut
-=head2 Base
+=item Base
Returns the current value of Base.
(In the database, Base is stored as varchar(240).)
-=head2 SetBase VALUE
+=item SetBase VALUE
Set Base to VALUE.
@@ -150,14 +125,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Target
+=item Target
Returns the current value of Target.
(In the database, Target is stored as varchar(240).)
-=head2 SetTarget VALUE
+=item SetTarget VALUE
Set Target to VALUE.
@@ -168,14 +143,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(20).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -186,14 +161,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LocalTarget
+=item LocalTarget
Returns the current value of LocalTarget.
(In the database, LocalTarget is stored as int(11).)
-=head2 SetLocalTarget VALUE
+=item SetLocalTarget VALUE
Set LocalTarget to VALUE.
@@ -204,14 +179,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LocalBase
+=item LocalBase
Returns the current value of LocalBase.
(In the database, LocalBase is stored as int(11).)
-=head2 SetLocalBase VALUE
+=item SetLocalBase VALUE
Set LocalBase to VALUE.
@@ -222,7 +197,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -231,7 +206,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -240,7 +215,7 @@ Returns the current value of LastUpdated.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -249,7 +224,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -259,29 +234,29 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Base =>
- {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+ {read => 1, write => 1, type => 'varchar(240)', default => ''},
Target =>
- {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+ {read => 1, write => 1, type => 'varchar(240)', default => ''},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 20, is_blob => 0, is_numeric => 0, type => 'varchar(20)', default => ''},
+ {read => 1, write => 1, type => 'varchar(20)', default => ''},
LocalTarget =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
LocalBase =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -313,7 +288,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Links.pm b/rt/lib/RT/Links.pm
index ecf9643fd..7a1773af9 100644
--- a/rt/lib/RT/Links.pm
+++ b/rt/lib/RT/Links.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Link item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Queue.pm b/rt/lib/RT/Queue.pm
index 183ab8f9f..b362c9f0d 100755
--- a/rt/lib/RT/Queue.pm
+++ b/rt/lib/RT/Queue.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -132,7 +107,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -141,14 +116,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -159,14 +134,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -177,14 +152,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CorrespondAddress
+=item CorrespondAddress
Returns the current value of CorrespondAddress.
(In the database, CorrespondAddress is stored as varchar(120).)
-=head2 SetCorrespondAddress VALUE
+=item SetCorrespondAddress VALUE
Set CorrespondAddress to VALUE.
@@ -195,14 +170,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CommentAddress
+=item CommentAddress
Returns the current value of CommentAddress.
(In the database, CommentAddress is stored as varchar(120).)
-=head2 SetCommentAddress VALUE
+=item SetCommentAddress VALUE
Set CommentAddress to VALUE.
@@ -213,14 +188,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 InitialPriority
+=item InitialPriority
Returns the current value of InitialPriority.
(In the database, InitialPriority is stored as int(11).)
-=head2 SetInitialPriority VALUE
+=item SetInitialPriority VALUE
Set InitialPriority to VALUE.
@@ -231,14 +206,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 FinalPriority
+=item FinalPriority
Returns the current value of FinalPriority.
(In the database, FinalPriority is stored as int(11).)
-=head2 SetFinalPriority VALUE
+=item SetFinalPriority VALUE
Set FinalPriority to VALUE.
@@ -249,14 +224,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 DefaultDueIn
+=item DefaultDueIn
Returns the current value of DefaultDueIn.
(In the database, DefaultDueIn is stored as int(11).)
-=head2 SetDefaultDueIn VALUE
+=item SetDefaultDueIn VALUE
Set DefaultDueIn to VALUE.
@@ -267,7 +242,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -276,7 +251,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -285,7 +260,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -294,7 +269,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -303,14 +278,14 @@ Returns the current value of LastUpdated.
=cut
-=head2 Disabled
+=item Disabled
Returns the current value of Disabled.
(In the database, Disabled is stored as smallint(6).)
-=head2 SetDisabled VALUE
+=item SetDisabled VALUE
Set Disabled to VALUE.
@@ -322,35 +297,35 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
CorrespondAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
CommentAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
InitialPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
FinalPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
DefaultDueIn =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Disabled =>
- {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ {read => 1, write => 1, type => 'smallint(6)', default => '0'},
}
};
@@ -382,7 +357,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Queues.pm b/rt/lib/RT/Queues.pm
index 48f40f1e5..60aec9086 100755
--- a/rt/lib/RT/Queues.pm
+++ b/rt/lib/RT/Queues.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Queue item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm
index b2e1e7110..8664e2867 100755
--- a/rt/lib/RT/Record.pm
+++ b/rt/lib/RT/Record.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Record - Base class for RT record objects
@@ -56,6 +55,11 @@
=head1 DESCRIPTION
+=begin testing
+
+ok (require RT::Record);
+
+=end testing
=head1 METHODS
@@ -66,21 +70,25 @@ package RT::Record;
use strict;
use warnings;
+our @ISA;
+use base qw(RT::Base);
+
use RT::Date;
+use RT::I18N;
use RT::User;
use RT::Attributes;
+use DBIx::SearchBuilder::Record::Cachable;
use Encode qw();
our $_TABLE_ATTR = { };
-use RT::Base;
-my $base = 'DBIx::SearchBuilder::Record::Cachable';
-if ( $RT::Config && $RT::Config->Get('DontCacheSearchBuilderRecords') ) {
- $base = 'DBIx::SearchBuilder::Record';
+
+if ( $RT::DontCacheSearchBuilderRecords ) {
+ push (@ISA, 'DBIx::SearchBuilder::Record');
+} else {
+ push (@ISA, 'DBIx::SearchBuilder::Record::Cachable');
+
}
-eval "require $base" or die $@;
-our @ISA = 'RT::Base';
-push @ISA, $base;
# {{{ sub _Init
@@ -100,7 +108,10 @@ The primary keys for RT classes is 'id'
=cut
-sub _PrimaryKeys { return ['id'] }
+sub _PrimaryKeys {
+ my $self = shift;
+ return ( ['id'] );
+}
# }}}
@@ -126,6 +137,14 @@ sub Delete {
Returns a string which is this object's type. The type is the class,
without the "RT::" prefix.
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my $group = RT::Group->new($RT::SystemUser);
+is($ticket->ObjectTypeStr, 'Ticket', "Ticket returns correct typestring");
+is($group->ObjectTypeStr, 'Group', "Group returns correct typestring");
+
+=end testing
=cut
@@ -241,7 +260,10 @@ sub FirstAttribute {
# {{{ sub _Handle
-sub _Handle { return $RT::Handle }
+sub _Handle {
+ my $self = shift;
+ return ($RT::Handle);
+}
# }}}
@@ -314,6 +336,8 @@ sub Create {
}
if (UNIVERSAL::isa('errno',$id)) {
+ exit(0);
+ warn "It's here!";
return(undef);
}
@@ -343,29 +367,40 @@ DB is case sensitive
sub LoadByCols {
my $self = shift;
+ my %hash = (@_);
# We don't want to hang onto this
delete $self->{'attributes'};
- return $self->SUPER::LoadByCols( @_ ) unless $self->_Handle->CaseSensitive;
-
# If this database is case sensitive we need to uncase objects for
# explicit loading
- my %hash = (@_);
- foreach my $key ( keys %hash ) {
-
- # If we've been passed an empty value, we can't do the lookup.
- # We don't need to explicitly downcase integers or an id.
- if ( $key ne 'id' && defined $hash{ $key } && $hash{ $key } !~ /^\d+$/ ) {
- my ($op, $val, $func);
- ($key, $op, $val, $func) =
- $self->_Handle->_MakeClauseCaseInsensitive( $key, '=', delete $hash{ $key } );
- $hash{$key}->{operator} = $op;
- $hash{$key}->{value} = $val;
- $hash{$key}->{function} = $func;
+ if ( $self->_Handle->CaseSensitive ) {
+ my %newhash;
+ foreach my $key ( keys %hash ) {
+
+ # If we've been passed an empty value, we can't do the lookup.
+ # We don't need to explicitly downcase integers or an id.
+ if ( $key =~ '^id$'
+ || !defined( $hash{$key} )
+ || $hash{$key} =~ /^\d+$/
+ )
+ {
+ $newhash{$key} = $hash{$key};
+ }
+ else {
+ my ($op, $val, $func);
+ ($key, $op, $val, $func) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key});
+ $newhash{$key}->{operator} = $op;
+ $newhash{$key}->{value} = $val;
+ $newhash{$key}->{function} = $func;
+ }
}
+
+ # We've clobbered everything we care about. bash the old hash
+ # and replace it with the new hash
+ %hash = %newhash;
}
- return $self->SUPER::LoadByCols( %hash );
+ $self->SUPER::LoadByCols(%hash);
}
# }}}
@@ -494,7 +529,7 @@ sub _Set {
$msg =
$self->loc(
"[_1] changed from [_2] to [_3]",
- $self->loc( $args{'Field'} ),
+ $args{'Field'},
( $old_val ? "'$old_val'" : $self->loc("(no value)") ),
'"' . $self->__Value( $args{'Field'}) . '"'
);
@@ -627,20 +662,27 @@ sub SQLType {
}
+
sub __Value {
my $self = shift;
my $field = shift;
- my %args = ( decode_utf8 => 1, @_ );
+ my %args = ( decode_utf8 => 1,
+ @_ );
- unless ( $field ) {
- $RT::Logger->error("__Value called with undef field");
+ unless (defined $field && $field) {
+ $RT::Logger->error("$self __Value called with undef field");
}
+ my $value = $self->SUPER::__Value($field);
+
+ return('') if ( !defined($value) || $value eq '');
- my $value = $self->SUPER::__Value( $field );
if( $args{'decode_utf8'} ) {
- return Encode::decode_utf8( $value ) unless Encode::is_utf8( $value );
+ # XXX: is_utf8 check should be here unless Encode bug would be fixed
+ # see http://rt.cpan.org/NoAuth/Bug.html?id=14559
+ return Encode::decode_utf8($value) unless Encode::is_utf8($value);
} else {
- return Encode::encode_utf8( $value ) if Encode::is_utf8( $value );
+ # check is_utf8 here just to be shure
+ return Encode::encode_utf8($value) if Encode::is_utf8($value);
}
return $value;
}
@@ -658,7 +700,6 @@ sub _CacheConfig {
sub _BuildTableAttributes {
my $self = shift;
- my $class = ref($self) || $self;
my $attributes;
if ( UNIVERSAL::can( $self, '_CoreAccessible' ) ) {
@@ -670,19 +711,37 @@ sub _BuildTableAttributes {
foreach my $column (%$attributes) {
foreach my $attr ( %{ $attributes->{$column} } ) {
- $_TABLE_ATTR->{$class}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ if ( UNIVERSAL::can( $self, '_OverlayAccessible' ) ) {
+ $attributes = $self->_OverlayAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
}
}
- foreach my $method ( qw(_OverlayAccessible _VendorAccessible _LocalAccessible) ) {
- next unless UNIVERSAL::can( $self, $method );
- $attributes = $self->$method();
+ if ( UNIVERSAL::can( $self, '_VendorAccessible' ) ) {
+ $attributes = $self->_VendorAccessible();
foreach my $column (%$attributes) {
foreach my $attr ( %{ $attributes->{$column} } ) {
- $_TABLE_ATTR->{$class}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
}
}
}
+ if ( UNIVERSAL::can( $self, '_LocalAccessible' ) ) {
+ $attributes = $self->_LocalAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ }
+
}
@@ -695,7 +754,7 @@ DBIx::SearchBuilder::Record
sub _ClassAccessible {
my $self = shift;
- return $_TABLE_ATTR->{ref($self) || $self};
+ return $_TABLE_ATTR->{ref($self)};
}
=head2 _Accessible COLUMN ATTRIBUTE
@@ -728,19 +787,19 @@ sub _EncodeLOB {
my $ContentEncoding = 'none';
#get the max attachment length from RT
- my $MaxSize = RT->Config->Get('MaxAttachmentSize');
+ my $MaxSize = $RT::MaxAttachmentSize;
#if the current attachment contains nulls and the
#database doesn't support embedded nulls
- if ( RT->Config->Get('AlwaysUseBase64') or
+ if ( $RT::AlwaysUseBase64 or
( !$RT::Handle->BinarySafeBLOBs ) && ( $Body =~ /\x00/ ) ) {
# set a flag telling us to mimencode the attachment
$ContentEncoding = 'base64';
#cut the max attchment size by 25% (for mime-encoding overhead.
- $RT::Logger->debug("Max size is $MaxSize");
+ $RT::Logger->debug("Max size is $MaxSize\n");
$MaxSize = $MaxSize * 3 / 4;
# Some databases (postgres) can't handle non-utf8 data
} elsif ( !$RT::Handle->BinarySafeBLOBs
@@ -753,7 +812,7 @@ sub _EncodeLOB {
if ( ($MaxSize) and ( $MaxSize < length($Body) ) ) {
# if we're supposed to truncate large attachments
- if (RT->Config->Get('TruncateLongAttachments')) {
+ if ($RT::TruncateLongAttachments) {
# truncate the attachment to that length.
$Body = substr( $Body, 0, $MaxSize );
@@ -761,12 +820,13 @@ sub _EncodeLOB {
}
# elsif we're supposed to drop large attachments on the floor,
- elsif (RT->Config->Get('DropLongAttachments')) {
+ elsif ($RT::DropLongAttachments) {
# drop the attachment on the floor
$RT::Logger->info( "$self: Dropped an attachment of size "
- . length($Body));
- $RT::Logger->info( "It started: " . substr( $Body, 0, 60 ) );
+ . length($Body) . "\n"
+ . "It started: " . substr( $Body, 0, 60 ) . "\n"
+ );
return ("none", "Large attachment dropped" );
}
}
@@ -790,8 +850,8 @@ sub _EncodeLOB {
sub _DecodeLOB {
my $self = shift;
- my $ContentType = shift || '';
- my $ContentEncoding = shift || 'none';
+ my $ContentType = shift;
+ my $ContentEncoding = shift;
my $Content = shift;
if ( $ContentEncoding eq 'base64' ) {
@@ -803,12 +863,14 @@ sub _DecodeLOB {
elsif ( $ContentEncoding && $ContentEncoding ne 'none' ) {
return ( $self->loc( "Unknown ContentEncoding [_1]", $ContentEncoding ) );
}
+
if ( RT::I18N::IsTextualContentType($ContentType) ) {
$Content = Encode::decode_utf8($Content) unless Encode::is_utf8($Content);
}
return ($Content);
}
+# {{{ LINKDIRMAP
# A helper table for links mapping to make it easier
# to build and parse links between tickets
@@ -882,18 +944,11 @@ sub Update {
# This is in an eval block because $object might not exist.
# and might not have a Name method. But "can" won't find autoloaded
# items. If it fails, we don't care
- do {
- no warnings "uninitialized";
- local $@;
- eval {
- my $object = $attribute . "Obj";
- my $name = $self->$object->Name;
- next if $name eq $value || $name eq ($value || 0);
- };
- next if $value eq $self->$attribute();
- next if ($value || 0) eq $self->$attribute();
+ eval {
+ my $object = $attribute . "Obj";
+ next if ($self->$object->Name eq $value);
};
-
+ next if ( $value eq $self->$attribute() );
my $method = "Set$attribute";
my ( $code, $msg ) = $self->$method($value);
my ($prefix) = ref($self) =~ /RT(?:.*)::(\w+)/;
@@ -901,17 +956,7 @@ sub Update {
# Default to $id, but use name if we can get it.
my $label = $self->id;
$label = $self->Name if (UNIVERSAL::can($self,'Name'));
- # this requires model names to be loc'ed.
-
-=for loc
-
- "Ticket" # loc
- "User" # loc
- "Group" # loc
- "Queue" # loc
-=cut
-
- push @results, $self->loc( $prefix ) . " $label: ". $msg;
+ push @results, $self->loc( "$prefix [_1]", $label ) . ': '. $msg;
=for loc
@@ -1020,10 +1065,54 @@ sub DependedOnBy {
=head2 HasUnresolvedDependencies
-Takes a paramhash of Type (default to '__any'). Returns the number of
-unresolved dependencies, if $self->UnresolvedDependencies returns an
-object with one or more members of that type. Returns false
-otherwise.
+ Takes a paramhash of Type (default to '__any'). Returns true if
+$self->UnresolvedDependencies returns an object with one or more members
+of that type. Returns false otherwise
+
+
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ($id, $trans, $msg) = $t1->Create(Subject => 'DepTest1', Queue => 'general');
+ok($id, "Created dep test 1 - $msg");
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+my ($id2, $trans, $msg2) = $t2->Create(Subject => 'DepTest2', Queue => 'general');
+ok($id2, "Created dep test 2 - $msg2");
+my $t3 = RT::Ticket->new($RT::SystemUser);
+my ($id3, $trans, $msg3) = $t3->Create(Subject => 'DepTest3', Queue => 'general', Type => 'approval');
+ok($id3, "Created dep test 3 - $msg3");
+my ($addid, $addmsg);
+ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t2->id));
+ok ($addid, $addmsg);
+ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t3->id));
+
+ok ($addid, $addmsg);
+my $link = RT::Link->new($RT::SystemUser);
+my ($rv, $msg) = $link->Load($addid);
+ok ($rv, $msg);
+ok ($link->LocalTarget == $t3->id, "Link LocalTarget is correct");
+ok ($link->LocalBase == $t1->id, "Link LocalBase is correct");
+
+ok ($t1->HasUnresolvedDependencies, "Ticket ".$t1->Id." has unresolved deps");
+ok (!$t1->HasUnresolvedDependencies( Type => 'blah' ), "Ticket ".$t1->Id." has no unresolved blahs");
+ok ($t1->HasUnresolvedDependencies( Type => 'approval' ), "Ticket ".$t1->Id." has unresolved approvals");
+ok (!$t2->HasUnresolvedDependencies, "Ticket ".$t2->Id." has no unresolved deps");
+;
+
+my ($rid, $rmsg)= $t1->Resolve();
+ok(!$rid, $rmsg);
+my ($rid2, $rmsg2) = $t2->Resolve();
+ok ($rid2, $rmsg2);
+($rid, $rmsg)= $t1->Resolve();
+ok(!$rid, $rmsg);
+my ($rid3,$rmsg3) = $t3->Resolve;
+ok ($rid3,$rmsg3);
+($rid, $rmsg)= $t1->Resolve();
+ok($rid, $rmsg);
+
+
+=end testing
=cut
@@ -1046,7 +1135,7 @@ sub HasUnresolvedDependencies {
}
if ($deps->Count > 0) {
- return $deps->Count;
+ return 1;
}
else {
return (undef);
@@ -1095,54 +1184,27 @@ dependency search.
sub AllDependedOnBy {
my $self = shift;
- return $self->_AllLinkedTickets( LinkType => 'DependsOn',
- Direction => 'Target', @_ );
-}
-
-=head2 AllDependsOn
-
-Returns an array of RT::Ticket objects which this ticket (directly or
-indirectly) depends on; takes an optional 'Type' argument in the param
-hash, which will limit returned tickets to that type, as well as cause
-tickets with that type to serve as 'leaf' nodes that stops the
-recursive dependency search.
-
-=cut
-
-sub AllDependsOn {
- my $self = shift;
- return $self->_AllLinkedTickets( LinkType => 'DependsOn',
- Direction => 'Base', @_ );
-}
-
-sub _AllLinkedTickets {
- my $self = shift;
-
+ my $dep = $self->DependedOnBy;
my %args = (
- LinkType => undef,
- Direction => undef,
Type => undef,
_found => {},
_top => 1,
@_
);
- my $dep = $self->_Links( $args{Direction}, $args{LinkType});
while (my $link = $dep->Next()) {
- my $uri = $args{Direction} eq 'Target' ? $link->BaseURI : $link->TargetURI;
- next unless ($uri->IsLocal());
- my $obj = $args{Direction} eq 'Target' ? $link->BaseObj : $link->TargetObj;
- next if $args{_found}{$obj->Id};
+ next unless ($link->BaseURI->IsLocal());
+ next if $args{_found}{$link->BaseObj->Id};
if (!$args{Type}) {
- $args{_found}{$obj->Id} = $obj;
- $obj->_AllLinkedTickets( %args, _top => 0 );
+ $args{_found}{$link->BaseObj->Id} = $link->BaseObj;
+ $link->BaseObj->AllDependedOnBy( %args, _top => 0 );
}
- elsif ($obj->Type eq $args{Type}) {
- $args{_found}{$obj->Id} = $obj;
+ elsif ($link->BaseObj->Type eq $args{Type}) {
+ $args{_found}{$link->BaseObj->Id} = $link->BaseObj;
}
else {
- $obj->_AllLinkedTickets( %args, _top => 0 );
+ $link->BaseObj->AllDependedOnBy( %args, _top => 0 );
}
}
@@ -1171,8 +1233,37 @@ sub DependsOn {
# }}}
+# {{{ Customers
+
+=head2 Customers
+
+ This returns an RT::Links object which references all the customers that this object is a member of.
+
+=cut
+sub Customers {
+ my( $self, %opt ) = @_;
+ my $Debug = $opt{'Debug'};
+
+ unless ( $self->{'Customers'} ) {
+
+ $self->{'Customers'} = $self->MemberOf->Clone;
+
+ $self->{'Customers'}->Limit(
+ FIELD => 'Target',
+ OPERATOR => 'STARTSWITH',
+ VALUE => 'freeside://freeside/cust_main/',
+ );
+ }
+ warn "->Customers method called on $self; returning ".
+ ref($self->{'Customers'}). ' object'
+ if $Debug;
+
+ return $self->{'Customers'};
+}
+
+# }}}
# {{{ sub _Links
@@ -1214,50 +1305,6 @@ sub _Links {
# }}}
-# {{{ sub FormatType
-
-=head2 FormatType
-
-Takes a Type and returns a string that is more human readable.
-
-=cut
-
-sub FormatType{
- my $self = shift;
- my %args = ( Type => '',
- @_
- );
- $args{Type} =~ s/([A-Z])/" " . lc $1/ge;
- $args{Type} =~ s/^\s+//;
- return $args{Type};
-}
-
-
-# }}}
-
-# {{{ sub FormatLink
-
-=head2 FormatLink
-
-Takes either a Target or a Base and returns a string of human friendly text.
-
-=cut
-
-sub FormatLink {
- my $self = shift;
- my %args = ( Object => undef,
- FallBack => '',
- @_
- );
- my $text = "URI " . $args{FallBack};
- if ($args{Object} && $args{Object}->isa("RT::Ticket")) {
- $text = "Ticket " . $args{Object}->id;
- }
- return $text;
-}
-
-# }}}
-
# {{{ sub _AddLink
=head2 _AddLink
@@ -1269,6 +1316,7 @@ Returns C<link id>, C<message> and C<exist> flag.
=cut
+
sub _AddLink {
my $self = shift;
my %args = ( Target => '',
@@ -1283,7 +1331,7 @@ sub _AddLink {
my $direction;
if ( $args{'Base'} and $args{'Target'} ) {
- $RT::Logger->debug( "$self tried to create a link. both base and target were specified" );
+ $RT::Logger->debug( "$self tried to create a link. both base and target were specified\n" );
return ( 0, $self->loc("Can't specifiy both base and target") );
}
elsif ( $args{'Base'} ) {
@@ -1325,14 +1373,10 @@ sub _AddLink {
return ( 0, $self->loc("Link could not be created") );
}
- my $basetext = $self->FormatLink(Object => $link->BaseObj,
- FallBack => $args{Base});
- my $targettext = $self->FormatLink(Object => $link->TargetObj,
- FallBack => $args{Target});
- my $typetext = $self->FormatType(Type => $args{Type});
my $TransString =
- "$basetext $typetext $targettext.";
- return ( $linkid, $TransString ) ;
+ "Record $args{'Base'} $args{Type} record $args{'Target'}.";
+
+ return ( $linkid, $self->loc( "Link created ([_1])", $TransString ) );
}
# }}}
@@ -1363,7 +1407,7 @@ sub _DeleteLink {
my $remote_link;
if ( $args{'Base'} and $args{'Target'} ) {
- $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target");
+ $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target\n");
return ( 0, $self->loc("Can't specifiy both base and target") );
}
elsif ( $args{'Base'} ) {
@@ -1377,32 +1421,28 @@ sub _DeleteLink {
$direction='Base';
}
else {
- $RT::Logger->error("Base or Target must be specified");
+ $RT::Logger->error("Base or Target must be specified\n");
return ( 0, $self->loc('Either base or target must be specified') );
}
my $link = new RT::Link( $self->CurrentUser );
- $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} );
+ $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} . "\n" );
$link->LoadByParams( Base=> $args{'Base'}, Type=> $args{'Type'}, Target=> $args{'Target'} );
#it's a real link.
-
if ( $link->id ) {
- my $basetext = $self->FormatLink(Object => $link->BaseObj,
- FallBack => $args{Base});
- my $targettext = $self->FormatLink(Object => $link->TargetObj,
- FallBack => $args{Target});
- my $typetext = $self->FormatType(Type => $args{Type});
+
my $linkid = $link->id;
$link->Delete();
- my $TransString = "$basetext no longer $typetext $targettext.";
- return ( 1, $TransString);
+
+ my $TransString = "Record $args{'Base'} no longer $args{Type} record $args{'Target'}.";
+ return ( 1, $self->loc("Link deleted ([_1])", $TransString));
}
#if it's not a link we can find
else {
- $RT::Logger->debug("Couldn't find that link");
+ $RT::Logger->debug("Couldn't find that link\n");
return ( 0, $self->loc("Link not found") );
}
}
@@ -1481,7 +1521,7 @@ sub _NewTransaction {
if ( defined $args{'TimeTaken'} and $self->can('_UpdateTimeTaken')) {
$self->_UpdateTimeTaken( $args{'TimeTaken'} );
}
- if ( RT->Config->Get('UseTransactionBatch') and $transaction ) {
+ if ( $RT::UseTransactionBatch and $transaction ) {
push @{$self->{_TransactionBatch}}, $trans if $args{'CommitScrips'};
}
return ( $transaction, $msg, $trans );
@@ -1524,13 +1564,11 @@ sub Transactions {
sub CustomFields {
my $self = shift;
my $cfs = RT::CustomFields->new( $self->CurrentUser );
-
- $cfs->SetContextObject( $self );
+
# XXX handle multiple types properly
$cfs->LimitToLookupType( $self->CustomFieldLookupType );
$cfs->LimitToGlobalOrObjectId(
- $self->_LookupId( $self->CustomFieldLookupType )
- );
+ $self->_LookupId( $self->CustomFieldLookupType ) );
return $cfs;
}
@@ -1563,18 +1601,27 @@ sub CustomFieldLookupType {
return ref($self);
}
+#TODO Deprecated API. Destroy in 3.6
+sub _LookupTypes {
+ my $self = shift;
+ $RT::Logger->warning("_LookupTypes call is deprecated at (". join(":",caller)."). Replace with CustomFieldLookupType");
+
+ return($self->CustomFieldLookupType);
+
+}
+
# {{{ AddCustomFieldValue
=head2 AddCustomFieldValue { Field => FIELD, Value => VALUE }
-VALUE should be a string. FIELD can be any identifier of a CustomField
-supported by L</LoadCustomFieldByIdentifier> method.
+VALUE should be a string.
+FIELD can be a CustomField object OR a CustomField ID.
-Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field,
-deletes the old value.
+
+Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field,
+deletes the old value.
If VALUE is not a valid value for the custom field, returns
-(0, 'Error message' ) otherwise, returns ($id, 'Success Message') where
-$id is ID of created L<ObjectCustomFieldValue> object.
+(0, 'Error message' ) otherwise, returns (1, 'Success Message')
=cut
@@ -1588,13 +1635,12 @@ sub _AddCustomFieldValue {
my %args = (
Field => undef,
Value => undef,
- LargeContent => undef,
- ContentType => undef,
RecordTransaction => 1,
@_
);
my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+
unless ( $cf->Id ) {
return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
}
@@ -1610,11 +1656,8 @@ sub _AddCustomFieldValue {
)
);
}
-
- # empty string is not correct value of any CF, so undef it
- foreach ( qw(Value LargeContent) ) {
- $args{ $_ } = undef if defined $args{ $_ } && !length $args{ $_ };
- }
+ # Load up a ObjectCustomFieldValues object for this custom field and this ticket
+ my $values = $cf->ValuesForObject($self);
unless ( $cf->ValidateValue( $args{'Value'} ) ) {
return ( 0, $self->loc("Invalid value for custom field") );
@@ -1622,14 +1665,11 @@ sub _AddCustomFieldValue {
# If the custom field only accepts a certain # of values, delete the existing
# value and record a "changed from foo to bar" transaction
- unless ( $cf->UnlimitedValues ) {
-
- # Load up a ObjectCustomFieldValues object for this custom field and this ticket
- my $values = $cf->ValuesForObject($self);
+ unless ( $cf->UnlimitedValues) {
- # We need to whack any old values here. In most cases, the custom field should
- # only have one value to delete. In the pathalogical case, this custom field
- # used to be a multiple and we have many values to whack....
+ # We need to whack any old values here. In most cases, the custom field should
+ # only have one value to delete. In the pathalogical case, this custom field
+ # used to be a multiple and we have many values to whack....
my $cf_values = $values->Count;
if ( $cf_values > $cf->MaxValues ) {
@@ -1658,27 +1698,8 @@ sub _AddCustomFieldValue {
my ( $old_value, $old_content );
if ( $old_value = $values->First ) {
- $old_content = $old_value->Content;
- $old_content = undef if defined $old_content && !length $old_content;
-
- my $is_the_same = 1;
- if ( defined $args{'Value'} ) {
- $is_the_same = 0 unless defined $old_content
- && lc $old_content eq lc $args{'Value'};
- } else {
- $is_the_same = 0 if defined $old_content;
- }
- if ( $is_the_same ) {
- my $old_content = $old_value->LargeContent;
- if ( defined $args{'LargeContent'} ) {
- $is_the_same = 0 unless defined $old_content
- && $old_content eq $args{'LargeContent'};
- } else {
- $is_the_same = 0 if defined $old_content;
- }
- }
-
- return $old_value->id if $is_the_same;
+ $old_content = $old_value->Content();
+ return (1) if( $old_content eq $args{'Value'} && $old_value->LargeContent eq $args{'LargeContent'});;
}
my ( $new_value_id, $value_msg ) = $cf->AddValueForObject(
@@ -1688,17 +1709,19 @@ sub _AddCustomFieldValue {
ContentType => $args{'ContentType'},
);
- unless ( $new_value_id ) {
- return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg ) );
+ unless ($new_value_id) {
+ return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) );
}
my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
- $new_value->Load( $new_value_id );
+ $new_value->Load($new_value_id);
# now that adding the new value was successful, delete the old one
- if ( $old_value ) {
+ if ($old_value) {
my ( $val, $msg ) = $old_value->Delete();
- return ( 0, $msg ) unless $val;
+ unless ($val) {
+ return ( 0, $msg );
+ }
}
if ( $args{'RecordTransaction'} ) {
@@ -1711,45 +1734,47 @@ sub _AddCustomFieldValue {
);
}
- my $new_content = $new_value->Content;
- unless ( defined $old_content && length $old_content ) {
- return ( $new_value_id, $self->loc( "[_1] [_2] added", $cf->Name, $new_content ));
+ if ( $old_value eq '' ) {
+ return ( 1, $self->loc( "[_1] [_2] added", $cf->Name, $new_value->Content ));
}
- elsif ( !defined $new_content || !length $new_content ) {
- return ( $new_value_id,
- $self->loc( "[_1] [_2] deleted", $cf->Name, $old_content ) );
+ elsif ( $new_value->Content eq '' ) {
+ return ( 1,
+ $self->loc( "[_1] [_2] deleted", $cf->Name, $old_value->Content ) );
}
else {
- return ( $new_value_id, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_content));
+ return ( 1, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_value->Content));
}
}
# otherwise, just add a new value and record "new value added"
else {
- my ($new_value_id, $msg) = $cf->AddValueForObject(
+ my ($new_value_id, $value_msg) = $cf->AddValueForObject(
Object => $self,
Content => $args{'Value'},
LargeContent => $args{'LargeContent'},
ContentType => $args{'ContentType'},
);
- unless ( $new_value_id ) {
- return ( 0, $self->loc( "Could not add new custom field value: [_1]", $msg ) );
+ unless ($new_value_id) {
+ return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) );
}
if ( $args{'RecordTransaction'} ) {
- my ( $tid, $msg ) = $self->_NewTransaction(
+ my ( $TransactionId, $Msg, $TransactionObj ) =
+ $self->_NewTransaction(
Type => 'CustomField',
Field => $cf->Id,
NewReference => $new_value_id,
ReferenceType => 'RT::ObjectCustomFieldValue',
- );
- unless ( $tid ) {
- return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $msg ) );
+ );
+ unless ($TransactionId) {
+ return ( 0,
+ $self->loc( "Couldn't create a transaction: [_1]", $Msg ) );
}
}
- return ( $new_value_id, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name ) );
+ return ( 1, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name));
}
+
}
# }}}
@@ -1777,10 +1802,10 @@ sub DeleteCustomFieldValue {
);
my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+
unless ( $cf->Id ) {
return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
}
-
my ( $val, $msg ) = $cf->DeleteValueForObject(
Object => $self,
Id => $args{'ValueId'},
@@ -1789,7 +1814,6 @@ sub DeleteCustomFieldValue {
unless ($val) {
return ( 0, $msg );
}
-
my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction(
Type => 'CustomField',
Field => $cf->Id,
@@ -1823,34 +1847,15 @@ Takes a field id or name
sub FirstCustomFieldValue {
my $self = shift;
my $field = shift;
+ my $values = $self->CustomFieldValues($field);
+ if ($values->First) {
+ return $values->First->Content;
+ } else {
+ return undef;
+ }
- my $values = $self->CustomFieldValues( $field );
- return undef unless my $first = $values->First;
- return $first->Content;
}
-=head2 CustomFieldValuesAsString FIELD
-
-Return the content of the CustomField FIELD for this ticket.
-If this is a multi-value custom field, values will be joined with newlines.
-
-Takes a field id or name as the first argument
-
-Takes an optional Separator => "," second and third argument
-if you want to join the values using something other than a newline
-
-=cut
-
-sub CustomFieldValuesAsString {
- my $self = shift;
- my $field = shift;
- my %args = @_;
- my $separator = $args{Separator} || "\n";
-
- my $values = $self->CustomFieldValues( $field );
- return join ($separator, grep { defined $_ }
- map { $_->Content } @{$values->ItemsArrayRef});
-}
# {{{ CustomFieldValues
@@ -1868,12 +1873,11 @@ sub CustomFieldValues {
my $self = shift;
my $field = shift;
- if ( $field ) {
- my $cf = $self->LoadCustomFieldByIdentifier( $field );
+ if ($field) {
+ my $cf = $self->LoadCustomFieldByIdentifier($field);
- # we were asked to search on a custom field we couldn't find
+ # we were asked to search on a custom field we couldn't fine
unless ( $cf->id ) {
- $RT::Logger->warning("Couldn't load custom field by '$field' identifier");
return RT::ObjectCustomFieldValues->new( $self->CurrentUser );
}
return ( $cf->ValuesForObject($self) );
@@ -1881,11 +1885,12 @@ sub CustomFieldValues {
# we're not limiting to a specific custom field;
my $ocfs = RT::ObjectCustomFieldValues->new( $self->CurrentUser );
- $ocfs->LimitToObject( $self );
+ $ocfs->LimitToObject($self);
return $ocfs;
+
}
-=head2 LoadCustomFieldByIdentifier IDENTIFER
+=head2 CustomField IDENTIFER
Find the custom field has id or name IDENTIFIER for this object.
@@ -1897,32 +1902,35 @@ sub LoadCustomFieldByIdentifier {
my $self = shift;
my $field = shift;
- my $cf;
+ my $cf = RT::CustomField->new($self->CurrentUser);
+
if ( UNIVERSAL::isa( $field, "RT::CustomField" ) ) {
- $cf = RT::CustomField->new($self->CurrentUser);
- $cf->SetContextObject( $self );
$cf->LoadById( $field->id );
}
elsif ($field =~ /^\d+$/) {
$cf = RT::CustomField->new($self->CurrentUser);
- $cf->SetContextObject( $self );
- $cf->LoadById($field);
+ $cf->Load($field);
} else {
my $cfs = $self->CustomFields($self->CurrentUser);
- $cfs->SetContextObject( $self );
$cfs->Limit(FIELD => 'Name', VALUE => $field, CASESENSITIVE => 0);
$cf = $cfs->First || RT::CustomField->new($self->CurrentUser);
}
return $cf;
}
-sub ACLEquivalenceObjects { }
-sub BasicColumns { }
+# }}}
+
+# }}}
+
+# }}}
+
+sub BasicColumns {
+}
sub WikiBase {
- return RT->Config->Get('WebPath'). "/index.html?q=";
+ return $RT::WebPath. "/index.html?q=";
}
eval "require RT::Record_Vendor";
diff --git a/rt/lib/RT/Scrip.pm b/rt/lib/RT/Scrip.pm
index ecc47e923..a69dde04e 100755
--- a/rt/lib/RT/Scrip.pm
+++ b/rt/lib/RT/Scrip.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -90,7 +65,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -145,7 +120,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -154,14 +129,14 @@ Returns the current value of id.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -172,14 +147,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ScripCondition
+=item ScripCondition
Returns the current value of ScripCondition.
(In the database, ScripCondition is stored as int(11).)
-=head2 SetScripCondition VALUE
+=item SetScripCondition VALUE
Set ScripCondition to VALUE.
@@ -190,7 +165,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ScripConditionObj
+=item ScripConditionObj
Returns the ScripCondition Object which has the id returned by ScripCondition
@@ -204,14 +179,14 @@ sub ScripConditionObj {
return($ScripCondition);
}
-=head2 ScripAction
+=item ScripAction
Returns the current value of ScripAction.
(In the database, ScripAction is stored as int(11).)
-=head2 SetScripAction VALUE
+=item SetScripAction VALUE
Set ScripAction to VALUE.
@@ -222,7 +197,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ScripActionObj
+=item ScripActionObj
Returns the ScripAction Object which has the id returned by ScripAction
@@ -236,14 +211,14 @@ sub ScripActionObj {
return($ScripAction);
}
-=head2 ConditionRules
+=item ConditionRules
Returns the current value of ConditionRules.
(In the database, ConditionRules is stored as text.)
-=head2 SetConditionRules VALUE
+=item SetConditionRules VALUE
Set ConditionRules to VALUE.
@@ -254,14 +229,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ActionRules
+=item ActionRules
Returns the current value of ActionRules.
(In the database, ActionRules is stored as text.)
-=head2 SetActionRules VALUE
+=item SetActionRules VALUE
Set ActionRules to VALUE.
@@ -272,14 +247,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CustomIsApplicableCode
+=item CustomIsApplicableCode
Returns the current value of CustomIsApplicableCode.
(In the database, CustomIsApplicableCode is stored as text.)
-=head2 SetCustomIsApplicableCode VALUE
+=item SetCustomIsApplicableCode VALUE
Set CustomIsApplicableCode to VALUE.
@@ -290,14 +265,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CustomPrepareCode
+=item CustomPrepareCode
Returns the current value of CustomPrepareCode.
(In the database, CustomPrepareCode is stored as text.)
-=head2 SetCustomPrepareCode VALUE
+=item SetCustomPrepareCode VALUE
Set CustomPrepareCode to VALUE.
@@ -308,14 +283,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CustomCommitCode
+=item CustomCommitCode
Returns the current value of CustomCommitCode.
(In the database, CustomCommitCode is stored as text.)
-=head2 SetCustomCommitCode VALUE
+=item SetCustomCommitCode VALUE
Set CustomCommitCode to VALUE.
@@ -326,14 +301,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Stage
+=item Stage
Returns the current value of Stage.
(In the database, Stage is stored as varchar(32).)
-=head2 SetStage VALUE
+=item SetStage VALUE
Set Stage to VALUE.
@@ -344,14 +319,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Queue
+=item Queue
Returns the current value of Queue.
(In the database, Queue is stored as int(11).)
-=head2 SetQueue VALUE
+=item SetQueue VALUE
Set Queue to VALUE.
@@ -362,7 +337,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 QueueObj
+=item QueueObj
Returns the Queue Object which has the id returned by Queue
@@ -376,14 +351,14 @@ sub QueueObj {
return($Queue);
}
-=head2 Template
+=item Template
Returns the current value of Template.
(In the database, Template is stored as int(11).)
-=head2 SetTemplate VALUE
+=item SetTemplate VALUE
Set Template to VALUE.
@@ -394,7 +369,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TemplateObj
+=item TemplateObj
Returns the Template Object which has the id returned by Template
@@ -408,7 +383,7 @@ sub TemplateObj {
return($Template);
}
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -417,7 +392,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -426,7 +401,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -435,7 +410,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -445,41 +420,41 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ScripCondition =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
ScripAction =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
ConditionRules =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
ActionRules =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
CustomIsApplicableCode =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
CustomPrepareCode =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
CustomCommitCode =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
Stage =>
- {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''},
+ {read => 1, write => 1, type => 'varchar(32)', default => ''},
Queue =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Template =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -511,7 +486,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/ScripAction.pm b/rt/lib/RT/ScripAction.pm
index 87a2613f2..26824df5d 100755
--- a/rt/lib/RT/ScripAction.pm
+++ b/rt/lib/RT/ScripAction.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -120,7 +95,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -129,14 +104,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -147,14 +122,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -165,14 +140,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExecModule
+=item ExecModule
Returns the current value of ExecModule.
(In the database, ExecModule is stored as varchar(60).)
-=head2 SetExecModule VALUE
+=item SetExecModule VALUE
Set ExecModule to VALUE.
@@ -183,14 +158,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Argument
+=item Argument
Returns the current value of Argument.
(In the database, Argument is stored as varchar(255).)
-=head2 SetArgument VALUE
+=item SetArgument VALUE
Set Argument to VALUE.
@@ -201,7 +176,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -210,7 +185,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -219,7 +194,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -228,7 +203,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -238,27 +213,27 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ExecModule =>
- {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ {read => 1, write => 1, type => 'varchar(60)', default => ''},
Argument =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -290,7 +265,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/ScripActions.pm b/rt/lib/RT/ScripActions.pm
index 1f6fb9815..614ff374f 100755
--- a/rt/lib/RT/ScripActions.pm
+++ b/rt/lib/RT/ScripActions.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::ScripAction item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/ScripCondition.pm b/rt/lib/RT/ScripCondition.pm
index 072a24ae5..fe0aa2d5a 100755
--- a/rt/lib/RT/ScripCondition.pm
+++ b/rt/lib/RT/ScripCondition.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -123,7 +98,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -132,14 +107,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -150,14 +125,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -168,14 +143,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExecModule
+=item ExecModule
Returns the current value of ExecModule.
(In the database, ExecModule is stored as varchar(60).)
-=head2 SetExecModule VALUE
+=item SetExecModule VALUE
Set ExecModule to VALUE.
@@ -186,14 +161,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Argument
+=item Argument
Returns the current value of Argument.
(In the database, Argument is stored as varchar(255).)
-=head2 SetArgument VALUE
+=item SetArgument VALUE
Set Argument to VALUE.
@@ -204,14 +179,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ApplicableTransTypes
+=item ApplicableTransTypes
Returns the current value of ApplicableTransTypes.
(In the database, ApplicableTransTypes is stored as varchar(60).)
-=head2 SetApplicableTransTypes VALUE
+=item SetApplicableTransTypes VALUE
Set ApplicableTransTypes to VALUE.
@@ -222,7 +197,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -231,7 +206,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -240,7 +215,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -249,7 +224,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -259,29 +234,29 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ExecModule =>
- {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ {read => 1, write => 1, type => 'varchar(60)', default => ''},
Argument =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ApplicableTransTypes =>
- {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ {read => 1, write => 1, type => 'varchar(60)', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -313,7 +288,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/ScripConditions.pm b/rt/lib/RT/ScripConditions.pm
index 43ea86d51..34f788d9c 100755
--- a/rt/lib/RT/ScripConditions.pm
+++ b/rt/lib/RT/ScripConditions.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::ScripCondition item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Scrips.pm b/rt/lib/RT/Scrips.pm
index 02d1019a3..a39443136 100755
--- a/rt/lib/RT/Scrips.pm
+++ b/rt/lib/RT/Scrips.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Scrip item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm
index f6b3571b7..fd0842419 100644
--- a/rt/lib/RT/SearchBuilder.pm
+++ b/rt/lib/RT/SearchBuilder.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::SearchBuilder - a baseclass for RT collection objects
@@ -58,6 +57,11 @@
=head1 METHODS
+=begin testing
+
+ok (require RT::SearchBuilder);
+
+=end testing
=cut
@@ -65,13 +69,13 @@
package RT::SearchBuilder;
use RT::Base;
-use DBIx::SearchBuilder "1.40";
+use DBIx::SearchBuilder "1.50";
use strict;
-use warnings;
-
-use base qw(DBIx::SearchBuilder RT::Base);
+use vars qw(@ISA);
+@ISA = qw(DBIx::SearchBuilder RT::Base);
+# {{{ sub _Init
sub _Init {
my $self = shift;
@@ -84,10 +88,13 @@ sub _Init {
}
$self->SUPER::_Init( 'Handle' => $RT::Handle);
}
+# }}}
+
+# {{{ sub LimitToEnabled
=head2 LimitToEnabled
-Only find items that haven't been disabled
+Only find items that haven\'t been disabled
=cut
@@ -98,6 +105,9 @@ sub LimitToEnabled {
VALUE => '0',
OPERATOR => '=' );
}
+# }}}
+
+# {{{ sub LimitToDisabled
=head2 LimitToDeleted
@@ -114,6 +124,9 @@ sub LimitToDeleted {
VALUE => '1'
);
}
+# }}}
+
+# {{{ sub LimitAttribute
=head2 LimitAttribute PARAMHASH
@@ -127,17 +140,17 @@ If NULL is set, also select rows without the named Attribute.
=cut
-my %Negate = (
- '=' => '!=',
- '!=' => '=',
- '>' => '<=',
- '<' => '>=',
- '>=' => '<',
- '<=' => '>',
- 'LIKE' => 'NOT LIKE',
- 'NOT LIKE' => 'LIKE',
- 'IS' => 'IS NOT',
- 'IS NOT' => 'IS',
+my %Negate = qw(
+ = !=
+ != =
+ > <=
+ < >=
+ >= <
+ <= >
+ LIKE NOT LIKE
+ NOT LIKE LIKE
+ IS IS NOT
+ IS NOT IS
);
sub LimitAttribute {
@@ -204,6 +217,9 @@ sub LimitAttribute {
VALUE => 'NULL',
) if $args{NULL};
}
+# }}}
+
+# {{{ sub LimitCustomField
=head2 LimitCustomField
@@ -262,6 +278,8 @@ sub LimitCustomField {
);
}
+# {{{ sub FindAllRows
+
=head2 FindAllRows
Find all matching rows, regardless of whether they are disabled or not
@@ -272,6 +290,8 @@ sub FindAllRows {
shift->{'find_disabled_rows'} = 1;
}
+# {{{ sub Limit
+
=head2 Limit PARAMHASH
This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus
@@ -288,6 +308,10 @@ sub Limit {
return $self->SUPER::Limit(%args);
}
+# }}}
+
+# {{{ sub ItemsOrderBy
+
=head2 ItemsOrderBy
If it has a SortOrder attribute, sort the array by SortOrder.
@@ -311,21 +335,55 @@ sub ItemsOrderBy {
return $items;
}
+# }}}
+
+# {{{ sub ItemsArrayRef
+
=head2 ItemsArrayRef
Return this object's ItemsArray, in the order that ItemsOrderBy sorts
it.
+=begin testing
+
+use_ok(RT::Queues);
+ok(my $queues = RT::Queues->new($RT::SystemUser), 'Created a queues object');
+ok( $queues->UnLimit(),'Unlimited the result set of the queues object');
+my $items = $queues->ItemsArrayRef();
+my @items = @{$items};
+
+ok($queues->NewItem->_Accessible('Name','read'));
+my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items;
+ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted));
+
+ok (@items, "We have an array of queues, raw". join(',',map {$_->Name} @items));
+my @sorted_ids = map {$_->id } @sorted;
+my @items_ids = map {$_->id } @items;
+
+is ($#sorted, $#items);
+is ($sorted[0]->Name, $items[0]->Name);
+is ($sorted[-1]->Name, $items[-1]->Name);
+is_deeply(\@items_ids, \@sorted_ids, "ItemsArrayRef sorts alphabetically by name");;
+
+
+=end testing
+
=cut
sub ItemsArrayRef {
my $self = shift;
+ my @items;
+
return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef());
}
+# }}}
+
eval "require RT::SearchBuilder_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Vendor.pm});
eval "require RT::SearchBuilder_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Local.pm});
1;
+
+
diff --git a/rt/lib/RT/Template.pm b/rt/lib/RT/Template.pm
index f27a0c27e..f73ea3ed6 100755
--- a/rt/lib/RT/Template.pm
+++ b/rt/lib/RT/Template.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -87,7 +62,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -130,7 +105,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -139,14 +114,14 @@ Returns the current value of id.
=cut
-=head2 Queue
+=item Queue
Returns the current value of Queue.
(In the database, Queue is stored as int(11).)
-=head2 SetQueue VALUE
+=item SetQueue VALUE
Set Queue to VALUE.
@@ -157,7 +132,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 QueueObj
+=item QueueObj
Returns the Queue Object which has the id returned by Queue
@@ -171,14 +146,14 @@ sub QueueObj {
return($Queue);
}
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -189,14 +164,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -207,14 +182,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(16).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -225,14 +200,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Language
+=item Language
Returns the current value of Language.
(In the database, Language is stored as varchar(16).)
-=head2 SetLanguage VALUE
+=item SetLanguage VALUE
Set Language to VALUE.
@@ -243,14 +218,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TranslationOf
+=item TranslationOf
Returns the current value of TranslationOf.
(In the database, TranslationOf is stored as int(11).)
-=head2 SetTranslationOf VALUE
+=item SetTranslationOf VALUE
Set TranslationOf to VALUE.
@@ -261,14 +236,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Content
+=item Content
Returns the current value of Content.
(In the database, Content is stored as blob.)
-=head2 SetContent VALUE
+=item SetContent VALUE
Set Content to VALUE.
@@ -279,7 +254,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -288,7 +263,7 @@ Returns the current value of LastUpdated.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -297,7 +272,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -306,7 +281,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -316,33 +291,33 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Queue =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
Language =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
TranslationOf =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Content =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -374,7 +349,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Templates.pm b/rt/lib/RT/Templates.pm
index 898870249..37db84086 100755
--- a/rt/lib/RT/Templates.pm
+++ b/rt/lib/RT/Templates.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Template item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Ticket.pm b/rt/lib/RT/Ticket.pm
index 4dcd189a8..2f075a20c 100755
--- a/rt/lib/RT/Ticket.pm
+++ b/rt/lib/RT/Ticket.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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:
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# (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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -87,7 +62,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -141,7 +116,7 @@ sub Create {
Resolved => '',
Disabled => '0',
- @_);
+ @_);
$self->SUPER::Create(
EffectiveId => $args{'EffectiveId'},
Queue => $args{'Queue'},
@@ -169,7 +144,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -178,14 +153,14 @@ Returns the current value of id.
=cut
-=head2 EffectiveId
+=item EffectiveId
Returns the current value of EffectiveId.
(In the database, EffectiveId is stored as int(11).)
-=head2 SetEffectiveId VALUE
+=item SetEffectiveId VALUE
Set EffectiveId to VALUE.
@@ -196,14 +171,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Queue
+=item Queue
Returns the current value of Queue.
(In the database, Queue is stored as int(11).)
-=head2 SetQueue VALUE
+=item SetQueue VALUE
Set Queue to VALUE.
@@ -214,7 +189,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 QueueObj
+=item QueueObj
Returns the Queue Object which has the id returned by Queue
@@ -222,22 +197,20 @@ Returns the Queue Object which has the id returned by Queue
=cut
sub QueueObj {
- my $self = shift;
- my $Queue = RT::Queue->new($self->CurrentUser);
- $Queue->Load($self->__Value('Queue'));
- return($Queue);
+ my $self = shift;
+ my $Queue = RT::Queue->new($self->CurrentUser);
+ $Queue->Load($self->__Value('Queue'));
+ return($Queue);
}
-
-
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(16).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -248,14 +221,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 IssueStatement
+=item IssueStatement
Returns the current value of IssueStatement.
(In the database, IssueStatement is stored as int(11).)
-=head2 SetIssueStatement VALUE
+=item SetIssueStatement VALUE
Set IssueStatement to VALUE.
@@ -266,14 +239,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Resolution
+=item Resolution
Returns the current value of Resolution.
(In the database, Resolution is stored as int(11).)
-=head2 SetResolution VALUE
+=item SetResolution VALUE
Set Resolution to VALUE.
@@ -284,14 +257,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Owner
+=item Owner
Returns the current value of Owner.
(In the database, Owner is stored as int(11).)
-=head2 SetOwner VALUE
+=item SetOwner VALUE
Set Owner to VALUE.
@@ -302,14 +275,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Subject
+=item Subject
Returns the current value of Subject.
(In the database, Subject is stored as varchar(200).)
-=head2 SetSubject VALUE
+=item SetSubject VALUE
Set Subject to VALUE.
@@ -320,14 +293,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 InitialPriority
+=item InitialPriority
Returns the current value of InitialPriority.
(In the database, InitialPriority is stored as int(11).)
-=head2 SetInitialPriority VALUE
+=item SetInitialPriority VALUE
Set InitialPriority to VALUE.
@@ -338,14 +311,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 FinalPriority
+=item FinalPriority
Returns the current value of FinalPriority.
(In the database, FinalPriority is stored as int(11).)
-=head2 SetFinalPriority VALUE
+=item SetFinalPriority VALUE
Set FinalPriority to VALUE.
@@ -356,14 +329,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Priority
+=item Priority
Returns the current value of Priority.
(In the database, Priority is stored as int(11).)
-=head2 SetPriority VALUE
+=item SetPriority VALUE
Set Priority to VALUE.
@@ -374,14 +347,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TimeEstimated
+=item TimeEstimated
Returns the current value of TimeEstimated.
(In the database, TimeEstimated is stored as int(11).)
-=head2 SetTimeEstimated VALUE
+=item SetTimeEstimated VALUE
Set TimeEstimated to VALUE.
@@ -392,14 +365,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TimeWorked
+=item TimeWorked
Returns the current value of TimeWorked.
(In the database, TimeWorked is stored as int(11).)
-=head2 SetTimeWorked VALUE
+=item SetTimeWorked VALUE
Set TimeWorked to VALUE.
@@ -410,14 +383,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Status
+=item Status
Returns the current value of Status.
(In the database, Status is stored as varchar(10).)
-=head2 SetStatus VALUE
+=item SetStatus VALUE
Set Status to VALUE.
@@ -428,14 +401,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TimeLeft
+=item TimeLeft
Returns the current value of TimeLeft.
(In the database, TimeLeft is stored as int(11).)
-=head2 SetTimeLeft VALUE
+=item SetTimeLeft VALUE
Set TimeLeft to VALUE.
@@ -446,14 +419,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Told
+=item Told
Returns the current value of Told.
(In the database, Told is stored as datetime.)
-=head2 SetTold VALUE
+=item SetTold VALUE
Set Told to VALUE.
@@ -464,14 +437,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Starts
+=item Starts
Returns the current value of Starts.
(In the database, Starts is stored as datetime.)
-=head2 SetStarts VALUE
+=item SetStarts VALUE
Set Starts to VALUE.
@@ -482,14 +455,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Started
+=item Started
Returns the current value of Started.
(In the database, Started is stored as datetime.)
-=head2 SetStarted VALUE
+=item SetStarted VALUE
Set Started to VALUE.
@@ -500,14 +473,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Due
+=item Due
Returns the current value of Due.
(In the database, Due is stored as datetime.)
-=head2 SetDue VALUE
+=item SetDue VALUE
Set Due to VALUE.
@@ -518,14 +491,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Resolved
+=item Resolved
Returns the current value of Resolved.
(In the database, Resolved is stored as datetime.)
-=head2 SetResolved VALUE
+=item SetResolved VALUE
Set Resolved to VALUE.
@@ -536,7 +509,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -545,7 +518,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -554,7 +527,7 @@ Returns the current value of LastUpdated.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -563,7 +536,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -572,14 +545,14 @@ Returns the current value of Created.
=cut
-=head2 Disabled
+=item Disabled
Returns the current value of Disabled.
(In the database, Disabled is stored as smallint(6).)
-=head2 SetDisabled VALUE
+=item SetDisabled VALUE
Set Disabled to VALUE.
@@ -591,59 +564,59 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
EffectiveId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Queue =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
IssueStatement =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Resolution =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Owner =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Subject =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => '[no subject]'},
+ {read => 1, write => 1, type => 'varchar(200)', default => '[no subject]'},
InitialPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
FinalPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Priority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
TimeEstimated =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
TimeWorked =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Status =>
- {read => 1, write => 1, sql_type => 12, length => 10, is_blob => 0, is_numeric => 0, type => 'varchar(10)', default => ''},
+ {read => 1, write => 1, type => 'varchar(10)', default => ''},
TimeLeft =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Told =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Starts =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Started =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Due =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Resolved =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Disabled =>
- {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ {read => 1, write => 1, type => 'smallint(6)', default => '0'},
}
};
@@ -675,7 +648,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/TicketCustomFieldValue.pm b/rt/lib/RT/TicketCustomFieldValue.pm
deleted file mode 100644
index 717647266..000000000
--- a/rt/lib/RT/TicketCustomFieldValue.pm
+++ /dev/null
@@ -1,308 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# 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
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
-#
-# !! DO NOT EDIT THIS FILE !!
-#
-
-use strict;
-
-
-=head1 NAME
-
-RT::TicketCustomFieldValue
-
-
-=head1 SYNOPSIS
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=cut
-
-package RT::TicketCustomFieldValue;
-use RT::Record;
-use RT::CustomField;
-use RT::Ticket;
-
-
-use vars qw( @ISA );
-@ISA= qw( RT::Record );
-
-sub _Init {
- my $self = shift;
-
- $self->Table('TicketCustomFieldValues');
- $self->SUPER::_Init(@_);
-}
-
-
-
-
-
-=head2 Create PARAMHASH
-
-Create takes a hash of values and creates a row in the database:
-
- int(11) 'Ticket'.
- int(11) 'CustomField'.
- varchar(255) 'Content'.
-
-=cut
-
-
-
-
-sub Create {
- my $self = shift;
- my %args = (
- Ticket => '0',
- CustomField => '0',
- Content => '',
-
- @_);
- $self->SUPER::Create(
- Ticket => $args{'Ticket'},
- CustomField => $args{'CustomField'},
- Content => $args{'Content'},
-);
-
-}
-
-
-
-=head2 id
-
-Returns the current value of id.
-(In the database, id is stored as int(11).)
-
-
-=cut
-
-
-=head2 Ticket
-
-Returns the current value of Ticket.
-(In the database, Ticket is stored as int(11).)
-
-
-
-=head2 SetTicket VALUE
-
-
-Set Ticket to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Ticket will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 TicketObj
-
-Returns the Ticket Object which has the id returned by Ticket
-
-
-=cut
-
-sub TicketObj {
- my $self = shift;
- my $Ticket = RT::Ticket->new($self->CurrentUser);
- $Ticket->Load($self->__Value('Ticket'));
- return($Ticket);
-}
-
-=head2 CustomField
-
-Returns the current value of CustomField.
-(In the database, CustomField is stored as int(11).)
-
-
-
-=head2 SetCustomField VALUE
-
-
-Set CustomField to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, CustomField will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 CustomFieldObj
-
-Returns the CustomField Object which has the id returned by CustomField
-
-
-=cut
-
-sub CustomFieldObj {
- my $self = shift;
- my $CustomField = RT::CustomField->new($self->CurrentUser);
- $CustomField->Load($self->__Value('CustomField'));
- return($CustomField);
-}
-
-=head2 Content
-
-Returns the current value of Content.
-(In the database, Content is stored as varchar(255).)
-
-
-
-=head2 SetContent VALUE
-
-
-Set Content to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Content will be stored as a varchar(255).)
-
-
-=cut
-
-
-=head2 Creator
-
-Returns the current value of Creator.
-(In the database, Creator is stored as int(11).)
-
-
-=cut
-
-
-=head2 Created
-
-Returns the current value of Created.
-(In the database, Created is stored as datetime.)
-
-
-=cut
-
-
-=head2 LastUpdatedBy
-
-Returns the current value of LastUpdatedBy.
-(In the database, LastUpdatedBy is stored as int(11).)
-
-
-=cut
-
-
-=head2 LastUpdated
-
-Returns the current value of LastUpdated.
-(In the database, LastUpdated is stored as datetime.)
-
-
-=cut
-
-
-
-sub _CoreAccessible {
- {
-
- id =>
- {read => 1, type => 'int(11)', default => ''},
- Ticket =>
- {read => 1, write => 1, type => 'int(11)', default => '0'},
- CustomField =>
- {read => 1, write => 1, type => 'int(11)', default => '0'},
- Content =>
- {read => 1, write => 1, type => 'varchar(255)', default => ''},
- Creator =>
- {read => 1, auto => 1, type => 'int(11)', default => '0'},
- Created =>
- {read => 1, auto => 1, type => 'datetime', default => ''},
- LastUpdatedBy =>
- {read => 1, auto => 1, type => 'int(11)', default => '0'},
- LastUpdated =>
- {read => 1, auto => 1, type => 'datetime', default => ''},
-
- }
-};
-
-
- eval "require RT::TicketCustomFieldValue_Overlay";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValue_Overlay.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValue_Vendor";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValue_Vendor.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValue_Local";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValue_Local.pm}) {
- die $@;
- };
-
-
-
-
-=head1 SEE ALSO
-
-This class allows "overlay" methods to be placed
-into the following files _Overlay is for a System overlay by the original author,
-_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
-
-These overlay files can contain new subs or subs to replace existing subs in this module.
-
-If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
-
- no warnings qw(redefine);
-
-so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
-
-RT::TicketCustomFieldValue_Overlay, RT::TicketCustomFieldValue_Vendor, RT::TicketCustomFieldValue_Local
-
-=cut
-
-
-1;
diff --git a/rt/lib/RT/TicketCustomFieldValue_Overlay.pm b/rt/lib/RT/TicketCustomFieldValue_Overlay.pm
deleted file mode 100644
index 270c5939a..000000000
--- a/rt/lib/RT/TicketCustomFieldValue_Overlay.pm
+++ /dev/null
@@ -1,74 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# 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;
-no warnings qw(redefine);
-
-
-
-=head2 LoadByTicketContentAndCustomField { Ticket => TICKET, CustomField => CUSTOMFIELD, Content => CONTENT }
-
-Loads a custom field value by Ticket, Content and which CustomField it's tied to
-
-=cut
-
-
-sub LoadByTicketContentAndCustomField {
- my $self = shift;
- my %args = ( Ticket => undef,
- CustomField => undef,
- Content => undef,
- @_
- );
-
-
- $self->LoadByCols( Content => $args{'Content'},
- CustomField => $args{'CustomField'},
- Ticket => $args{'Ticket'});
-
-
-}
-
-1;
diff --git a/rt/lib/RT/TicketCustomFieldValues.pm b/rt/lib/RT/TicketCustomFieldValues.pm
deleted file mode 100644
index 2174afef3..000000000
--- a/rt/lib/RT/TicketCustomFieldValues.pm
+++ /dev/null
@@ -1,137 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# 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
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
-#
-# !! DO NOT EDIT THIS FILE !!
-#
-
-use strict;
-
-
-=head1 NAME
-
- RT::TicketCustomFieldValues -- Class Description
-
-=head1 SYNOPSIS
-
- use RT::TicketCustomFieldValues
-
-=head1 DESCRIPTION
-
-
-=head1 METHODS
-
-=cut
-
-package RT::TicketCustomFieldValues;
-
-use RT::SearchBuilder;
-use RT::TicketCustomFieldValue;
-
-use vars qw( @ISA );
-@ISA= qw(RT::SearchBuilder);
-
-
-sub _Init {
- my $self = shift;
- $self->{'table'} = 'TicketCustomFieldValues';
- $self->{'primary_key'} = 'id';
-
-
- return ( $self->SUPER::_Init(@_) );
-}
-
-
-=head2 NewItem
-
-Returns an empty new RT::TicketCustomFieldValue item
-
-=cut
-
-sub NewItem {
- my $self = shift;
- return(RT::TicketCustomFieldValue->new($self->CurrentUser));
-}
-
- eval "require RT::TicketCustomFieldValues_Overlay";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValues_Overlay.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValues_Vendor";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValues_Vendor.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValues_Local";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValues_Local.pm}) {
- die $@;
- };
-
-
-
-
-=head1 SEE ALSO
-
-This class allows "overlay" methods to be placed
-into the following files _Overlay is for a System overlay by the original author,
-_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
-
-These overlay files can contain new subs or subs to replace existing subs in this module.
-
-If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
-
- no warnings qw(redefine);
-
-so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
-
-RT::TicketCustomFieldValues_Overlay, RT::TicketCustomFieldValues_Vendor, RT::TicketCustomFieldValues_Local
-
-=cut
-
-
-1;
diff --git a/rt/lib/RT/TicketCustomFieldValues_Overlay.pm b/rt/lib/RT/TicketCustomFieldValues_Overlay.pm
deleted file mode 100644
index 8cbaca574..000000000
--- a/rt/lib/RT/TicketCustomFieldValues_Overlay.pm
+++ /dev/null
@@ -1,108 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# 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;
-no warnings qw(redefine);
-
-# {{{ sub LimitToCustomField
-
-=head2 LimitToCustomField FIELD
-
-Limits the returned set to values for the custom field with Id FIELD
-
-=cut
-
-sub LimitToCustomField {
- my $self = shift;
- my $cf = shift;
- return ($self->Limit( FIELD => 'CustomField',
- VALUE => $cf,
- OPERATOR => '='));
-
-}
-
-# }}}
-
-# {{{ sub LimitToTicket
-
-=head2 LimitToTicket TICKETID
-
-Limits the returned set to values for the ticket with Id TICKETID
-
-=cut
-
-sub LimitToTicket {
- my $self = shift;
- my $ticket = shift;
- return ($self->Limit( FIELD => 'Ticket',
- VALUE => $ticket,
- OPERATOR => '='));
-
-}
-
-# }}}
-
-
-=sub HasEntry VALUE
-
-Returns true if this CustomFieldValues collection has an entry with content that eq VALUE
-
-=cut
-
-
-sub HasEntry {
- my $self = shift;
- my $value = shift;
-
- #TODO: this could cache and optimize a fair bit.
- foreach my $item (@{$self->ItemsArrayRef}) {
- return(1) if ($item->Content eq $value);
- }
- return undef;
-
-}
-
-1;
-
diff --git a/rt/lib/RT/Ticket_Overlay.pm b/rt/lib/RT/Ticket_Overlay.pm
index a8d32ef69..20f54625f 100644
--- a/rt/lib/RT/Ticket_Overlay.pm
+++ b/rt/lib/RT/Ticket_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
# {{{ Front Material
=head1 SYNOPSIS
@@ -61,6 +60,82 @@ This module lets you manipulate RT\'s ticket object.
=head1 METHODS
+=begin testing
+
+use_ok ( RT::Queue);
+ok(my $testqueue = RT::Queue->new($RT::SystemUser));
+ok($testqueue->Create( Name => 'ticket tests'));
+ok($testqueue->Id != 0);
+use_ok(RT::CustomField);
+ok(my $testcf = RT::CustomField->new($RT::SystemUser));
+my ($ret, $cmsg) = $testcf->Create( Name => 'selectmulti',
+ Queue => $testqueue->id,
+ Type => 'SelectMultiple');
+ok($ret,"Created the custom field - ".$cmsg);
+($ret,$cmsg) = $testcf->AddValue ( Name => 'Value1',
+ SortOrder => '1',
+ Description => 'A testing value');
+
+ok($ret, "Added a value - ".$cmsg);
+
+ok($testcf->AddValue ( Name => 'Value2',
+ SortOrder => '2',
+ Description => 'Another testing value'));
+ok($testcf->AddValue ( Name => 'Value3',
+ SortOrder => '3',
+ Description => 'Yet Another testing value'));
+
+ok($testcf->Values->Count == 3);
+
+use_ok(RT::Ticket);
+
+my $u = RT::User->new($RT::SystemUser);
+$u->Load("root");
+ok ($u->Id, "Found the root user");
+ok(my $t = RT::Ticket->new($RT::SystemUser));
+ok(my ($id, $msg) = $t->Create( Queue => $testqueue->Id,
+ Subject => 'Testing',
+ Owner => $u->Id
+ ));
+ok($id != 0);
+ok ($t->OwnerObj->Id == $u->Id, "Root is the ticket owner");
+ok(my ($cfv, $cfm) =$t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1'));
+ok($cfv != 0, "Custom field creation didn't return an error: $cfm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 1);
+ok($t->CustomFieldValues($testcf->Id)->First &&
+ $t->CustomFieldValues($testcf->Id)->First->Content eq 'Value1');;
+
+ok(my ($cfdv, $cfdm) = $t->DeleteCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1'));
+ok ($cfdv != 0, "Deleted a custom field value: $cfdm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 0);
+
+ok(my $t2 = RT::Ticket->new($RT::SystemUser));
+ok($t2->Load($id));
+is($t2->Subject, 'Testing');
+is($t2->QueueObj->Id, $testqueue->id);
+ok($t2->OwnerObj->Id == $u->Id);
+
+my $t3 = RT::Ticket->new($RT::SystemUser);
+my ($id3, $msg3) = $t3->Create( Queue => $testqueue->Id,
+ Subject => 'Testing',
+ Owner => $u->Id);
+my ($cfv1, $cfm1) = $t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1');
+ok($cfv1 != 0, "Adding a custom field to ticket 1 is successful: $cfm");
+my ($cfv2, $cfm2) = $t3->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value2');
+ok($cfv2 != 0, "Adding a custom field to ticket 2 is successful: $cfm");
+my ($cfv3, $cfm3) = $t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value3');
+ok($cfv3 != 0, "Adding a custom field to ticket 1 is successful: $cfm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 2,
+ "This ticket has 2 custom field values");
+ok($t3->CustomFieldValues($testcf->Id)->Count == 1,
+ "This ticket has 1 custom field value");
+
+=end testing
=cut
@@ -83,20 +158,32 @@ use RT::URI::fsck_com_rt;
use RT::URI;
use MIME::Entity;
+=begin testing
+
+
+ok(require RT::Ticket, "Loading the RT::Ticket library");
+
+=end testing
+
+=cut
+
+# }}}
# {{{ LINKTYPEMAP
# A helper table for links mapping to make it easier
# to build and parse links between tickets
-our %LINKTYPEMAP = (
+use vars '%LINKTYPEMAP';
+
+%LINKTYPEMAP = (
MemberOf => { Type => 'MemberOf',
Mode => 'Target', },
Parents => { Type => 'MemberOf',
- Mode => 'Target', },
+ Mode => 'Target', },
Members => { Type => 'MemberOf',
Mode => 'Base', },
Children => { Type => 'MemberOf',
- Mode => 'Base', },
+ Mode => 'Base', },
HasMember => { Type => 'MemberOf',
Mode => 'Base', },
RefersTo => { Type => 'RefersTo',
@@ -118,7 +205,9 @@ our %LINKTYPEMAP = (
# A helper table for links mapping to make it easier
# to build and parse links between tickets
-our %LINKDIRMAP = (
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
MemberOf => { Base => 'MemberOf',
Target => 'HasMember', },
RefersTo => { Base => 'RefersTo',
@@ -152,10 +241,9 @@ sub Load {
#TODO modify this routine to look at EffectiveId and do the recursive load
# thing. be careful to cache all the interim tickets we try so we don't loop forever.
- # FIXME: there is no TicketBaseURI option in config
- my $base_uri = RT->Config->Get('TicketBaseURI') || '';
+
#If it's a local URI, turn it into a ticket id
- if ( $base_uri && defined $id && $id =~ /^$base_uri(\d+)$/ ) {
+ if ( $RT::TicketBaseURI && $id =~ /^$RT::TicketBaseURI(\d+)$/ ) {
$id = $1;
}
@@ -165,18 +253,18 @@ sub Load {
}
#If we have an integer URI, load the ticket
- if ( defined $id && $id =~ /^\d+$/ ) {
+ if ( $id =~ /^\d+$/ ) {
my ($ticketid,$msg) = $self->LoadById($id);
unless ($self->Id) {
- $RT::Logger->debug("$self tried to load a bogus ticket: $id");
+ $RT::Logger->crit("$self tried to load a bogus ticket: $id\n");
return (undef);
}
}
#It's not a URI. It's not a numerical ticket ID. Punt!
else {
- $RT::Logger->debug("Tried to load a bogus ticket id: '$id'");
+ $RT::Logger->warning("Tried to load a bogus ticket id: '$id'");
return (undef);
}
@@ -193,6 +281,29 @@ sub Load {
# }}}
+# {{{ sub LoadByURI
+
+=head2 LoadByURI
+
+Given a local ticket URI, loads the specified ticket.
+
+=cut
+
+sub LoadByURI {
+ my $self = shift;
+ my $uri = shift;
+
+ if ( $uri =~ /^$RT::TicketBaseURI(\d+)$/ ) {
+ my $id = $1;
+ return ( $self->Load($id) );
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
# {{{ sub Create
=head2 Create (ARGS)
@@ -204,8 +315,6 @@ Arguments: ARGS is a hash of named parameters. Valid parameters are:
Requestor - A reference to a list of email addresses or RT user Names
Cc - A reference to a list of email addresses or Names
AdminCc - A reference to a list of email addresses or Names
- SquelchMailTo - A reference to a list of email addresses -
- who should this ticket not mail
Type -- The ticket\'s type. ignore this for now
Owner -- This ticket\'s owner. either an RT::User object or this user\'s id
Subject -- A string describing the subject of the ticket
@@ -235,6 +344,18 @@ C<Members> and C<Children> are aliases for C<HasMember>.
Returns: TICKETID, Transaction Object, Error Message
+=begin testing
+
+my $t = RT::Ticket->new($RT::SystemUser);
+
+ok( $t->Create(Queue => 'General', Due => '2002-05-21 00:00:00', ReferredToBy => 'http://www.cpan.org', RefersTo => 'http://fsck.com', Subject => 'This is a subject'), "Ticket Created");
+
+ok ( my $id = $t->Id, "Got ticket id");
+ok ($t->RefersTo->First->Target =~ /fsck.com/, "Got refers to");
+ok ($t->ReferredToBy->First->Base =~ /cpan.org/, "Got referredtoby");
+ok ($t->ResolvedObj->Unix == -1, "It hasn't been resolved - ". $t->ResolvedObj->Unix);
+
+=end testing
=cut
@@ -248,7 +369,6 @@ sub Create {
Requestor => undef,
Cc => undef,
AdminCc => undef,
- SquelchMailTo => undef,
Type => 'ticket',
Owner => undef,
Subject => '',
@@ -265,30 +385,30 @@ sub Create {
Resolved => undef,
MIMEObj => undef,
_RecordTransaction => 1,
- DryRun => 0,
@_
);
- my ($ErrStr, @non_fatal_errors);
+ my ( $ErrStr, $Owner, $resolved );
+ my (@non_fatal_errors);
- my $QueueObj = RT::Queue->new( $RT::SystemUser );
- if ( ref $args{'Queue'} eq 'RT::Queue' ) {
- $QueueObj->Load( $args{'Queue'}->Id );
- }
- elsif ( $args{'Queue'} ) {
+ my $QueueObj = RT::Queue->new($RT::SystemUser);
+
+ if ( ( defined( $args{'Queue'} ) ) && ( !ref( $args{'Queue'} ) ) ) {
$QueueObj->Load( $args{'Queue'} );
}
+ elsif ( ref( $args{'Queue'} ) eq 'RT::Queue' ) {
+ $QueueObj->Load( $args{'Queue'}->Id );
+ }
else {
- $RT::Logger->debug("'". ( $args{'Queue'} ||''). "' not a recognised queue object." );
+ $RT::Logger->debug( $args{'Queue'} . " not a recognised queue object." );
}
#Can't create a ticket without a queue.
- unless ( $QueueObj->Id ) {
+ unless ( defined($QueueObj) && $QueueObj->Id ) {
$RT::Logger->debug("$self No queue given for ticket creation.");
return ( 0, 0, $self->loc('Could not create ticket. Queue not set') );
}
-
#Now that we have a queue, Check the ACLS
unless (
$self->CurrentUser->HasRight(
@@ -307,21 +427,21 @@ sub Create {
}
#Since we have a queue, we can set queue defaults
-
#Initial Priority
+
# If there's no queue default initial priority and it's not set, set it to 0
- $args{'InitialPriority'} = $QueueObj->InitialPriority || 0
- unless defined $args{'InitialPriority'};
+ $args{'InitialPriority'} = ( $QueueObj->InitialPriority || 0 )
+ unless ( $args{'InitialPriority'} );
#Final priority
+
# If there's no queue default final priority and it's not set, set it to 0
- $args{'FinalPriority'} = $QueueObj->FinalPriority || 0
- unless defined $args{'FinalPriority'};
+ $args{'FinalPriority'} = ( $QueueObj->FinalPriority || 0 )
+ unless ( $args{'FinalPriority'} );
# Priority may have changed from InitialPriority, for the case
# where we're importing tickets (eg, from an older RT version.)
- $args{'Priority'} = $args{'InitialPriority'}
- unless defined $args{'Priority'};
+ my $priority = $args{'Priority'} || $args{'InitialPriority'};
# {{{ Dates
#TODO we should see what sort of due date we're getting, rather +
@@ -329,7 +449,8 @@ sub Create {
#Set the due date. if we didn't get fed one, use the queue default due in
my $Due = new RT::Date( $self->CurrentUser );
- if ( defined $args{'Due'} ) {
+
+ if ( $args{'Due'} ) {
$Due->Set( Format => 'ISO', Value => $args{'Due'} );
}
elsif ( my $due_in = $QueueObj->DefaultDueIn ) {
@@ -346,9 +467,6 @@ sub Create {
if ( defined $args{'Started'} ) {
$Started->Set( Format => 'ISO', Value => $args{'Started'} );
}
- elsif ( $args{'Status'} ne 'new' ) {
- $Started->SetToNow;
- }
my $Resolved = new RT::Date( $self->CurrentUser );
if ( defined $args{'Resolved'} ) {
@@ -356,10 +474,10 @@ sub Create {
}
#If the status is an inactive status, set the resolved date
- elsif ( $QueueObj->IsInactiveStatus( $args{'Status'} ) )
+ if ( $QueueObj->IsInactiveStatus( $args{'Status'} ) && !$args{'Resolved'} )
{
$RT::Logger->debug( "Got a ". $args{'Status'}
- ."(inactive) ticket with undefined resolved date. Setting to now."
+ ." ticket with undefined resolved date. Setting to now."
);
$Resolved->SetToNow;
}
@@ -376,42 +494,51 @@ sub Create {
# {{{ Deal with setting the owner
- my $Owner;
if ( ref( $args{'Owner'} ) eq 'RT::User' ) {
- if ( $args{'Owner'}->id ) {
- $Owner = $args{'Owner'};
- } else {
- $RT::Logger->error('passed not loaded owner object');
- push @non_fatal_errors, $self->loc("Invalid owner object");
- $Owner = undef;
- }
+ $Owner = $args{'Owner'};
}
#If we've been handed something else, try to load the user.
elsif ( $args{'Owner'} ) {
$Owner = RT::User->new( $self->CurrentUser );
$Owner->Load( $args{'Owner'} );
- $Owner->LoadByEmail( $args{'Owner'} )
- unless $Owner->Id;
- unless ( $Owner->Id ) {
- push @non_fatal_errors,
+
+ push( @non_fatal_errors,
$self->loc("Owner could not be set.") . " "
- . $self->loc( "User '[_1]' could not be found.", $args{'Owner'} );
- $Owner = undef;
- }
+ . $self->loc( "User '[_1]' could not be found.", $args{'Owner'} )
+ )
+ unless ( $Owner->Id );
}
#If we have a proposed owner and they don't have the right
#to own a ticket, scream about it and make them not the owner
-
- my $DeferOwner;
- if ( $Owner && $Owner->Id != $RT::Nobody->Id
- && !$Owner->HasRight( Object => $QueueObj, Right => 'OwnTicket' ) )
+ if (
+ ( defined($Owner) )
+ and ( $Owner->Id )
+ and ( $Owner->Id != $RT::Nobody->Id )
+ and (
+ !$Owner->HasRight(
+ Object => $QueueObj,
+ Right => 'OwnTicket'
+ )
+ )
+ )
{
- $DeferOwner = $Owner;
- $Owner = undef;
- $RT::Logger->debug('going to deffer setting owner');
+ $RT::Logger->warning( "User "
+ . $Owner->Name . "("
+ . $Owner->id
+ . ") was proposed "
+ . "as a ticket owner but has no rights to own "
+ . "tickets in "
+ . $QueueObj->Name );
+
+ push @non_fatal_errors,
+ $self->loc( "Owner '[_1]' does not have rights to own this ticket.",
+ $Owner->Name
+ );
+
+ $Owner = undef;
}
#If we haven't been handed a valid owner, make it nobody.
@@ -425,24 +552,13 @@ sub Create {
# We attempt to load or create each of the people who might have a role for this ticket
# _outside_ the transaction, so we don't get into ticket creation races
foreach my $type ( "Cc", "AdminCc", "Requestor" ) {
- $args{ $type } = [ $args{ $type } ] unless ref $args{ $type };
- foreach my $watcher ( splice @{ $args{$type} } ) {
- next unless $watcher;
- if ( $watcher =~ /^\d+$/ ) {
- push @{ $args{$type} }, $watcher;
- } else {
- my @addresses = RT::EmailParser->ParseEmailAddress( $watcher );
- foreach my $address( @addresses ) {
- my $user = RT::User->new( $RT::SystemUser );
- my ($uid, $msg) = $user->LoadOrCreateByEmail( $address );
- unless ( $uid ) {
- push @non_fatal_errors,
- $self->loc("Couldn't load or create user: [_1]", $msg);
- } else {
- push @{ $args{$type} }, $user->id;
- }
- }
- }
+ next unless ( defined $args{$type} );
+ foreach my $watcher (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ my $user = RT::User->new($RT::SystemUser);
+ $user->LoadOrCreateByEmail($watcher)
+ if ( $watcher && $watcher !~ /^\d+$/ );
}
}
@@ -454,7 +570,7 @@ sub Create {
Subject => $args{'Subject'},
InitialPriority => $args{'InitialPriority'},
FinalPriority => $args{'FinalPriority'},
- Priority => $args{'Priority'},
+ Priority => $priority,
Status => $args{'Status'},
TimeWorked => $args{'TimeWorked'},
TimeEstimated => $args{'TimeEstimated'},
@@ -468,21 +584,20 @@ sub Create {
# Parameters passed in during an import that we probably don't want to touch, otherwise
foreach my $attr qw(id Creator Created LastUpdated LastUpdatedBy) {
- $params{$attr} = $args{$attr} if $args{$attr};
+ $params{$attr} = $args{$attr} if ( $args{$attr} );
}
# Delete null integer parameters
foreach my $attr
- qw(TimeWorked TimeLeft TimeEstimated InitialPriority FinalPriority)
- {
+ qw(TimeWorked TimeLeft TimeEstimated InitialPriority FinalPriority) {
delete $params{$attr}
unless ( exists $params{$attr} && $params{$attr} );
}
# Delete the time worked if we're counting it in the transaction
- delete $params{'TimeWorked'} if $args{'_RecordTransaction'};
-
- my ($id,$ticket_message) = $self->SUPER::Create( %params );
+ delete $params{TimeWorked} if $args{'_RecordTransaction'};
+
+ my ($id,$ticket_message) = $self->SUPER::Create( %params);
unless ($id) {
$RT::Logger->crit( "Couldn't create a ticket: " . $ticket_message );
$RT::Handle->Rollback();
@@ -496,9 +611,10 @@ sub Create {
Field => 'EffectiveId',
Value => ( $args{'EffectiveId'} || $id )
);
- unless ( $val ) {
- $RT::Logger->crit("Couldn't set EffectiveId: $msg");
- $RT::Handle->Rollback;
+
+ unless ($val) {
+ $RT::Logger->crit("$self ->Create couldn't set EffectiveId: $msg\n");
+ $RT::Handle->Rollback();
return ( 0, 0,
$self->loc("Ticket could not be created due to an internal error")
);
@@ -515,86 +631,60 @@ sub Create {
);
}
- # Set the owner in the Groups table
- # We denormalize it into the Ticket table too because doing otherwise would
- # kill performance, bigtime. It gets kept in lockstep thanks to the magic of transactionalization
+# Set the owner in the Groups table
+# We denormalize it into the Ticket table too because doing otherwise would
+# kill performance, bigtime. It gets kept in lockstep thanks to the magic of transactionalization
+
$self->OwnerGroup->_AddMember(
PrincipalId => $Owner->PrincipalId,
InsideTransaction => 1
- ) unless $DeferOwner;
-
-
+ );
# {{{ Deal with setting up watchers
foreach my $type ( "Cc", "AdminCc", "Requestor" ) {
- # we know it's an array ref
- foreach my $watcher ( @{ $args{$type} } ) {
-
- # Note that we're using AddWatcher, rather than _AddWatcher, as we
- # actually _want_ that ACL check. Otherwise, random ticket creators
- # could make themselves adminccs and maybe get ticket rights. that would
- # be poor
- my $method = $type eq 'AdminCc'? 'AddWatcher': '_AddWatcher';
-
- my ($val, $msg) = $self->$method(
- Type => $type,
- PrincipalId => $watcher,
- Silent => 1,
- );
- push @non_fatal_errors, $self->loc("Couldn't set [_1] watcher: [_2]", $type, $msg)
- unless $val;
- }
- }
-
- if ($args{'SquelchMailTo'}) {
- my @squelch = ref( $args{'SquelchMailTo'} ) ? @{ $args{'SquelchMailTo'} }
- : $args{'SquelchMailTo'};
- $self->_SquelchMailTo( @squelch );
- }
+ next unless ( defined $args{$type} );
+ foreach my $watcher (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ # If there is an empty entry in the list, let's get out of here.
+ next unless $watcher;
- # }}}
+ # we reason that all-digits number must be a principal id, not email
+ # this is the only way to can add
+ my $field = 'Email';
+ $field = 'PrincipalId' if $watcher =~ /^\d+$/;
- # {{{ Add all the custom fields
+ my ( $wval, $wmsg );
- foreach my $arg ( keys %args ) {
- next unless $arg =~ /^CustomField-(\d+)$/i;
- my $cfid = $1;
+ if ( $type eq 'AdminCc' ) {
- foreach my $value (
- UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) )
- {
- next unless defined $value && length $value;
+ # Note that we're using AddWatcher, rather than _AddWatcher, as we
+ # actually _want_ that ACL check. Otherwise, random ticket creators
+ # could make themselves adminccs and maybe get ticket rights. that would
+ # be poor
+ ( $wval, $wmsg ) = $self->AddWatcher(
+ Type => $type,
+ $field => $watcher,
+ Silent => 1
+ );
+ }
+ else {
+ ( $wval, $wmsg ) = $self->_AddWatcher(
+ Type => $type,
+ $field => $watcher,
+ Silent => 1
+ );
+ }
- # Allow passing in uploaded LargeContent etc by hash reference
- my ($status, $msg) = $self->_AddCustomFieldValue(
- (UNIVERSAL::isa( $value => 'HASH' )
- ? %$value
- : (Value => $value)
- ),
- Field => $cfid,
- RecordTransaction => 0,
- );
- push @non_fatal_errors, $msg unless $status;
+ push @non_fatal_errors, $wmsg unless ($wval);
}
}
# }}}
-
# {{{ Deal with setting up links
- # TODO: Adding link may fire scrips on other end and those scrips
- # could create transactions on this ticket before 'Create' transaction.
- #
- # We should implement different schema: record 'Create' transaction,
- # create links and only then fire create transaction's scrips.
- #
- # Ideal variant: add all links without firing scrips, record create
- # transaction and only then fire scrips on the other ends of links.
- #
- # //RUZ
-
foreach my $type ( keys %LINKTYPEMAP ) {
next unless ( defined $args{$type} );
foreach my $link (
@@ -602,7 +692,7 @@ sub Create {
{
# Check rights on the other end of the link if we must
# then run _AddLink that doesn't check for ACLs
- if ( RT->Config->Get( 'StrictLinkACL' ) ) {
+ if ( $RT::StrictLinkACL ) {
my ($val, $msg, $obj) = $self->__GetTicketFromURI( URI => $link );
unless ( $val ) {
push @non_fatal_errors, $msg;
@@ -617,9 +707,7 @@ sub Create {
my ( $wval, $wmsg ) = $self->_AddLink(
Type => $LINKTYPEMAP{$type}->{'Type'},
$LINKTYPEMAP{$type}->{'Mode'} => $link,
- Silent => !$args{'_RecordTransaction'},
- 'Silent'. ( $LINKTYPEMAP{$type}->{'Mode'} eq 'Base'? 'Target': 'Base' )
- => 1,
+ Silent => 1
);
push @non_fatal_errors, $wmsg unless ($wval);
@@ -627,37 +715,100 @@ sub Create {
}
# }}}
- # Now that we've created the ticket and set up its metadata, we can actually go and check OwnTicket on the ticket itself.
- # This might be different than before in cases where extensions like RTIR are doing clever things with RT's ACL system
- if ( $DeferOwner ) {
- if (!$DeferOwner->HasRight( Object => $self, Right => 'OwnTicket')) {
-
- $RT::Logger->warning( "User " . $DeferOwner->Name . "(" . $DeferOwner->id
- . ") was proposed as a ticket owner but has no rights to own "
- . "tickets in " . $QueueObj->Name );
- push @non_fatal_errors, $self->loc(
- "Owner '[_1]' does not have rights to own this ticket.",
- $DeferOwner->Name
- );
- } else {
- $Owner = $DeferOwner;
- $self->__Set(Field => 'Owner', Value => $Owner->id);
+ # {{{ Deal with auto-customer association
+
+ #unless we already have (a) customer(s)...
+ unless ( $self->Customers->Count ) {
+
+ #first find any requestors with emails but *without* customer targets
+ my @NoCust_Requestors =
+ grep { $_->EmailAddress && ! $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ for my $Requestor (@NoCust_Requestors) {
+
+ #perhaps the stuff in here should be in a User method??
+ my @Customers =
+ &RT::URI::freeside::email_search( email=>$Requestor->EmailAddress );
+
+ foreach my $custnum ( map $_->{'custnum'}, @Customers ) {
+
+ ## false laziness w/RT/Interface/Web_Vendor.pm
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Requestor->_AddLink(@link);
+ #XXX should do something with $msg# push @non_fatal_errors, $msg;
+
+ }
+
+ }
+
+ #find any requestors with customer targets
+
+ my %cust_target = ();
+
+ my @Requestors =
+ grep { $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ foreach my $Requestor ( @Requestors ) {
+ foreach my $cust_link ( @{ $Requestor->Customers->ItemsArrayRef } ) {
+ $cust_target{ $cust_link->Target } = 1;
}
- $self->OwnerGroup->_AddMember(
- PrincipalId => $Owner->PrincipalId,
- InsideTransaction => 1
- );
+ }
+
+ #and then auto-associate this ticket with those customers
+
+ foreach my $cust_target ( keys %cust_target ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ #'Target' => "freeside://freeside/cust_main/$custnum",
+ 'Target' => $cust_target,
+ );
+
+ my( $val, $msg ) = $self->_AddLink(@link);
+ push @non_fatal_errors, $msg;
+
+ }
+
}
+ # }}}
+
+ # {{{ Add all the custom fields
+
+ foreach my $arg ( keys %args ) {
+ next unless ( $arg =~ /^CustomField-(\d+)$/i );
+ my $cfid = $1;
+ foreach
+ my $value ( UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) )
+ {
+ next unless ( length($value) );
+
+ # Allow passing in uploaded LargeContent etc by hash reference
+ $self->_AddCustomFieldValue(
+ (UNIVERSAL::isa( $value => 'HASH' )
+ ? %$value
+ : (Value => $value)
+ ),
+ Field => $cfid,
+ RecordTransaction => 0,
+ );
+ }
+ }
+
+ # }}}
+
if ( $args{'_RecordTransaction'} ) {
# {{{ Add a transaction for the create
my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
- Type => "Create",
- TimeTaken => $args{'TimeWorked'},
- MIMEObj => $args{'MIMEObj'},
- CommitScrips => !$args{'DryRun'},
+ Type => "Create",
+ TimeTaken => $args{'TimeWorked'},
+ MIMEObj => $args{'MIMEObj'}
);
if ( $self->Id && $Trans ) {
@@ -676,10 +827,6 @@ sub Create {
return ( 0, 0, $self->loc( "Ticket could not be created due to an internal error"));
}
- if ( $args{'DryRun'} ) {
- $RT::Handle->Rollback();
- return ($self->id, $TransObj, $ErrStr);
- }
$RT::Handle->Commit();
return ( $self->Id, $TransObj->Id, $ErrStr );
@@ -699,6 +846,179 @@ sub Create {
# }}}
+
+# {{{ UpdateFrom822
+
+=head2 UpdateFrom822 $MESSAGE
+
+Takes an RFC822 format message as a string and uses it to make a bunch of changes to a ticket.
+Returns an um. ask me again when the code exists
+
+
+=begin testing
+
+my $simple_update = <<EOF;
+Subject: target
+AddRequestor: jesse\@example.com
+EOF
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id,$msg) =$ticket->Create(Subject => 'first', Queue => 'general');
+ok($ticket->Id, "Created the test ticket - ".$id ." - ".$msg);
+$ticket->UpdateFrom822($simple_update);
+is($ticket->Subject, 'target', "changed the subject");
+my $jesse = RT::User->new($RT::SystemUser);
+$jesse->LoadByEmail('jesse@example.com');
+ok ($jesse->Id, "There's a user for jesse");
+ok($ticket->Requestors->HasMember( $jesse->PrincipalObj), "It has the jesse principal object as a requestor ");
+
+=end testing
+
+
+=cut
+
+sub UpdateFrom822 {
+ my $self = shift;
+ my $content = shift;
+ my %args = $self->_Parse822HeadersForAttributes($content);
+
+
+ my %ticketargs = (
+ Queue => $args{'queue'},
+ Subject => $args{'subject'},
+ Status => $args{'status'},
+ Due => $args{'due'},
+ Starts => $args{'starts'},
+ Started => $args{'started'},
+ Resolved => $args{'resolved'},
+ Owner => $args{'owner'},
+ Requestor => $args{'requestor'},
+ Cc => $args{'cc'},
+ AdminCc => $args{'admincc'},
+ TimeWorked => $args{'timeworked'},
+ TimeEstimated => $args{'timeestimated'},
+ TimeLeft => $args{'timeleft'},
+ InitialPriority => $args{'initialpriority'},
+ Priority => $args{'priority'},
+ FinalPriority => $args{'finalpriority'},
+ Type => $args{'type'},
+ DependsOn => $args{'dependson'},
+ DependedOnBy => $args{'dependedonby'},
+ RefersTo => $args{'refersto'},
+ ReferredToBy => $args{'referredtoby'},
+ Members => $args{'members'},
+ MemberOf => $args{'memberof'},
+ MIMEObj => $args{'mimeobj'}
+ );
+
+ foreach my $type qw(Requestor Cc Admincc) {
+
+ foreach my $action ( 'Add', 'Del', '' ) {
+
+ my $lctag = lc($action) . lc($type);
+ foreach my $list ( $args{$lctag}, $args{ $lctag . 's' } ) {
+
+ foreach my $entry ( ref($list) ? @{$list} : ($list) ) {
+ push @{$ticketargs{ $action . $type }} , split ( /\s*,\s*/, $entry );
+ }
+
+ }
+
+ # Todo: if we're given an explicit list, transmute it into a list of adds/deletes
+
+ }
+ }
+
+ # Add custom field entries to %ticketargs.
+ # TODO: allow named custom fields
+ map {
+ /^customfield-(\d+)$/
+ && ( $ticketargs{ "CustomField-" . $1 } = $args{$_} );
+ } keys(%args);
+
+# for each ticket we've been told to update, iterate through the set of
+# rfc822 headers and perform that update to the ticket.
+
+
+ # {{{ Set basic fields
+ my @attribs = qw(
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Status
+ Queue
+ Type
+ );
+
+
+ # Resolve the queue from a name to a numeric id.
+ if ( $ticketargs{'Queue'} and ( $ticketargs{'Queue'} !~ /^(\d+)$/ ) ) {
+ my $tempqueue = RT::Queue->new($RT::SystemUser);
+ $tempqueue->Load( $ticketargs{'Queue'} );
+ $ticketargs{'Queue'} = $tempqueue->Id() if ( $tempqueue->id );
+ }
+
+ my @results;
+
+ foreach my $attribute (@attribs) {
+ my $value = $ticketargs{$attribute};
+
+ if ( $value ne $self->$attribute() ) {
+
+ my $method = "Set$attribute";
+ my ( $code, $msg ) = $self->$method($value);
+
+ push @results, $self->loc($attribute) . ': ' . $msg;
+
+ }
+ }
+
+ # We special case owner changing, so we can use ForceOwnerChange
+ if ( $ticketargs{'Owner'} && ( $self->Owner != $ticketargs{'Owner'} ) ) {
+ my $ChownType = "Give";
+ $ChownType = "Force" if ( $ticketargs{'ForceOwnerChange'} );
+
+ my ( $val, $msg ) = $self->SetOwner( $ticketargs{'Owner'}, $ChownType );
+ push ( @results, $msg );
+ }
+
+ # }}}
+# Deal with setting watchers
+
+
+# Acceptable arguments:
+# Requestor
+# Requestors
+# AddRequestor
+# AddRequestors
+# DelRequestor
+
+ foreach my $type qw(Requestor Cc AdminCc) {
+
+ # If we've been given a number of delresses to del, do it.
+ foreach my $address (@{$ticketargs{'Del'.$type}}) {
+ my ($id, $msg) = $self->DeleteWatcher( Type => $type, Email => $address);
+ push (@results, $msg) ;
+ }
+
+ # If we've been given a number of addresses to add, do it.
+ foreach my $address (@{$ticketargs{'Add'.$type}}) {
+ $RT::Logger->debug("Adding $address as a $type");
+ my ($id, $msg) = $self->AddWatcher( Type => $type, Email => $address);
+ push (@results, $msg) ;
+
+ }
+
+
+}
+
+
+}
+# }}}
+
# {{{ _Parse822HeadersForAttributes Content
=head2 _Parse822HeadersForAttributes Content
@@ -743,7 +1063,7 @@ sub _Parse822HeadersForAttributes {
foreach my $date qw(due starts started resolved) {
my $dateobj = RT::Date->new($RT::SystemUser);
- if ( defined ($args{$date}) and $args{$date} =~ /^\d+$/ ) {
+ if ( $args{$date} =~ /^\d+$/ ) {
$dateobj->Set( Format => 'unix', Value => $args{$date} );
}
else {
@@ -869,7 +1189,7 @@ sub Import {
. ") was proposed "
. "as a ticket owner but has no rights to own "
. "tickets in '"
- . $QueueObj->Name . "'" );
+ . $QueueObj->Name . "'\n" );
$Owner = undef;
}
@@ -903,18 +1223,18 @@ sub Import {
EffectiveId => $EffectiveId,
Queue => $QueueObj->Id,
Owner => $Owner->Id,
- Subject => $args{'Subject'}, # loc
- InitialPriority => $args{'InitialPriority'}, # loc
- FinalPriority => $args{'FinalPriority'}, # loc
- Priority => $args{'InitialPriority'}, # loc
- Status => $args{'Status'}, # loc
- TimeWorked => $args{'TimeWorked'}, # loc
- Type => $args{'Type'}, # loc
- Created => $args{'Created'}, # loc
- Told => $args{'Told'}, # loc
- LastUpdated => $args{'Updated'}, # loc
- Resolved => $args{'Resolved'}, # loc
- Due => $args{'Due'}, # loc
+ Subject => $args{'Subject'}, # loc
+ InitialPriority => $args{'InitialPriority'}, # loc
+ FinalPriority => $args{'FinalPriority'}, # loc
+ Priority => $args{'InitialPriority'}, # loc
+ Status => $args{'Status'}, # loc
+ TimeWorked => $args{'TimeWorked'}, # loc
+ Type => $args{'Type'}, # loc
+ Created => $args{'Created'}, # loc
+ Told => $args{'Told'}, # loc
+ LastUpdated => $args{'Updated'}, # loc
+ Resolved => $args{'Resolved'}, # loc
+ Due => $args{'Due'}, # loc
);
# If the ticket didn't have an id
@@ -928,7 +1248,7 @@ sub Import {
unless ($val) {
$RT::Logger->err(
- $self . "->Import couldn't set EffectiveId: $msg" );
+ $self . "->Import couldn't set EffectiveId: $msg\n" );
}
}
@@ -971,6 +1291,48 @@ It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
It will return true on success and undef on failure.
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id, $msg) = $ticket->Create(Subject => "Foo",
+ Owner => $RT::SystemUser->Id,
+ Status => 'open',
+ Requestor => ['jesse@example.com'],
+ Queue => '1'
+ );
+ok ($id, "Ticket $id was created");
+ok(my $group = RT::Group->new($RT::SystemUser));
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Requestor'));
+ok ($group->Id, "Found the requestors object for this ticket");
+
+ok(my $jesse = RT::User->new($RT::SystemUser), "Creating a jesse rt::user");
+$jesse->LoadByEmail('jesse@example.com');
+ok($jesse->Id, "Found the jesse rt user");
+
+
+ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $jesse->PrincipalId), "The ticket actually has jesse at fsck.com as a requestor");
+ok ((my $add_id, $add_msg) = $ticket->AddWatcher(Type => 'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok ($add_id, "Add succeeded: ($add_msg)");
+ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
+$bob->LoadByEmail('bob@fsck.com');
+ok($bob->Id, "Found the bob rt user");
+ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $bob->PrincipalId), "The ticket actually has bob at fsck.com as a requestor");;
+ok ((my $add_id, $add_msg) = $ticket->DeleteWatcher(Type =>'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok (!$ticket->IsWatcher(Type => 'Requestor', Principal => $bob->PrincipalId), "The ticket no longer has bob at fsck.com as a requestor");;
+
+
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Cc'));
+ok ($group->Id, "Found the cc object for this ticket");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'AdminCc'));
+ok ($group->Id, "Found the AdminCc object for this ticket");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Owner'));
+ok ($group->Id, "Found the Owner object for this ticket");
+ok($group->HasMember($RT::SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'");
+
+=end testing
=cut
@@ -1023,12 +1385,12 @@ AddWatcher takes a parameter hash. The keys are as follows:
Type One of Requestor, Cc, AdminCc
-PrincipalId The RT::Principal id of the user or group that's being added as a watcher
+PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
Email The email address of the new watcher. If a user with this
email address can't be found, a new nonprivileged user will be created.
-If the watcher you\'re trying to set has an RT account, set the PrincipalId paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
+If the watcher you\'re trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
=cut
@@ -1041,47 +1403,43 @@ sub AddWatcher {
@_
);
- # ModifyTicket works in any case
- return $self->_AddWatcher( %args )
- if $self->CurrentUserHasRight('ModifyTicket');
- if ( $args{'Email'} ) {
- my ($addr) = RT::EmailParser->ParseEmailAddress( $args{'Email'} );
- return (0, $self->loc("Couldn't parse address from '[_1]' string", $args{'Email'} ))
- unless $addr;
+ return ( 0, "No principal specified" )
+ unless $args{'Email'} or $args{'PrincipalId'};
- if ( lc $self->CurrentUser->UserObj->EmailAddress
- eq lc RT::User->CanonicalizeEmailAddress( $addr->address ) )
- {
- $args{'PrincipalId'} = $self->CurrentUser->id;
+ if ( !$args{'PrincipalId'} and $args{'Email'} ) {
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->LoadByEmail( $args{'Email'} );
+ if ( $user->id ) {
+ $args{'PrincipalId'} = $user->PrincipalId;
delete $args{'Email'};
}
}
- # If the watcher isn't the current user then the current user has no right
- # bail
- unless ( $args{'PrincipalId'} && $self->CurrentUser->id == $args{'PrincipalId'} ) {
- return ( 0, $self->loc("Permission Denied") );
- }
+ # {{{ Check ACLS
+ # ModifyTicket allow you to add any watcher
+ return $self->_AddWatcher(%args)
+ if $self->CurrentUserHasRight('ModifyTicket');
- # If it's an AdminCc and they don't have 'WatchAsAdminCc', bail
- if ( $args{'Type'} eq 'AdminCc' ) {
- unless ( $self->CurrentUserHasRight('WatchAsAdminCc') ) {
- return ( 0, $self->loc('Permission Denied') );
+ #If the watcher we're trying to add is for the current user
+ if ( $self->CurrentUser->PrincipalId == ($args{'PrincipalId'} || 0) ) {
+ # If it's an AdminCc and they have 'WatchAsAdminCc'
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ return $self->_AddWatcher( %args )
+ if $self->CurrentUserHasRight('WatchAsAdminCc');
}
- }
- # If it's a Requestor or Cc and they don't have 'Watch', bail
- elsif ( $args{'Type'} eq 'Cc' || $args{'Type'} eq 'Requestor' ) {
- unless ( $self->CurrentUserHasRight('Watch') ) {
- return ( 0, $self->loc('Permission Denied') );
+ # If it's a Requestor or Cc and they have 'Watch'
+ elsif ( $args{'Type'} eq 'Cc' || $args{'Type'} eq 'Requestor' ) {
+ return $self->_AddWatcher( %args )
+ if $self->CurrentUserHasRight('Watch');
+ }
+ else {
+ $RT::Logger->warning( "AddWatcher got passed a bogus type" );
+ return ( 0, $self->loc('Error in parameters to Ticket->AddWatcher') );
}
- }
- else {
- $RT::Logger->warning( "AddWatcher got passed a bogus type");
- return ( 0, $self->loc('Error in parameters to Ticket->AddWatcher') );
}
- return $self->_AddWatcher( %args );
+ return ( 0, $self->loc("Permission Denied") );
}
#This contains the meat of AddWatcher. but can be called from a routine like
@@ -1100,8 +1458,14 @@ sub _AddWatcher {
my $principal = RT::Principal->new($self->CurrentUser);
if ($args{'Email'}) {
my $user = RT::User->new($RT::SystemUser);
- my ($pid, $msg) = $user->LoadOrCreateByEmail( $args{'Email'} );
- $args{'PrincipalId'} = $pid if $pid;
+ my ($pid, $msg) = $user->LoadOrCreateByEmail($args{'Email'});
+ # If we can't load the user by email address, let's try to load by username
+ unless ($pid) {
+ ($pid,$msg) = $user->Load($args{'Email'})
+ }
+ if ($pid) {
+ $args{'PrincipalId'} = $pid;
+ }
}
if ($args{'PrincipalId'}) {
$principal->Load($args{'PrincipalId'});
@@ -1130,7 +1494,7 @@ sub _AddWatcher {
my ( $m_id, $m_msg ) = $group->_AddMember( PrincipalId => $principal->Id,
InsideTransaction => 1 );
unless ($m_id) {
- $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg);
+ $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
return ( 0, $self->loc('Could not make that principal a [_1] for this ticket', $self->loc($args{'Type'})) );
}
@@ -1253,7 +1617,7 @@ sub DeleteWatcher {
$RT::Logger->error( "Failed to delete "
. $principal->Id
. " as a member of group "
- . $group->Id . ": "
+ . $group->Id . "\n"
. $m_msg );
return (0,
@@ -1286,6 +1650,35 @@ Takes an optional email address to never email about updates to this ticket.
Returns an array of the RT::Attribute objects for this ticket's 'SquelchMailTo' attributes.
+=begin testing
+
+my $t = RT::Ticket->new($RT::SystemUser);
+ok($t->Create(Queue => 'general', Subject => 'SquelchTest'));
+
+is($#{$t->SquelchMailTo}, -1, "The ticket has no squelched recipients");
+
+my @returned = $t->SquelchMailTo('nobody@example.com');
+
+is($#returned, 0, "The ticket has one squelched recipients");
+
+my @names = $t->Attributes->Names;
+is(shift @names, 'SquelchMailTo', "The attribute we have is SquelchMailTo");
+@returned = $t->SquelchMailTo('nobody@example.com');
+
+
+is($#returned, 0, "The ticket has one squelched recipients");
+
+@names = $t->Attributes->Names;
+is(shift @names, 'SquelchMailTo', "The attribute we have is SquelchMailTo");
+
+
+my ($ret, $msg) = $t->UnsquelchMailTo('nobody@example.com');
+ok($ret, "Removed nobody as a squelched recipient - ".$msg);
+@returned = $t->SquelchMailTo();
+is($#returned, -1, "The ticket has no squelched recipients". join(',',@returned));
+
+
+=end testing
=cut
@@ -1295,22 +1688,14 @@ sub SquelchMailTo {
unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
return undef;
}
- } else {
- unless ( $self->CurrentUserHasRight('ShowTicket') ) {
- return undef;
- }
-
- }
- return $self->_SquelchMailTo(@_);
-}
-
-sub _SquelchMailTo {
- my $self = shift;
- if (@_) {
my $attr = shift;
$self->AddAttribute( Name => 'SquelchMailTo', Content => $attr )
- unless grep { $_->Content eq $attr }
- $self->Attributes->Named('SquelchMailTo');
+ unless grep { $_->Content eq $attr }
+ $self->Attributes->Named('SquelchMailTo');
+
+ }
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return undef;
}
my @attributes = $self->Attributes->Named('SquelchMailTo');
return (@attributes);
@@ -1386,6 +1771,7 @@ sub CcAddresses {
unless ( $self->CurrentUserHasRight('ShowTicket') ) {
return undef;
}
+
return ( $self->Cc->MemberEmailAddressesAsString);
}
@@ -1416,6 +1802,25 @@ sub Requestors {
# }}}
+# {{{ sub _Requestors
+
+=head2 _Requestors
+
+Private non-ACLed variant of Reqeustors so that we can look them up for the
+purposes of customer auto-association during create.
+
+=cut
+
+sub _Requestors {
+ my $self = shift;
+
+ my $group = RT::Group->new($RT::SystemUser);
+ $group->LoadTicketRoleGroup(Type => 'Requestor', Ticket => $self->Id);
+ return ($group);
+}
+
+# }}}
+
# {{{ sub Cc
=head2 Cc
@@ -1498,6 +1903,7 @@ sub IsWatcher {
$group->LoadTicketRoleGroup(Type => $args{'Type'}, Ticket => $self->id);
# Find the relevant principal.
+ my $principal = RT::Principal->new($self->CurrentUser);
if (!$args{PrincipalId} && $args{Email}) {
# Look up the specified user.
my $user = RT::User->new($self->CurrentUser);
@@ -1510,9 +1916,10 @@ sub IsWatcher {
return 0;
}
}
+ $principal->Load($args{'PrincipalId'});
# Ask if it has the member in question
- return $group->HasMember( $args{'PrincipalId'} );
+ return ($group->HasMember($principal));
}
# }}}
@@ -1521,9 +1928,9 @@ sub IsWatcher {
=head2 IsRequestor PRINCIPAL_ID
-Takes an L<RT::Principal> id.
+ Takes an RT::Principal id
+ Returns true if the principal is a requestor of the current ticket.
-Returns true if the principal is a requestor of the current ticket.
=cut
@@ -1542,7 +1949,7 @@ sub IsRequestor {
=head2 IsCc PRINCIPAL_ID
Takes an RT::Principal id.
- Returns true if the principal is a Cc of the current ticket.
+ Returns true if the principal is a requestor of the current ticket.
=cut
@@ -1562,7 +1969,7 @@ sub IsCc {
=head2 IsAdminCc PRINCIPAL_ID
Takes an RT::Principal id.
- Returns true if the principal is an AdminCc of the current ticket.
+ Returns true if the principal is a requestor of the current ticket.
=cut
@@ -1591,8 +1998,8 @@ sub IsOwner {
# no ACL check since this is used in acl decisions
# unless ($self->CurrentUserHasRight('ShowTicket')) {
- # return(undef);
- # }
+ # return(undef);
+ # }
#Tickets won't yet have owners when they're being created.
unless ( $self->OwnerObj->id ) {
@@ -1613,46 +2020,6 @@ sub IsOwner {
# }}}
-
-=head2 TransactionAddresses
-
-Returns a composite hashref of the results of L<RT::Transaction/Addresses> for all this ticket's Create, Comment or Correspond transactions.
-The keys are C<To>, C<Cc> and C<Bcc>. The values are lists of C<Email::Address> objects.
-
-NOTE: For performance reasons, this method might want to skip transactions and go straight for attachments. But to make that work right, we're going to need to go and walk around the access control in Attachment.pm's sub _Value.
-
-=cut
-
-
-sub TransactionAddresses {
- my $self = shift;
- my $txns = $self->Transactions;
-
- my %addresses = ();
- foreach my $type (qw(Create Comment Correspond)) {
- $txns->Limit(FIELD => 'Type', OPERATOR => '=', VALUE => $type , ENTRYAGGREGATOR => 'OR', CASESENSITIVE => 1);
- }
-
- while (my $txn = $txns->Next) {
- my $txnaddrs = $txn->Addresses;
- foreach my $addrlist ( values %$txnaddrs ) {
- foreach my $addr (@$addrlist) {
- # Skip addresses without a phrase (things that are just raw addresses) if we have a phrase
- next if ($addresses{$addr->address} && $addresses{$addr->address}->phrase && not $addr->phrase);
- # skips "comment-only" addresses
- next unless ($addr->address);
- $addresses{$addr->address} = $addr;
- }
- }
- }
-
- return \%addresses;
-
-}
-
-
-
-
# {{{ Routines dealing with queues
# {{{ sub ValidateQueue
@@ -1726,18 +2093,7 @@ sub SetQueue {
$RT::Logger->error("Couldn't set owner on queue change: $msg") unless $status;
}
- my ($status, $msg) = $self->_Set( Field => 'Queue', Value => $NewQueueObj->Id() );
-
- if ( $status ) {
- # On queue change, change queue for reminders too
- my $reminder_collection = $self->Reminders->Collection;
- while ( my $reminder = $reminder_collection->Next ) {
- my ($status, $msg) = $reminder->SetQueue($NewQueue);
- $RT::Logger->error('Queue change failed for reminder #' . $reminder->Id . ': ' . $msg) unless $status;
- }
- }
-
- return ($status, $msg);
+ return ( $self->_Set( Field => 'Queue', Value => $NewQueueObj->Id() ) );
}
# }}}
@@ -1780,8 +2136,8 @@ sub DueObj {
my $time = new RT::Date( $self->CurrentUser );
# -1 is RT::Date slang for never
- if ( my $due = $self->Due ) {
- $time->Set( Format => 'sql', Value => $due );
+ if ( $self->Due ) {
+ $time->Set( Format => 'sql', Value => $self->Due );
}
else {
$time->Set( Format => 'unix', Value => -1 );
@@ -1960,37 +2316,23 @@ Returns the amount of time worked on this ticket as a Text String
sub TimeWorkedAsString {
my $self = shift;
- my $value = $self->TimeWorked;
+ return "0" unless $self->TimeWorked;
- # return the # of minutes worked turned into seconds and written as
- # a simple text string, this is not really a date object, but if we
- # diff a number of seconds vs the epoch, we'll get a nice description
- # of time worked.
- return "" unless $value;
- return RT::Date->new( $self->CurrentUser )
- ->DurationAsString( $value * 60 );
-}
-
-# }}}
-
-# {{{ sub TimeLeftAsString
+ #This is not really a date object, but if we diff a number of seconds
+ #vs the epoch, we'll get a nice description of time worked.
-=head2 TimeLeftAsString
+ my $worked = new RT::Date( $self->CurrentUser );
-Returns the amount of time left on this ticket as a Text String
+ #return the #of minutes worked turned into seconds and written as
+ # a simple text string
-=cut
-
-sub TimeLeftAsString {
- my $self = shift;
- my $value = $self->TimeLeft;
- return "" unless $value;
- return RT::Date->new( $self->CurrentUser )
- ->DurationAsString( $value * 60 );
+ return ( $worked->DurationAsString( $self->TimeWorked * 60 ) );
}
# }}}
+# }}}
+
# {{{ Routines dealing with correspondence/comments
# {{{ sub Comment
@@ -1998,9 +2340,9 @@ sub TimeLeftAsString {
=head2 Comment
Comment on this ticket.
-Takes a hash with the following attributes:
+Takes a hashref with the following attributes:
If MIMEObj is undefined, Content will be used to build a MIME::Entity for this
-comment.
+commentl
MIMEObj, TimeTaken, CcMessageTo, BccMessageTo, Content, DryRun
@@ -2111,37 +2453,34 @@ Performs no access control checks. hence, dangerous.
=cut
sub _RecordNote {
+
my $self = shift;
- my %args = (
- CcMessageTo => undef,
- BccMessageTo => undef,
- Encrypt => undef,
- Sign => undef,
- MIMEObj => undef,
- Content => undef,
- NoteType => 'Correspond',
- TimeTaken => 0,
- CommitScrips => 1,
- @_
- );
+ my %args = ( CcMessageTo => undef,
+ BccMessageTo => undef,
+ MIMEObj => undef,
+ Content => undef,
+ TimeTaken => 0,
+ CommitScrips => 1,
+ @_ );
unless ( $args{'MIMEObj'} || $args{'Content'} ) {
- return ( 0, $self->loc("No message attached"), undef );
+ return ( 0, $self->loc("No message attached"), undef );
}
-
unless ( $args{'MIMEObj'} ) {
- $args{'MIMEObj'} = MIME::Entity->build(
- Data => ( ref $args{'Content'}? $args{'Content'}: [ $args{'Content'} ] )
- );
- }
+ $args{'MIMEObj'} = MIME::Entity->build( Data => (
+ ref $args{'Content'}
+ ? $args{'Content'}
+ : [ $args{'Content'} ]
+ ) );
+ }
# convert text parts into utf-8
RT::I18N::SetMIMEEntityToUTF8( $args{'MIMEObj'} );
- # If we've been passed in CcMessageTo and BccMessageTo fields,
- # add them to the mime object for passing on to the transaction handler
- # The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and
- # RT-Send-Bcc: headers
+# If we've been passed in CcMessageTo and BccMessageTo fields,
+# add them to the mime object for passing on to the transaction handler
+# The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and RT-Send-Bcc:
+# headers
foreach my $type (qw/Cc Bcc/) {
@@ -2149,26 +2488,28 @@ sub _RecordNote {
my $addresses = join ', ', (
map { RT::User->CanonicalizeEmailAddress( $_->address ) }
- Email::Address->parse( $args{ $type . 'MessageTo' } ) );
+ Mail::Address->parse( $args{ $type . 'MessageTo' } ) );
$args{'MIMEObj'}->head->add( 'RT-Send-' . $type, $addresses );
}
}
- foreach my $argument (qw(Encrypt Sign)) {
- $args{'MIMEObj'}->head->add(
- "X-RT-$argument" => $args{ $argument }
- ) if defined $args{ $argument };
- }
-
# If this is from an external source, we need to come up with its
# internal Message-ID now, so all emails sent because of this
# message have a common Message-ID
- my $org = RT->Config->Get('Organization');
- my $msgid = $args{'MIMEObj'}->head->get('Message-ID');
- unless (defined $msgid && $msgid =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$org\E>/) {
- $args{'MIMEObj'}->head->set(
- 'RT-Message-ID' => RT::Interface::Email::GenMessageId( Ticket => $self )
- );
+ unless ( ($args{'MIMEObj'}->head->get('Message-ID') || '')
+ =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$RT::Organization>/ )
+ {
+ $args{'MIMEObj'}->head->replace( 'RT-Message-ID',
+ "<rt-"
+ . $RT::VERSION . "-"
+ . $$ . "-"
+ . CORE::time() . "-"
+ . int(rand(2000)) . '.'
+ . $self->id . "-"
+ . "0" . "-" # Scrip
+ . "0" . "@" # Email sent
+ . $RT::Organization
+ . ">" );
}
#Record the correspondence (write the transaction)
@@ -2204,7 +2545,13 @@ sub _Links {
unless ( $self->{"$field$type"} ) {
$self->{"$field$type"} = new RT::Links( $self->CurrentUser );
- if ( $self->CurrentUserHasRight('ShowTicket') ) {
+
+ #not sure what this ACL was supposed to do... but returning the
+ # bare (unlimited) RT::Links certainly seems wrong, it causes the
+ # $Ticket->Customers method during creation to return results for every
+ # ticket...
+ #if ( $self->CurrentUserHasRight('ShowTicket') ) {
+
# Maybe this ticket is a merged ticket
my $Tickets = new RT::Tickets( $self->CurrentUser );
# at least to myself
@@ -2221,7 +2568,7 @@ sub _Links {
$self->{"$field$type"}->Limit( FIELD => 'Type',
VALUE => $type )
if ($type);
- }
+ #}
}
return ( $self->{"$field$type"} );
}
@@ -2232,14 +2579,9 @@ sub _Links {
=head2 DeleteLink
-Delete a link. takes a paramhash of Base, Target, Type, Silent,
-SilentBase and SilentTarget. Either Base or Target must be null.
-The null value will be replaced with this ticket\'s id.
-
-If Silent is true then no transaction would be recorded, in other
-case you can control creation of transactions on both base and
-target with SilentBase and SilentTarget respectively. By default
-both transactions are created.
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
=cut
@@ -2249,21 +2591,18 @@ sub DeleteLink {
Base => undef,
Target => undef,
Type => undef,
- Silent => undef,
- SilentBase => undef,
- SilentTarget => undef,
@_
);
unless ( $args{'Target'} || $args{'Base'} ) {
- $RT::Logger->error("Base or Target must be specified");
+ $RT::Logger->error("Base or Target must be specified\n");
return ( 0, $self->loc('Either base or target must be specified') );
}
#check acls
my $right = 0;
$right++ if $self->CurrentUserHasRight('ModifyTicket');
- if ( !$right && RT->Config->Get( 'StrictLinkACL' ) ) {
+ if ( !$right && $RT::StrictLinkACL ) {
return ( 0, $self->loc("Permission Denied") );
}
@@ -2274,55 +2613,57 @@ sub DeleteLink {
if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
$right++;
}
- if ( ( !RT->Config->Get( 'StrictLinkACL' ) && $right == 0 ) ||
- ( RT->Config->Get( 'StrictLinkACL' ) && $right < 2 ) )
+ if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+ ( $RT::StrictLinkACL && $right < 2 ) )
{
return ( 0, $self->loc("Permission Denied") );
}
my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
- return ( 0, $Msg ) unless $val;
- return ( $val, $Msg ) if $args{'Silent'};
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
my ($direction, $remote_link);
if ( $args{'Base'} ) {
- $remote_link = $args{'Base'};
- $direction = 'Target';
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
}
elsif ( $args{'Target'} ) {
- $remote_link = $args{'Target'};
- $direction = 'Base';
- }
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
- my $remote_uri = RT::URI->new( $self->CurrentUser );
- $remote_uri->FromURI( $remote_link );
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
- unless ( $args{ 'Silent'. $direction } ) {
my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
Type => 'DeleteLink',
- Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
- OldValue => $remote_uri->URI || $remote_link,
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
TimeTaken => 0
);
- $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
- }
-
- if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) {
- my $OtherObj = $remote_uri->Object;
- my ( $val, $Msg ) = $OtherObj->_NewTransaction(
- Type => 'DeleteLink',
- Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
- : $LINKDIRMAP{$args{'Type'}}->{Target},
- OldValue => $self->URI,
- ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
- TimeTaken => 0,
- );
- $RT::Logger->error("Couldn't create transaction: $Msg") unless $val;
- }
- return ( $val, $Msg );
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
}
# }}}
@@ -2333,31 +2674,24 @@ sub DeleteLink {
Takes a paramhash of Type and one of Base or Target. Adds that link to this ticket.
-If Silent is true then no transaction would be recorded, in other
-case you can control creation of transactions on both base and
-target with SilentBase and SilentTarget respectively. By default
-both transactions are created.
-
=cut
sub AddLink {
my $self = shift;
- my %args = ( Target => '',
- Base => '',
- Type => '',
- Silent => undef,
- SilentBase => undef,
- SilentTarget => undef,
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
@_ );
unless ( $args{'Target'} || $args{'Base'} ) {
- $RT::Logger->error("Base or Target must be specified");
+ $RT::Logger->error("Base or Target must be specified\n");
return ( 0, $self->loc('Either base or target must be specified') );
}
my $right = 0;
$right++ if $self->CurrentUserHasRight('ModifyTicket');
- if ( !$right && RT->Config->Get( 'StrictLinkACL' ) ) {
+ if ( !$right && $RT::StrictLinkACL ) {
return ( 0, $self->loc("Permission Denied") );
}
@@ -2368,8 +2702,8 @@ sub AddLink {
if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
$right++;
}
- if ( ( !RT->Config->Get( 'StrictLinkACL' ) && $right == 0 ) ||
- ( RT->Config->Get( 'StrictLinkACL' ) && $right < 2 ) )
+ if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+ ( $RT::StrictLinkACL && $right < 2 ) )
{
return ( 0, $self->loc("Permission Denied") );
}
@@ -2387,8 +2721,8 @@ sub __GetTicketFromURI {
$uri_obj->FromURI( $args{'URI'} );
unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
- my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
- $RT::Logger->warning( $msg );
+ my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+ $RT::Logger->warning( "$msg\n" );
return( 0, $msg );
}
my $obj = $uri_obj->Resolver->Object;
@@ -2406,17 +2740,14 @@ Private non-acled variant of AddLink so that links can be added during create.
sub _AddLink {
my $self = shift;
- my %args = ( Target => '',
- Base => '',
- Type => '',
- Silent => undef,
- SilentBase => undef,
- SilentTarget => undef,
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
@_ );
my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
return ($val, $msg) if !$val || $exist;
- return ($val, $msg) if $args{'Silent'};
my ($direction, $remote_link);
if ( $args{'Target'} ) {
@@ -2427,33 +2758,34 @@ sub _AddLink {
$direction = 'Target';
}
- my $remote_uri = RT::URI->new( $self->CurrentUser );
- $remote_uri->FromURI( $remote_link );
-
- unless ( $args{ 'Silent'. $direction } ) {
- my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
- Type => 'AddLink',
- Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
- NewValue => $remote_uri->URI || $remote_link,
- TimeTaken => 0
- );
- $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
- }
-
- if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) {
- my $OtherObj = $remote_uri->Object;
- my ( $val, $msg ) = $OtherObj->_NewTransaction(
- Type => 'AddLink',
- Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
- : $LINKDIRMAP{$args{'Type'}}->{Target},
- NewValue => $self->URI,
- ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
- TimeTaken => 0,
- );
- $RT::Logger->error("Couldn't create transaction: $msg") unless $val;
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
}
- return ( $val, $msg );
}
# }}}
@@ -2466,6 +2798,26 @@ sub _AddLink {
MergeInto take the id of the ticket to merge this ticket into.
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+$t1->Create ( Subject => 'Merge test 1', Queue => 'general', Requestor => 'merge1@example.com');
+my $t1id = $t1->id;
+my $t2 = RT::Ticket->new($RT::SystemUser);
+$t2->Create ( Subject => 'Merge test 2', Queue => 'general', Requestor => 'merge2@example.com');
+my $t2id = $t2->id;
+my ($msg, $val) = $t1->MergeInto($t2->id);
+ok ($msg,$val);
+$t1 = RT::Ticket->new($RT::SystemUser);
+is ($t1->id, undef, "ok. we've got a blank ticket1");
+$t1->Load($t1id);
+
+is ($t1->id, $t2->id);
+
+is ($t1->Requestors->MembersObj->Count, 2);
+
+
+=end testing
=cut
@@ -2631,29 +2983,6 @@ sub MergeInto {
return ( 1, $self->loc("Merge Successful") );
}
-=head2 Merged
-
-Returns list of tickets' ids that's been merged into this ticket.
-
-=cut
-
-sub Merged {
- my $self = shift;
-
- my $mergees = new RT::Tickets( $self->CurrentUser );
- $mergees->Limit(
- FIELD => 'EffectiveId',
- OPERATOR => '=',
- VALUE => $self->Id,
- );
- $mergees->Limit(
- FIELD => 'id',
- OPERATOR => '!=',
- VALUE => $self->Id,
- );
- return map $_->id, @{ $mergees->ItemsArrayRef || [] };
-}
-
# }}}
# }}}
@@ -2710,6 +3039,28 @@ Takes two arguments:
and (optionally) the type of the SetOwner Transaction. It defaults
to 'Give'. 'Steal' is also a valid option.
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok ($root->Id, "Loaded the root user");
+my $t = RT::Ticket->new($RT::SystemUser);
+$t->Load(1);
+$t->SetOwner('root');
+is ($t->OwnerObj->Name, 'root' , "Root owns the ticket");
+$t->Steal();
+is ($t->OwnerObj->id, $RT::SystemUser->id , "SystemUser owns the ticket");
+my $txns = RT::Transactions->new($RT::SystemUser);
+$txns->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$txns->Limit(FIELD => 'ObjectId', VALUE => '1');
+$txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket');
+$txns->Limit(FIELD => 'Type', OPERATOR => '!=', VALUE => 'EmailRecord');
+
+my $steal = $txns->First;
+ok($steal->OldValue == $root->Id , "Stolen from root");
+ok($steal->NewValue == $RT::SystemUser->Id , "Stolen by the systemuser");
+
+=end testing
=cut
@@ -2792,15 +3143,10 @@ sub SetOwner {
# Delete the owner in the owner group, then add a new one
# TODO: is this safe? it's not how we really want the API to work
# for most things, but it's fast.
- my ( $del_id, $del_msg );
- for my $owner (@{$self->OwnerGroup->MembersObj->ItemsArrayRef}) {
- ($del_id, $del_msg) = $owner->Delete();
- last unless ($del_id);
- }
-
+ my ( $del_id, $del_msg ) = $self->OwnerGroup->MembersObj->First->Delete();
unless ($del_id) {
$RT::Handle->Rollback();
- return ( 0, $self->loc("Could not change owner: [_1]", $del_msg) );
+ return ( 0, $self->loc("Could not change owner. ") . $del_msg );
}
my ( $add_id, $add_msg ) = $self->OwnerGroup->_AddMember(
@@ -2808,7 +3154,7 @@ sub SetOwner {
InsideTransaction => 1 );
unless ($add_id) {
$RT::Handle->Rollback();
- return ( 0, $self->loc("Could not change owner: [_1]", $add_msg ) );
+ return ( 0, $self->loc("Could not change owner. ") . $add_msg );
}
# We call set twice with slightly different arguments, so
@@ -2825,7 +3171,7 @@ sub SetOwner {
unless ($val) {
$RT::Handle->Rollback;
- return ( 0, $self->loc("Could not change owner: [_1]", $msg) );
+ return ( 0, $self->loc("Could not change owner. ") . $msg );
}
($val, $msg) = $self->_NewTransaction(
@@ -2942,6 +3288,25 @@ Set this ticket\'s status. STATUS can be one of: new, open, stalled, resolved, r
Alternatively, you can pass in a list of named parameters (Status => STATUS, Force => FORCE). If FORCE is true, ignore unresolved dependencies and force a status change.
+=begin testing
+
+my $tt = RT::Ticket->new($RT::SystemUser);
+my ($id, $tid, $msg)= $tt->Create(Queue => 'general',
+ Subject => 'test');
+ok($id, $msg);
+is($tt->Status, 'new', "New ticket is created as new");
+
+($id, $msg) = $tt->SetStatus('open');
+ok($id, $msg);
+like($msg, qr/open/i, "Status message is correct");
+($id, $msg) = $tt->SetStatus('resolved');
+ok($id, $msg);
+like($msg, qr/resolved/i, "Status message is correct");
+($id, $msg) = $tt->SetStatus('resolved');
+ok(!$id,$msg);
+
+
+=end testing
=cut
@@ -2951,10 +3316,10 @@ sub SetStatus {
my %args;
if (@_ == 1) {
- $args{Status} = shift;
+ $args{Status} = shift;
}
else {
- %args = (@_);
+ %args = (@_);
}
#Check ACL
@@ -2976,7 +3341,7 @@ sub SetStatus {
$now->SetToNow();
#If we're changing the status from new, record that we've started
- if ( $self->Status eq 'new' && $args{Status} ne 'new' ) {
+ if ( ( $self->Status =~ /new/ ) && ( $args{Status} ne 'new' ) ) {
#Set the Started time to "now"
$self->_Set( Field => 'Started',
@@ -3004,14 +3369,20 @@ sub SetStatus {
# }}}
-# {{{ sub Delete
+# {{{ sub Kill
-=head2 Delete
+=head2 Kill
Takes no arguments. Marks this ticket for garbage collection
=cut
+sub Kill {
+ my $self = shift;
+ $RT::Logger->crit("'Kill' is deprecated. use 'Delete' instead at (". join(":",caller).").");
+ return $self->Delete;
+}
+
sub Delete {
my $self = shift;
return ( $self->SetStatus('deleted') );
@@ -3083,7 +3454,7 @@ sub Resolve {
# }}}
-
+
# {{{ Actions + Routines dealing with transactions
# {{{ sub SetTold and _SetTold
@@ -3136,39 +3507,14 @@ sub _SetTold {
Value => $now->ISO ) );
}
-=head2 SeenUpTo
-
-
-=cut
-
-sub SeenUpTo {
- my $self = shift;
- my $uid = $self->CurrentUser->id;
- my $attr = $self->FirstAttribute( "User-". $uid ."-SeenUpTo" );
- return if $attr && $attr->Content gt $self->LastUpdated;
-
- my $txns = $self->Transactions;
- $txns->Limit( FIELD => 'Type', VALUE => 'Comment' );
- $txns->Limit( FIELD => 'Type', VALUE => 'Correspond' );
- $txns->Limit( FIELD => 'Creator', OPERATOR => '!=', VALUE => $uid );
- $txns->Limit(
- FIELD => 'Created',
- OPERATOR => '>',
- VALUE => $attr->Content
- ) if $attr;
- $txns->RowsPerPage(1);
- return $txns->First;
-}
-
# }}}
=head2 TransactionBatch
-Returns an array reference of all transactions created on this ticket during
-this ticket object's lifetime or since last application of a batch, or undef
-if there were none.
+ Returns an array reference of all transactions created on this ticket during
+ this ticket object's lifetime, or undef if there were none.
-Only works when the C<UseTransactionBatch> config option is set to true.
+ Only works when the $RT::UseTransactionBatch config variable is set to true.
=cut
@@ -3177,49 +3523,6 @@ sub TransactionBatch {
return $self->{_TransactionBatch};
}
-=head2 ApplyTransactionBatch
-
-Applies scrips on the current batch of transactions and shinks it. Usually
-batch is applied when object is destroyed, but in some cases it's too late.
-
-=cut
-
-sub ApplyTransactionBatch {
- my $self = shift;
-
- my $batch = $self->TransactionBatch;
- return unless $batch && @$batch;
-
- $self->_ApplyTransactionBatch;
-
- $self->{_TransactionBatch} = [];
-}
-
-sub _ApplyTransactionBatch {
- my $self = shift;
- my $batch = $self->TransactionBatch;
-
- my %seen;
- my $types = join ',', grep !$seen{$_}++, grep defined, map $_->Type, grep defined, @{$batch};
-
- require RT::Scrips;
- RT::Scrips->new($RT::SystemUser)->Apply(
- Stage => 'TransactionBatch',
- TicketObj => $self,
- TransactionObj => $batch->[0],
- Type => $types,
- );
-
- # Entry point of the rule system
- my $rules = RT::Ruleset->FindAllRules(
- Stage => 'TransactionBatch',
- TicketObj => $self,
- TransactionObj => $batch->[0],
- Type => $types,
- );
- RT::Ruleset->CommitRules($rules);
-}
-
sub DESTROY {
my $self = shift;
@@ -3233,10 +3536,16 @@ sub DESTROY {
# when an object's refcount is changed in its destructor.
return if $self->{_Destroyed}++;
- my $batch = $self->TransactionBatch;
- return unless $batch && @$batch;
+ my $batch = $self->TransactionBatch or return;
+ return unless @$batch;
- return $self->_ApplyTransactionBatch;
+ require RT::Scrips;
+ RT::Scrips->new($RT::SystemUser)->Apply(
+ Stage => 'TransactionBatch',
+ TicketObj => $self,
+ TransactionObj => $batch->[0],
+ Type => join(',', (map { $_->Type } @{$batch}) )
+ );
}
# }}}
@@ -3353,7 +3662,7 @@ sub _Value {
#if the field is public, return it.
if ( $self->_Accessible( $field, 'public' ) ) {
- #$RT::Logger->debug("Skipping ACL check for $field");
+ #$RT::Logger->debug("Skipping ACL check for $field\n");
return ( $self->SUPER::_Value($field) );
}
@@ -3412,10 +3721,13 @@ sub CurrentUserHasRight {
my $self = shift;
my $right = shift;
- return $self->CurrentUser->PrincipalObj->HasRight(
- Object => $self,
- Right => $right,
- )
+ return (
+ $self->HasRight(
+ Principal => $self->CurrentUser->UserObj(),
+ Right => "$right"
+ )
+ );
+
}
# }}}
@@ -3442,7 +3754,7 @@ sub HasRight {
unless ( ( defined $args{'Principal'} ) and ( ref( $args{'Principal'} ) ) )
{
- Carp::cluck("Principal attrib undefined for Ticket::HasRight");
+ Carp::cluck;
$RT::Logger->crit("Principal attrib undefined for Ticket::HasRight");
return(undef);
}
@@ -3499,13 +3811,11 @@ sub Transactions {
# if the user may not see comments do not return them
unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
$transactions->Limit(
- SUBCLAUSE => 'acl',
FIELD => 'Type',
OPERATOR => '!=',
VALUE => "Comment"
);
$transactions->Limit(
- SUBCLAUSE => 'acl',
FIELD => 'Type',
OPERATOR => '!=',
VALUE => "CommentEmailRecord",
@@ -3513,13 +3823,6 @@ sub Transactions {
);
}
- } else {
- $transactions->Limit(
- SUBCLAUSE => 'acl',
- FIELD => 'id',
- VALUE => 0,
- ENTRYAGGREGATOR => 'AND'
- );
}
return ($transactions);
@@ -3558,17 +3861,18 @@ sub CustomFieldValues {
my $self = shift;
my $field = shift;
- return $self->SUPER::CustomFieldValues( $field ) if !$field || $field =~ /^\d+$/;
+ return $self->SUPER::CustomFieldValues( $field )
+ if !$field || $field =~ /^\d+$/;
my $cf = RT::CustomField->new( $self->CurrentUser );
- $cf->SetContextObject( $self );
$cf->LoadByNameAndQueue( Name => $field, Queue => $self->Queue );
unless ( $cf->id ) {
$cf->LoadByNameAndQueue( Name => $field, Queue => 0 );
}
# If we didn't find a valid cfid, give up.
- return RT::ObjectCustomFieldValues->new( $self->CurrentUser ) unless $cf->id;
+ return RT::ObjectCustomFieldValues->new( $self->CurrentUser )
+ unless $cf->id;
return $self->SUPER::CustomFieldValues( $cf->id );
}
@@ -3590,23 +3894,6 @@ sub CustomFieldLookupType {
"RT::Queue-RT::Ticket";
}
-=head2 ACLEquivalenceObjects
-
-This method returns a list of objects for which a user's rights also apply
-to this ticket. Generally, this is only the ticket's queue, but some RT
-extensions may make other objects available too.
-
-This method is called from L<RT::Principal/HasRight>.
-
-=cut
-
-sub ACLEquivalenceObjects {
- my $self = shift;
- return $self->QueueObj;
-
-}
-
-
1;
=head1 AUTHOR
diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm
index d0699e599..b6b349144 100755
--- a/rt/lib/RT/Tickets.pm
+++ b/rt/lib/RT/Tickets.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Ticket item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Transaction.pm b/rt/lib/RT/Transaction.pm
index 351e79d51..ca491a6c7 100755
--- a/rt/lib/RT/Transaction.pm
+++ b/rt/lib/RT/Transaction.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -70,6 +45,7 @@ RT::Transaction
package RT::Transaction;
use RT::Record;
+use RT::Ticket;
use vars qw( @ISA );
@@ -86,21 +62,18 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
- varchar(64) 'ObjectType'.
- int(11) 'ObjectId'.
+ int(11) 'EffectiveTicket'.
+ int(11) 'Ticket'.
int(11) 'TimeTaken'.
varchar(20) 'Type'.
varchar(40) 'Field'.
varchar(255) 'OldValue'.
varchar(255) 'NewValue'.
- varchar(255) 'ReferenceType'.
- int(11) 'OldReference'.
- int(11) 'NewReference'.
- varchar(255) 'Data'.
+ varchar(100) 'Data'.
=cut
@@ -110,30 +83,24 @@ Create takes a hash of values and creates a row in the database:
sub Create {
my $self = shift;
my %args = (
- ObjectType => '',
- ObjectId => '0',
+ EffectiveTicket => '0',
+ Ticket => '0',
TimeTaken => '0',
Type => '',
Field => '',
OldValue => '',
NewValue => '',
- ReferenceType => '',
- OldReference => '',
- NewReference => '',
Data => '',
@_);
$self->SUPER::Create(
- ObjectType => $args{'ObjectType'},
- ObjectId => $args{'ObjectId'},
+ EffectiveTicket => $args{'EffectiveTicket'},
+ Ticket => $args{'Ticket'},
TimeTaken => $args{'TimeTaken'},
Type => $args{'Type'},
Field => $args{'Field'},
OldValue => $args{'OldValue'},
NewValue => $args{'NewValue'},
- ReferenceType => $args{'ReferenceType'},
- OldReference => $args{'OldReference'},
- NewReference => $args{'NewReference'},
Data => $args{'Data'},
);
@@ -141,7 +108,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -150,50 +117,64 @@ Returns the current value of id.
=cut
-=head2 ObjectType
+=item EffectiveTicket
-Returns the current value of ObjectType.
-(In the database, ObjectType is stored as varchar(64).)
+Returns the current value of EffectiveTicket.
+(In the database, EffectiveTicket is stored as int(11).)
-=head2 SetObjectType VALUE
+=item SetEffectiveTicket VALUE
-Set ObjectType to VALUE.
+Set EffectiveTicket to VALUE.
Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, ObjectType will be stored as a varchar(64).)
+(In the database, EffectiveTicket will be stored as a int(11).)
=cut
-=head2 ObjectId
+=item Ticket
-Returns the current value of ObjectId.
-(In the database, ObjectId is stored as int(11).)
+Returns the current value of Ticket.
+(In the database, Ticket is stored as int(11).)
-=head2 SetObjectId VALUE
+=item SetTicket VALUE
-Set ObjectId to VALUE.
+Set Ticket to VALUE.
Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, ObjectId will be stored as a int(11).)
+(In the database, Ticket will be stored as a int(11).)
=cut
-=head2 TimeTaken
+=item TicketObj
+
+Returns the Ticket Object which has the id returned by Ticket
+
+
+=cut
+
+sub TicketObj {
+ my $self = shift;
+ my $Ticket = RT::Ticket->new($self->CurrentUser);
+ $Ticket->Load($self->__Value('Ticket'));
+ return($Ticket);
+}
+
+=item TimeTaken
Returns the current value of TimeTaken.
(In the database, TimeTaken is stored as int(11).)
-=head2 SetTimeTaken VALUE
+=item SetTimeTaken VALUE
Set TimeTaken to VALUE.
@@ -204,14 +185,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(20).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -222,14 +203,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Field
+=item Field
Returns the current value of Field.
(In the database, Field is stored as varchar(40).)
-=head2 SetField VALUE
+=item SetField VALUE
Set Field to VALUE.
@@ -240,14 +221,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 OldValue
+=item OldValue
Returns the current value of OldValue.
(In the database, OldValue is stored as varchar(255).)
-=head2 SetOldValue VALUE
+=item SetOldValue VALUE
Set OldValue to VALUE.
@@ -258,14 +239,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 NewValue
+=item NewValue
Returns the current value of NewValue.
(In the database, NewValue is stored as varchar(255).)
-=head2 SetNewValue VALUE
+=item SetNewValue VALUE
Set NewValue to VALUE.
@@ -276,79 +257,25 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ReferenceType
-
-Returns the current value of ReferenceType.
-(In the database, ReferenceType is stored as varchar(255).)
-
-
-
-=head2 SetReferenceType VALUE
-
-
-Set ReferenceType to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, ReferenceType will be stored as a varchar(255).)
-
-
-=cut
-
-
-=head2 OldReference
-
-Returns the current value of OldReference.
-(In the database, OldReference is stored as int(11).)
-
-
-
-=head2 SetOldReference VALUE
-
-
-Set OldReference to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, OldReference will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 NewReference
-
-Returns the current value of NewReference.
-(In the database, NewReference is stored as int(11).)
-
-
-
-=head2 SetNewReference VALUE
-
-
-Set NewReference to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, NewReference will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 Data
+=item Data
Returns the current value of Data.
-(In the database, Data is stored as varchar(255).)
+(In the database, Data is stored as varchar(100).)
-=head2 SetData VALUE
+=item SetData VALUE
Set Data to VALUE.
Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Data will be stored as a varchar(255).)
+(In the database, Data will be stored as a varchar(100).)
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -357,7 +284,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -367,37 +294,31 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
- ObjectType =>
- {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
- ObjectId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, type => 'int(11)', default => ''},
+ EffectiveTicket =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Ticket =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
TimeTaken =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 20, is_blob => 0, is_numeric => 0, type => 'varchar(20)', default => ''},
+ {read => 1, write => 1, type => 'varchar(20)', default => ''},
Field =>
- {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+ {read => 1, write => 1, type => 'varchar(40)', default => ''},
OldValue =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
NewValue =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
- ReferenceType =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
- OldReference =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
- NewReference =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Data =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -429,7 +350,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Transaction_Overlay.pm b/rt/lib/RT/Transaction_Overlay.pm
index 96ccf51d4..1a5473b3c 100644
--- a/rt/lib/RT/Transaction_Overlay.pm
+++ b/rt/lib/RT/Transaction_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Transaction - RT\'s transaction object
@@ -65,6 +64,11 @@ It can have arbitrary MIME attachments.
=head1 METHODS
+=begin testing
+
+ok(require RT::Transaction);
+
+=end testing
=cut
@@ -78,11 +82,11 @@ use vars qw( %_BriefDescriptions $PreferredContentType );
use RT::Attachments;
use RT::Scrips;
-use RT::Ruleset;
use HTML::FormatText;
use HTML::TreeBuilder;
+
# {{{ sub Create
=head2 Create
@@ -172,21 +176,9 @@ sub Create {
Ticket => $args{'ObjectId'},
Transaction => $self->id,
);
-
- # Entry point of the rule system
- my $ticket = RT::Ticket->new($RT::SystemUser);
- $ticket->Load($args{'ObjectId'});
- my $rules = RT::Ruleset->FindAllRules(
- Stage => 'TransactionCreate',
- Type => $args{'Type'},
- TicketObj => $ticket,
- TransactionObj => $self,
- );
-
if ($args{'CommitScrips'} ) {
$RT::Logger->debug('About to commit scrips for transaction #' .$self->Id);
$self->{'scrips'}->Commit();
- RT::Ruleset->CommitRules($rules);
}
}
@@ -251,28 +243,26 @@ sub Delete {
=head2 Message
-Returns the L<RT::Attachments> object which contains the "top-level" object
-attachment for this transaction.
+ Returns the RT::Attachments Object which contains the "top-level"object
+ attachment for this transaction
=cut
sub Message {
- my $self = shift;
- # XXX: Where is ACL check?
+ my $self = shift;
- unless ( defined $self->{'message'} ) {
+ if ( !defined( $self->{'message'} ) ) {
- $self->{'message'} = RT::Attachments->new( $self->CurrentUser );
+ $self->{'message'} = new RT::Attachments( $self->CurrentUser );
$self->{'message'}->Limit(
FIELD => 'TransactionId',
VALUE => $self->Id
);
+
$self->{'message'}->ChildrenOf(0);
- } else {
- $self->{'message'}->GotoFirstItem;
}
- return $self->{'message'};
+ return ( $self->{'message'} );
}
# }}}
@@ -286,43 +276,43 @@ textual part (as defined in RT::I18N::IsTextualContentType). Otherwise,
returns undef.
Takes a paramhash. If the $args{'Quote'} parameter is set, wraps this message
-at $args{'Wrap'}. $args{'Wrap'} defaults to 70.
+at $args{'Wrap'}. $args{'Wrap'} defaults to $RT::MessageBoxWidth - 2 or 70.
-If $args{'Type'} is set to C<text/html>, this will return an HTML
-part of the message, if available. Otherwise it looks for a text/plain
-part. If $args{'Type'} is missing, it defaults to the value of
-C<$RT::Transaction::PreferredContentType>, if that's missing too,
-defaults to 'text/plain'.
+If $args{'Type'} is set to C<text/html>, plain texts are upgraded to HTML.
+Otherwise, HTML texts are downgraded to plain text. If $args{'Type'} is
+missing, it defaults to the value of C<$RT::Transaction::PreferredContentType>.
=cut
sub Content {
my $self = shift;
my %args = (
- Type => $PreferredContentType || 'text/plain',
+ Type => $PreferredContentType,
Quote => 0,
Wrap => 70,
+ Wrap => ( $RT::MessageBoxWidth || 72 ) - 2,
@_
);
my $content;
- if ( my $content_obj = $self->ContentObj( Type => $args{Type} ) ) {
- $content = $content_obj->Content ||'';
+ if (my $content_obj = $self->ContentObj) {
+ $content = $content_obj->Content;
- if ( lc $content_obj->ContentType eq 'text/html' ) {
+ if ($content_obj->ContentType =~ m{^text/html$}i) {
$content =~ s/<p>--\s+<br \/>.*?$//s if $args{'Quote'};
if ($args{Type} ne 'text/html') {
- my $tree = HTML::TreeBuilder->new_from_content( $content );
$content = HTML::FormatText->new(
leftmargin => 0,
rightmargin => 78,
- )->format( $tree);
- $tree->delete;
+ )->format(
+ HTML::TreeBuilder->new_from_content( $content )
+ );
}
- }
+ }
else {
$content =~ s/\n-- \n.*?$//s if $args{'Quote'};
+
if ($args{Type} eq 'text/html') {
# Extremely simple text->html converter
$content =~ s/&/&#38;/g;
@@ -343,10 +333,10 @@ sub Content {
# What's the longest line like?
my $max = 0;
foreach ( split ( /\n/, $content ) ) {
- $max = length if length > $max;
+ $max = length if ( length > $max );
}
- if ( $max > 76 ) {
+ if ( $max > $args{'Wrap'}+6 ) { # 76 ) {
require Text::Wrapper;
my $wrapper = new Text::Wrapper(
columns => $args{'Wrap'},
@@ -357,7 +347,7 @@ sub Content {
}
$content =~ s/^/> /gm;
- $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name)
+ $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString(), $self->CreatorObj->Name())
. "\n$content\n\n";
}
@@ -366,26 +356,6 @@ sub Content {
# }}}
-
-=head2 Addresses
-
-Returns a hashref of addresses related to this transaction. See L<RT::Attachment/Addresses> for details.
-
-=cut
-
-sub Addresses {
- my $self = shift;
-
- if (my $attach = $self->Attachments->First) {
- return $attach->Addresses;
- }
- else {
- return {};
- }
-
-}
-
-
# {{{ ContentObj
=head2 ContentObj
@@ -396,13 +366,16 @@ Returns the RT::Attachment object which contains the content for this Transactio
sub ContentObj {
+
my $self = shift;
- my %args = ( Type => $PreferredContentType || 'text/plain',
- @_ );
- # If we don't have any content, return undef now.
+ # If we don\'t have any content, return undef now.
+ unless ( $self->Attachments->First ) {
+ return (undef);
+ }
+
# Get the set of toplevel attachments to this transaction.
- return undef unless my $Attachment = $self->Attachments->First;
+ my $Attachment = $self->Attachments->First();
# If it's a textual part, just return the body.
if ( RT::I18N::IsTextualContentType($Attachment->ContentType) ) {
@@ -412,23 +385,27 @@ sub ContentObj {
# If it's a multipart object, first try returning the first part with preferred
# MIME type ('text/plain' by default).
- elsif ( $Attachment->ContentType =~ '^multipart/' ) {
- my $plain_parts = $Attachment->Children;
- $plain_parts->ContentType( VALUE => $args{Type} );
- $plain_parts->LimitNotEmpty;
+ elsif ( $Attachment->ContentType() =~ '^multipart/' ) {
+ my $plain_parts = $Attachment->Children();
+ $plain_parts->ContentType( VALUE => ($PreferredContentType || 'text/plain') );
# If we actully found a part, return its content
- if ( my $first = $plain_parts->First ) {
- return $first;
+ if ( $plain_parts->First && $plain_parts->First->Content ne '' ) {
+ return ( $plain_parts->First );
}
+
# If that fails, return the first textual part which has some content.
- my $all_parts = $self->Attachments;
- while ( my $part = $all_parts->Next ) {
- next unless RT::I18N::IsTextualContentType($part->ContentType)
- && $part->Content;
- return $part;
+
+ else {
+ my $all_parts = $self->Attachments();
+ while ( my $part = $all_parts->Next ) {
+ if ( ( RT::I18N::IsTextualContentType($part->ContentType) ) and ( $part->Content() ne '' ) ) {
+ return ($part);
+ }
+ }
}
+
}
# We found no content. suck
@@ -448,8 +425,12 @@ Otherwise, returns null
sub Subject {
my $self = shift;
- return undef unless my $first = $self->Attachments->First;
- return $first->Subject;
+ if ( $self->Attachments->First ) {
+ return ( $self->Attachments->First->Subject );
+ }
+ else {
+ return (undef);
+ }
}
# }}}
@@ -458,7 +439,7 @@ sub Subject {
=head2 Attachments
-Returns all the RT::Attachment objects which are attached
+ Returns all the RT::Attachment objects which are attached
to this transaction. Takes an optional parameter, which is
a ContentType that Attachments should be restricted to.
@@ -467,28 +448,38 @@ a ContentType that Attachments should be restricted to.
sub Attachments {
my $self = shift;
- if ( $self->{'attachments'} ) {
- $self->{'attachments'}->GotoFirstItem;
- return $self->{'attachments'};
- }
+ unless ( $self->{'attachments'} ) {
+ $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
- $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
+ #If it's a comment, return an empty object if they don't have the right to see it
+ if ( $self->Type eq 'Comment' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->{'attachments'} );
+ }
+ }
- unless ( $self->CurrentUserCanSee ) {
- $self->{'attachments'}->Limit(FIELD => 'id', VALUE => '0');
- return $self->{'attachments'};
- }
+ #if they ain't got rights to see, return an empty object
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->{'attachments'} );
+ }
+ }
+
+ $self->{'attachments'}->Limit( FIELD => 'TransactionId',
+ VALUE => $self->Id );
- $self->{'attachments'}->Limit( FIELD => 'TransactionId', VALUE => $self->Id );
+ # Get the self->{'attachments'} in the order they're put into
+ # the database. Arguably, we should be returning a tree
+ # of self->{'attachments'}, not a set...but no current app seems to need
+ # it.
- # Get the self->{'attachments'} in the order they're put into
- # the database. Arguably, we should be returning a tree
- # of self->{'attachments'}, not a set...but no current app seems to need
- # it.
+ $self->{'attachments'}->OrderBy( ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'asc' );
- $self->{'attachments'}->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+ }
+ return ( $self->{'attachments'} );
- return $self->{'attachments'};
}
# }}}
@@ -505,70 +496,26 @@ sub _Attach {
my $self = shift;
my $MIMEObject = shift;
- unless ( defined $MIMEObject ) {
- $RT::Logger->error("We can't attach a mime object if you don't give us one.");
+ if ( !defined($MIMEObject) ) {
+ $RT::Logger->error(
+"$self _Attach: We can't attach a mime object if you don't give us one.\n"
+ );
return ( 0, $self->loc("[_1]: no attachment specified", $self) );
}
- my $Attachment = RT::Attachment->new( $self->CurrentUser );
+ my $Attachment = new RT::Attachment( $self->CurrentUser );
my ($id, $msg) = $Attachment->Create(
TransactionId => $self->Id,
Attachment => $MIMEObject
);
return ( $Attachment, $msg || $self->loc("Attachment created") );
+
}
# }}}
# }}}
-sub ContentAsMIME {
- my $self = shift;
-
- my $main_content = $self->ContentObj;
- my $entity = $main_content->ContentAsMIME;
-
- if ( $main_content->Parent ) {
- # main content is not top most entity, we shouldn't loose
- # From/To/Cc headers that are on a top part
- my $attachments = RT::Attachments->new( $self->CurrentUser );
- $attachments->Columns(qw(id Parent TransactionId Headers));
- $attachments->Limit( FIELD => 'TransactionId', VALUE => $self->id );
- $attachments->Limit( FIELD => 'Parent', VALUE => 0 );
- $attachments->Limit( FIELD => 'Parent', OPERATOR => 'IS', VALUE => 'NULL', QUOTEVALUE => 0 );
- $attachments->OrderBy( FIELD => 'id', ORDER => 'ASC' );
- my $tmp = $attachments->First;
- if ( $tmp && $tmp->id ne $main_content->id ) {
- $entity->make_multipart;
- $entity->head->add( split /:/, $_, 2 ) foreach $tmp->SplitHeaders;
- $entity->make_singlepart;
- }
- }
-
- my $attachments = RT::Attachments->new( $self->CurrentUser );
- $attachments->Limit( FIELD => 'TransactionId', VALUE => $self->id );
- $attachments->Limit(
- FIELD => 'id',
- OPERATOR => '!=',
- VALUE => $main_content->id,
- );
- $attachments->Limit(
- FIELD => 'ContentType',
- OPERATOR => 'NOT STARTSWITH',
- VALUE => 'multipart/',
- );
- $attachments->Limit(
- FIELD => 'Content',
- OPERATOR => '!=',
- VALUE => '',
- );
- while ( my $a = $attachments->Next ) {
- $entity->make_multipart unless $entity->is_multipart;
- $entity->add_part( $a->ContentAsMIME );
- }
- return $entity;
-}
-
# {{{ Routines dealing with Transaction Attributes
# {{{ sub Description
@@ -582,15 +529,28 @@ Returns a text string which describes this transaction
sub Description {
my $self = shift;
- unless ( $self->CurrentUserCanSee ) {
- return ( $self->loc("Permission Denied") );
+ #Check those ACLs
+ #If it's a comment or a comment email record,
+ # we need to be extra special careful
+
+ if ( $self->__Value('Type') =~ /^Comment/ ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->loc("Permission Denied") );
+ }
}
- unless ( defined $self->Type ) {
+ #if they ain't got rights to see, don't let em
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ($self->loc("Permission Denied") );
+ }
+ }
+
+ if ( !defined( $self->Type ) ) {
return ( $self->loc("No transaction type specified"));
}
- return $self->loc("[_1] by [_2]", $self->BriefDescription , $self->CreatorObj->Name );
+ return ( $self->loc("[_1] by [_2]",$self->BriefDescription , $self->CreatorObj->Name ));
}
# }}}
@@ -606,13 +566,24 @@ Returns a text string which briefly describes this transaction
sub BriefDescription {
my $self = shift;
- unless ( $self->CurrentUserCanSee ) {
- return ( $self->loc("Permission Denied") );
+ #If it's a comment or a comment email record,
+ # we need to be extra special careful
+ if ( $self->__Value('Type') =~ /^Comment/ ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->loc("Permission Denied") );
+ }
+ }
+
+ #if they ain't got rights to see, don't let em
+ elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->loc("Permission Denied") );
+ }
}
my $type = $self->Type; #cache this, rather than calling it 30 times
- unless ( defined $type ) {
+ if ( !defined($type) ) {
return $self->loc("No transaction type specified");
}
@@ -621,12 +592,6 @@ sub BriefDescription {
if ( $type eq 'Create' ) {
return ( $self->loc( "[_1] created", $obj_type ) );
}
- elsif ( $type eq 'Enabled' ) {
- return ( $self->loc( "[_1] enabled", $obj_type ) );
- }
- elsif ( $type eq 'Disabled' ) {
- return ( $self->loc( "[_1] disabled", $obj_type ) );
- }
elsif ( $type =~ /Status/ ) {
if ( $self->Field eq 'Status' ) {
if ( $self->NewValue eq 'deleted' ) {
@@ -700,10 +665,10 @@ sub BriefDescription {
$field = $cf->Name();
}
- if ( ! defined $self->OldValue || $self->OldValue eq '' ) {
+ if ( $self->OldValue eq '' ) {
return ( $self->loc("[_1] [_2] added", $field, $self->NewValue) );
}
- elsif ( !defined $self->NewValue || $self->NewValue eq '' ) {
+ elsif ( $self->NewValue eq '' ) {
return ( $self->loc("[_1] [_2] deleted", $field, $self->OldValue) );
}
@@ -832,21 +797,6 @@ sub BriefDescription {
return ( $self->Data );
}
},
- Told => sub {
- my $self = shift;
- if ( $self->Field eq 'Told' ) {
- my $t1 = new RT::Date($self->CurrentUser);
- $t1->Set(Format => 'ISO', Value => $self->NewValue);
- my $t2 = new RT::Date($self->CurrentUser);
- $t2->Set(Format => 'ISO', Value => $self->OldValue);
- return $self->loc( "[_1] changed from [_2] to [_3]", $self->loc($self->Field), $t2->AsString, $t1->AsString );
- }
- else {
- return $self->loc( "[_1] changed from [_2] to [_3]",
- $self->loc($self->Field),
- ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
- }
- },
Set => sub {
my $self = shift;
if ( $self->Field eq 'Password' ) {
@@ -857,8 +807,7 @@ sub BriefDescription {
$q1->Load( $self->OldValue );
my $q2 = new RT::Queue( $self->CurrentUser );
$q2->Load( $self->NewValue );
- return $self->loc("[_1] changed from [_2] to [_3]",
- $self->loc($self->Field) , $q1->Name , $q2->Name);
+ return $self->loc("[_1] changed from [_2] to [_3]", $self->Field , $q1->Name , $q2->Name);
}
# Write the date/time change at local time:
@@ -867,12 +816,10 @@ sub BriefDescription {
$t1->Set(Format => 'ISO', Value => $self->NewValue);
my $t2 = new RT::Date($self->CurrentUser);
$t2->Set(Format => 'ISO', Value => $self->OldValue);
- return $self->loc( "[_1] changed from [_2] to [_3]", $self->loc($self->Field), $t2->AsString, $t1->AsString );
+ return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $t2->AsString, $t1->AsString );
}
else {
- return $self->loc( "[_1] changed from [_2] to [_3]",
- $self->loc($self->Field),
- ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
+ return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
}
},
PurgeTransaction => sub {
@@ -957,19 +904,52 @@ Returns its value as a string, if the user passes an ACL check
=cut
sub _Value {
+
my $self = shift;
my $field = shift;
#if the field is public, return it.
if ( $self->_Accessible( $field, 'public' ) ) {
- return $self->SUPER::_Value( $field );
+ return ( $self->__Value($field) );
+
+ }
+
+ #If it's a comment, we need to be extra special careful
+ if ( $self->__Value('Type') eq 'Comment' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return (undef);
+ }
+ }
+ elsif ( $self->__Value('Type') eq 'CommentEmailRecord' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments')
+ && $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
+ return (undef);
+ }
+
}
+ elsif ( $self->__Value('Type') eq 'EmailRecord' ) {
+ unless ( $self->CurrentUserHasRight('ShowOutgoingEmail')) {
+ return (undef);
+ }
- unless ( $self->CurrentUserCanSee ) {
- return undef;
+ }
+ # Make sure the user can see the custom field before showing that it changed
+ elsif ( ( $self->__Value('Type') eq 'CustomField' ) && $self->__Value('Field') ) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->Load( $self->__Value('Field') );
+ return (undef) unless ( $cf->CurrentUserHasRight('SeeCustomField') );
+ }
+
+
+ #if they ain't got rights to see, don't let em
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return (undef);
+ }
}
- return $self->SUPER::_Value( $field );
+ return ( $self->__Value($field) );
+
}
# }}}
@@ -986,60 +966,14 @@ passed in here.
sub CurrentUserHasRight {
my $self = shift;
my $right = shift;
- return $self->CurrentUser->HasRight(
- Right => $right,
- Object => $self->Object
+ return (
+ $self->CurrentUser->HasRight(
+ Right => "$right",
+ Object => $self->TicketObj
+ )
);
}
-=head2 CurrentUserCanSee
-
-Returns true if current user has rights to see this particular transaction.
-
-This fact depends on type of the transaction, type of an object the transaction
-is attached to and may be other conditions, so this method is prefered over
-custom implementations.
-
-=cut
-
-sub CurrentUserCanSee {
- my $self = shift;
-
- # If it's a comment, we need to be extra special careful
- my $type = $self->__Value('Type');
- if ( $type eq 'Comment' ) {
- unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
- return 0;
- }
- }
- elsif ( $type eq 'CommentEmailRecord' ) {
- unless ( $self->CurrentUserHasRight('ShowTicketComments')
- && $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
- return 0;
- }
- }
- elsif ( $type eq 'EmailRecord' ) {
- unless ( $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
- return 0;
- }
- }
- # Make sure the user can see the custom field before showing that it changed
- elsif ( $type eq 'CustomField' and my $cf_id = $self->__Value('Field') ) {
- my $cf = RT::CustomField->new( $self->CurrentUser );
- $cf->SetContextObject( $self->Object );
- $cf->Load( $cf_id );
- return 0 unless $cf->CurrentUserHasRight('SeeCustomField');
- }
- #if they ain't got rights to see, don't let em
- elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) {
- unless ( $self->CurrentUserHasRight('ShowTicket') ) {
- return 0;
- }
- }
-
- return 1;
-}
-
# }}}
sub Ticket {
@@ -1084,7 +1018,7 @@ sub Object {
my $self = shift;
my $Object = $self->__Value('ObjectType')->new($self->CurrentUser);
$Object->Load($self->__Value('ObjectId'));
- return $Object;
+ return($Object);
}
sub FriendlyObjectType {
@@ -1114,6 +1048,7 @@ sub UpdateCustomFields {
# value "ARGSRef", which was a reference to a hash of arguments.
# This was insane. The next few lines of code preserve that API
# while giving us something saner.
+
# TODO: 3.6: DEPRECATE OLD API
@@ -1161,11 +1096,9 @@ sub CustomFieldValues {
if ( UNIVERSAL::can( $self->Object, 'QueueObj' ) ) {
- # XXX: $field could be undef when we want fetch values for all CFs
- # do we want to cover this situation somehow here?
unless ( defined $field && $field =~ /^\d+$/o ) {
my $CFs = RT::CustomFields->new( $self->CurrentUser );
- $CFs->Limit( FIELD => 'Name', VALUE => $field );
+ $CFs->Limit( FIELD => 'Name', VALUE => $field);
$CFs->LimitToLookupType($self->CustomFieldLookupType);
$CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
$field = $CFs->First->id if $CFs->First;
@@ -1191,54 +1124,7 @@ sub CustomFieldLookupType {
"RT::Queue-RT::Ticket-RT::Transaction";
}
-
-=head2 DeferredRecipients($freq, $include_sent )
-
-Takes the following arguments:
-
-=over
-
-=item * a string to indicate the frequency of digest delivery. Valid values are "daily", "weekly", or "susp".
-
-=item * an optional argument which, if true, will return addresses even if this notification has been marked as 'sent' for this transaction.
-
-=back
-
-Returns an array of users who should now receive the notification that
-was recorded in this transaction. Returns an empty array if there were
-no deferred users, or if $include_sent was not specified and the deferred
-notifications have been sent.
-
-=cut
-
-sub DeferredRecipients {
- my $self = shift;
- my $freq = shift;
- my $include_sent = @_? shift : 0;
-
- my $attr = $self->FirstAttribute('DeferredRecipients');
-
- return () unless ($attr);
-
- my $deferred = $attr->Content;
-
- return () unless ( ref($deferred) eq 'HASH' && exists $deferred->{$freq} );
-
- # Skip it.
-
- for my $user (keys %{$deferred->{$freq}}) {
- if ($deferred->{$freq}->{$user}->{_sent} && !$include_sent) {
- delete $deferred->{$freq}->{$user}
- }
- }
- # Now get our users. Easy.
-
- return keys %{ $deferred->{$freq} };
-}
-
-
-
-# Transactions don't change. by adding this cache config directive, we don't lose pathalogically on long tickets.
+# Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets.
sub _CacheConfig {
{
'cache_p' => 1,
@@ -1246,27 +1132,4 @@ sub _CacheConfig {
'cache_for_sec' => 6000,
}
}
-
-
-=head2 ACLEquivalenceObjects
-
-This method returns a list of objects for which a user's rights also apply
-to this Transaction.
-
-This currently only applies to Transaction Custom Fields on Tickets, so we return
-the Ticket's Queue and the Ticket.
-
-This method is called from L<RT::Principal/HasRight>.
-
-=cut
-
-sub ACLEquivalenceObjects {
- my $self = shift;
-
- return unless $self->ObjectType eq 'RT::Ticket';
- my $object = $self->Object;
- return $object,$object->QueueObj;
-
-}
-
1;
diff --git a/rt/lib/RT/Transactions.pm b/rt/lib/RT/Transactions.pm
index deae6ae27..23a475ac6 100755
--- a/rt/lib/RT/Transactions.pm
+++ b/rt/lib/RT/Transactions.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Transaction item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/URI/freeside.pm b/rt/lib/RT/URI/freeside.pm
new file mode 100644
index 000000000..d73dbacad
--- /dev/null
+++ b/rt/lib/RT/URI/freeside.pm
@@ -0,0 +1,285 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# 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.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+package RT::URI::freeside;
+
+use RT::URI::base;
+use strict;
+use vars qw(@ISA $IntegrationType $URL);
+@ISA = qw/RT::URI::base/;
+
+
+=head1 NAME
+
+RT::URI::freeside
+
+=head1 DESCRIPTION
+
+URI handler for Freeside URIs. See http://www.freeside.biz/ for more
+information on Freeside.
+
+
+=head1 Public subroutines
+
+=over 4
+
+=item FreesideGetConfig CONFKEY
+
+Subroutine that returns the freeside's configuration value(s) for CONFKEY
+as a scalar or list.
+
+=cut
+
+sub FreesideGetConfig { return undef; }
+
+
+=item FreesideURL
+
+Returns the URL for freeside's web interface.
+
+=cut
+
+sub FreesideURL { return $URL; }
+
+
+=item FreesideVersion
+
+Returns a string describing the freeside version being used.
+
+=cut
+
+sub FreesideVersion { return undef; }
+
+
+=item smart_search
+
+A wrapper for the FS::cust_main::smart_search subroutine.
+
+=cut
+
+sub smart_search { return undef; }
+
+
+=item small_custview
+
+A wrapper for the FS::CGI::small_custview subroutine.
+
+=cut
+
+sub small_custview { return 'Freeside integration error!</A>'; }
+
+
+=back
+
+=head1 Private methods
+
+=over 4
+
+=item _FreesideGetRecord
+
+Method returns a hashref of the freeside record referenced in the URI.
+Must be called after ParseURI.
+
+=cut
+
+sub _FreesideGetRecord { return undef; }
+
+
+=item _FreesideURIPrefix
+
+Method that returns the URI prefix for freeside URIs.
+
+=cut
+
+sub _FreesideURIPrefix {
+
+ my $self = shift;
+ return($self->Scheme . '://freeside');
+
+}
+
+=item _FreesideURILabel
+
+Method that returns a short string describing the customer referenced
+in the URI.
+
+=cut
+
+sub _FreesideURILabel {
+
+ my $self = shift;
+
+ $RT::Logger->debug("Called _FreesideURILabel()");
+
+ return unless (exists($self->{'fstable'}) and
+ exists($self->{'fspkey'}));
+
+ my $label;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+ if ($table ne 'cust_main') {
+ warn "FS::${table} not currently supported";
+ return;
+ }
+
+ my $rec = $self->_FreesideGetRecord();
+
+ if (ref($rec) eq 'HASH' and $table eq 'cust_main') {
+ my $name = $rec->{'last'} . ', ' . $rec->{'first'};
+ $name = $rec->{'company'} . " ($name)" if $rec->{'company'};
+ $label = "$pkey: $name";
+ } else {
+ $label = "$pkey: $table";
+ }
+
+ if ($label and !$@) {
+ return($label);
+ } else {
+ return;
+ }
+
+}
+
+=item _FreesideURILabelLong
+
+Method that returns a longer string describing the customer referenced
+in the URI.
+
+=cut
+
+sub _FreesideURILabelLong {
+
+ my $self = shift;
+
+ return $self->_FreesideURILabel();
+
+}
+
+=back
+
+=head1 Public methods
+
+=over 4
+
+=cut
+
+sub ParseURI {
+ my $self = shift;
+ my $uri = shift;
+ my ($table, $pkey);
+
+ my $uriprefix = $self->_FreesideURIPrefix;
+ if ($uri =~ /^$uriprefix\/(\w+)\/(\d+)$/) {
+ $table = $1;
+ $pkey = $2;
+ $self->{'scheme'} = $self->Scheme;
+ } else {
+ return(undef);
+ }
+
+ $self->{'uri'} = "${uriprefix}/${table}/${pkey}";
+ $self->{'fstable'} = $table;
+ $self->{'fspkey'} = $pkey;
+
+
+ my $url = $self->FreesideURL();
+
+ if ($url ne '') {
+ $self->{'href'} = "${url}/view/${table}.cgi?${pkey}";
+ } else {
+ $self->{'href'} = $self->{'uri'};
+ }
+
+ $self->{'uri'};
+
+}
+
+sub Scheme {
+ my $self = shift;
+ return('freeside');
+
+}
+
+sub HREF {
+ my $self = shift;
+ return($self->{'href'} || $self->{'uri'});
+}
+
+sub IsLocal {
+ my $self = shift;
+ return undef;
+}
+
+=item AsString
+
+Return a "pretty" string representing the URI object.
+
+This is meant to be used like this:
+
+ % $re = $uri->Resolver;
+ <A HREF="<% $re->HREF %>"><% $re->AsString %></A>
+
+=cut
+
+sub AsString {
+ my $self = shift;
+ my $prettystring;
+ if ($prettystring = $self->_FreesideURILabel) {
+ return $prettystring;
+ } else {
+ return $self->URI;
+ }
+}
+
+=item AsStringLong
+
+Return a longer (HTML) string representing the URI object.
+
+=cut
+
+sub AsStringLong {
+ my $self = shift;
+ my $prettystring;
+ if ($prettystring = $self->_FreesideURILabelLong || $self->_FreesideURILabel){
+ return $prettystring;
+ } else {
+ return $self->URI;
+ }
+}
+
+$IntegrationType ||= 'Internal';
+eval "require RT::URI::freeside::${RT::URI::freeside::IntegrationType}";
+warn $@ if $@;
+if ($@ &&
+ $@ !~ qr(^Can't locate RT/URI/freeside/${RT::URI::freeside::IntegrationType}.pm)) {
+ die $@;
+};
+
+=back
+
+=cut
+
+1;
diff --git a/rt/lib/RT/URI/freeside/Internal.pm b/rt/lib/RT/URI/freeside/Internal.pm
new file mode 100644
index 000000000..bd7c42ccf
--- /dev/null
+++ b/rt/lib/RT/URI/freeside/Internal.pm
@@ -0,0 +1,145 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# 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.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+#
+use strict;
+no warnings qw(redefine);
+
+#use vars qw($conf);
+
+use FS;
+use FS::UID qw(dbh);
+use FS::CGI qw(popurl);
+use FS::UI::Web::small_custview qw(small_custview);
+use FS::Conf;
+use FS::Record qw(qsearchs qsearch dbdef);
+use FS::cust_main;
+use FS::cust_svc;
+
+=head1 NAME
+
+RT::URI::freeside::Internal
+
+=head1 DESCRIPTION
+
+Overlay for the RT::URI::freeside URI handler implementing the Internal integration type.
+
+See L<RT::URI::freeside> for public/private interface documentation.
+
+=cut
+
+
+
+sub _FreesideGetRecord {
+
+ my $self = shift;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+ $RT::Logger->debug("Called _FreesideGetRecord()");
+
+ #eval "use FS::$table;";
+
+ my $dbdef = dbdef;
+ unless ($dbdef) {
+ $RT::Logger->error("Using Internal freeside integration type, ".
+ "but it doesn't look like we're running under ".
+ "freeside's Mason handler.");
+ return;
+ }
+
+ my $pkeyfield = $dbdef->table($table)->primary_key;
+ unless ($pkeyfield) {
+ $RT::Logger->error("No primary key for freeside table '$table'");
+ return;
+ }
+
+ my $fsrec = qsearchs($table, { $pkeyfield => $pkey });
+ unless ($fsrec) {
+ $RT::Logger->error("Record with '$pkeyfield' == '$pkey' does " .
+ "not exist in table $table");
+ return;
+ }
+
+ return { $fsrec->hash, '_object' => $fsrec };
+
+}
+
+sub FreesideVersion {
+
+ return $FS::VERSION;
+
+}
+
+sub FreesideGetConfig {
+
+ #$conf = new FS::Conf unless ref($conf);
+ my $conf = new FS::Conf;
+
+ return scalar($conf->config(@_));
+
+}
+
+sub smart_search { #Subroutine
+
+ return map { { $_->hash } } &FS::cust_main::smart_search(@_);
+
+}
+
+sub email_search { #Subroutine
+
+ return map { { $_->hash } } &FS::cust_main::email_search(@_);
+
+}
+
+sub small_custview {
+
+ return &FS::UI::Web::small_custview::small_custview(@_);
+
+}
+
+sub _FreesideURILabelLong {
+
+ my $self = shift;
+
+ my $table = $self->{'fstable'};
+
+ if ( $table eq 'cust_main' ) {
+
+ my $rec = $self->_FreesideGetRecord();
+ return small_custview( $rec->{'_object'},
+ scalar(FS::Conf->new->config('countrydefault')),
+ 1 #nobalance
+ );
+
+ } else {
+
+ return $self->_FreesideURILabel();
+
+ }
+
+}
+
+1;
diff --git a/rt/lib/RT/URI/freeside/XMLRPC.pm b/rt/lib/RT/URI/freeside/XMLRPC.pm
new file mode 100644
index 000000000..916c20d7b
--- /dev/null
+++ b/rt/lib/RT/URI/freeside/XMLRPC.pm
@@ -0,0 +1,122 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# 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.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+
+use strict;
+no warnings qw(redefine);
+
+use vars qw($XMLRPC_URL $_FS_VERSION);
+
+use Frontier::Client;
+
+=head1 NAME
+
+RT::URI::freeside::XMLRPC
+
+=head1 DESCRIPTION
+
+Overlay for the RT::URI::freeside URI handler implementing the XMLRPC integration type.
+
+See L<RT::URI::freeside> for public/private interface documentation.
+
+=cut
+
+
+sub _XMLRPCRequest { #Subroutine
+
+ my $method = shift;
+ my @args = @_;
+
+ my $result;
+ eval {
+ my $server = new Frontier::Client ( url => $XMLRPC_URL );
+ $result = $server->call($method, @args);
+ };
+
+ if (not $@ and ref($result) eq 'ARRAY') {
+ return (scalar(@$result) == 1) ? @$result[0] : @$result;
+ } else {
+ $RT::Logger->debug("Freeside XMLRPC: " . $result || $@);
+ return ();
+ }
+
+}
+
+sub _FreesideGetRecord {
+
+ my $self = shift;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+ my $record;
+
+ $RT::Logger->debug("Called XMLRPC::_FreesideGetRecord()");
+
+ #FIXME: Need a better way to get primary keys.
+ # Maybe create a method for it and cache them like version?
+ my %table_pkeys = (
+ cust_main => 'custnum',
+ );
+
+ my $method = 'Record.qsearchs';
+ my @args = ($table, { $table_pkeys{$table} => $pkey });
+ my ($record) = &_XMLRPCRequest($method, @args);
+
+ return $record;
+
+}
+
+
+sub FreesideGetConfig {
+
+ return _XMLRPCRequest('Conf.config', @_);
+
+}
+
+
+sub FreesideVersion {
+
+ return $_FS_VERSION if ($_FS_VERSION =~ /^\d+\.\d+\.\d+/);
+
+ $RT::Logger->debug("Requesting freeside version...");
+ ($_FS_VERSION) = &_XMLRPCRequest('version');
+ $RT::Logger->debug("Cached freeside version: ${_FS_VERSION}");
+
+ return $_FS_VERSION;
+
+}
+
+sub smart_search { #Subroutine
+
+ return _XMLRPCRequest('cust_main.smart_search', @_);
+
+}
+
+sub small_custview {
+
+ return _XMLRPCRequest('Web.UI.small_custview.small_custview', @_);
+
+}
+
+1;
diff --git a/rt/lib/RT/User.pm b/rt/lib/RT/User.pm
index 6919cbafa..cbc10f5b4 100755
--- a/rt/lib/RT/User.pm
+++ b/rt/lib/RT/User.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -195,7 +170,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -204,14 +179,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -222,14 +197,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Password
+=item Password
Returns the current value of Password.
(In the database, Password is stored as varchar(40).)
-=head2 SetPassword VALUE
+=item SetPassword VALUE
Set Password to VALUE.
@@ -240,14 +215,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Comments
+=item Comments
Returns the current value of Comments.
(In the database, Comments is stored as blob.)
-=head2 SetComments VALUE
+=item SetComments VALUE
Set Comments to VALUE.
@@ -258,14 +233,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Signature
+=item Signature
Returns the current value of Signature.
(In the database, Signature is stored as blob.)
-=head2 SetSignature VALUE
+=item SetSignature VALUE
Set Signature to VALUE.
@@ -276,14 +251,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 EmailAddress
+=item EmailAddress
Returns the current value of EmailAddress.
(In the database, EmailAddress is stored as varchar(120).)
-=head2 SetEmailAddress VALUE
+=item SetEmailAddress VALUE
Set EmailAddress to VALUE.
@@ -294,14 +269,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 FreeformContactInfo
+=item FreeformContactInfo
Returns the current value of FreeformContactInfo.
(In the database, FreeformContactInfo is stored as blob.)
-=head2 SetFreeformContactInfo VALUE
+=item SetFreeformContactInfo VALUE
Set FreeformContactInfo to VALUE.
@@ -312,14 +287,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Organization
+=item Organization
Returns the current value of Organization.
(In the database, Organization is stored as varchar(200).)
-=head2 SetOrganization VALUE
+=item SetOrganization VALUE
Set Organization to VALUE.
@@ -330,14 +305,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 RealName
+=item RealName
Returns the current value of RealName.
(In the database, RealName is stored as varchar(120).)
-=head2 SetRealName VALUE
+=item SetRealName VALUE
Set RealName to VALUE.
@@ -348,14 +323,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 NickName
+=item NickName
Returns the current value of NickName.
(In the database, NickName is stored as varchar(16).)
-=head2 SetNickName VALUE
+=item SetNickName VALUE
Set NickName to VALUE.
@@ -366,14 +341,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Lang
+=item Lang
Returns the current value of Lang.
(In the database, Lang is stored as varchar(16).)
-=head2 SetLang VALUE
+=item SetLang VALUE
Set Lang to VALUE.
@@ -384,14 +359,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 EmailEncoding
+=item EmailEncoding
Returns the current value of EmailEncoding.
(In the database, EmailEncoding is stored as varchar(16).)
-=head2 SetEmailEncoding VALUE
+=item SetEmailEncoding VALUE
Set EmailEncoding to VALUE.
@@ -402,14 +377,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 WebEncoding
+=item WebEncoding
Returns the current value of WebEncoding.
(In the database, WebEncoding is stored as varchar(16).)
-=head2 SetWebEncoding VALUE
+=item SetWebEncoding VALUE
Set WebEncoding to VALUE.
@@ -420,14 +395,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExternalContactInfoId
+=item ExternalContactInfoId
Returns the current value of ExternalContactInfoId.
(In the database, ExternalContactInfoId is stored as varchar(100).)
-=head2 SetExternalContactInfoId VALUE
+=item SetExternalContactInfoId VALUE
Set ExternalContactInfoId to VALUE.
@@ -438,14 +413,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ContactInfoSystem
+=item ContactInfoSystem
Returns the current value of ContactInfoSystem.
(In the database, ContactInfoSystem is stored as varchar(30).)
-=head2 SetContactInfoSystem VALUE
+=item SetContactInfoSystem VALUE
Set ContactInfoSystem to VALUE.
@@ -456,14 +431,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExternalAuthId
+=item ExternalAuthId
Returns the current value of ExternalAuthId.
(In the database, ExternalAuthId is stored as varchar(100).)
-=head2 SetExternalAuthId VALUE
+=item SetExternalAuthId VALUE
Set ExternalAuthId to VALUE.
@@ -474,14 +449,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 AuthSystem
+=item AuthSystem
Returns the current value of AuthSystem.
(In the database, AuthSystem is stored as varchar(30).)
-=head2 SetAuthSystem VALUE
+=item SetAuthSystem VALUE
Set AuthSystem to VALUE.
@@ -492,14 +467,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Gecos
+=item Gecos
Returns the current value of Gecos.
(In the database, Gecos is stored as varchar(16).)
-=head2 SetGecos VALUE
+=item SetGecos VALUE
Set Gecos to VALUE.
@@ -510,14 +485,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 HomePhone
+=item HomePhone
Returns the current value of HomePhone.
(In the database, HomePhone is stored as varchar(30).)
-=head2 SetHomePhone VALUE
+=item SetHomePhone VALUE
Set HomePhone to VALUE.
@@ -528,14 +503,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 WorkPhone
+=item WorkPhone
Returns the current value of WorkPhone.
(In the database, WorkPhone is stored as varchar(30).)
-=head2 SetWorkPhone VALUE
+=item SetWorkPhone VALUE
Set WorkPhone to VALUE.
@@ -546,14 +521,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 MobilePhone
+=item MobilePhone
Returns the current value of MobilePhone.
(In the database, MobilePhone is stored as varchar(30).)
-=head2 SetMobilePhone VALUE
+=item SetMobilePhone VALUE
Set MobilePhone to VALUE.
@@ -564,14 +539,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 PagerPhone
+=item PagerPhone
Returns the current value of PagerPhone.
(In the database, PagerPhone is stored as varchar(30).)
-=head2 SetPagerPhone VALUE
+=item SetPagerPhone VALUE
Set PagerPhone to VALUE.
@@ -582,14 +557,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Address1
+=item Address1
Returns the current value of Address1.
(In the database, Address1 is stored as varchar(200).)
-=head2 SetAddress1 VALUE
+=item SetAddress1 VALUE
Set Address1 to VALUE.
@@ -600,14 +575,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Address2
+=item Address2
Returns the current value of Address2.
(In the database, Address2 is stored as varchar(200).)
-=head2 SetAddress2 VALUE
+=item SetAddress2 VALUE
Set Address2 to VALUE.
@@ -618,14 +593,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 City
+=item City
Returns the current value of City.
(In the database, City is stored as varchar(100).)
-=head2 SetCity VALUE
+=item SetCity VALUE
Set City to VALUE.
@@ -636,14 +611,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 State
+=item State
Returns the current value of State.
(In the database, State is stored as varchar(100).)
-=head2 SetState VALUE
+=item SetState VALUE
Set State to VALUE.
@@ -654,14 +629,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Zip
+=item Zip
Returns the current value of Zip.
(In the database, Zip is stored as varchar(16).)
-=head2 SetZip VALUE
+=item SetZip VALUE
Set Zip to VALUE.
@@ -672,14 +647,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Country
+=item Country
Returns the current value of Country.
(In the database, Country is stored as varchar(50).)
-=head2 SetCountry VALUE
+=item SetCountry VALUE
Set Country to VALUE.
@@ -690,14 +665,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Timezone
+=item Timezone
Returns the current value of Timezone.
(In the database, Timezone is stored as varchar(50).)
-=head2 SetTimezone VALUE
+=item SetTimezone VALUE
Set Timezone to VALUE.
@@ -708,14 +683,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 PGPKey
+=item PGPKey
Returns the current value of PGPKey.
(In the database, PGPKey is stored as text.)
-=head2 SetPGPKey VALUE
+=item SetPGPKey VALUE
Set PGPKey to VALUE.
@@ -726,7 +701,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -735,7 +710,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -744,7 +719,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -753,7 +728,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -763,77 +738,77 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Password =>
- {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+ {read => 1, write => 1, type => 'varchar(40)', default => ''},
Comments =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
Signature =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
EmailAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
FreeformContactInfo =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
Organization =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
RealName =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
NickName =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
Lang =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
EmailEncoding =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
WebEncoding =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
ExternalContactInfoId =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
ContactInfoSystem =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
ExternalAuthId =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
AuthSystem =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
Gecos =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
HomePhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
WorkPhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
MobilePhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
PagerPhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
Address1 =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Address2 =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
City =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
State =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
Zip =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
Country =>
- {read => 1, write => 1, sql_type => 12, length => 50, is_blob => 0, is_numeric => 0, type => 'varchar(50)', default => ''},
+ {read => 1, write => 1, type => 'varchar(50)', default => ''},
Timezone =>
- {read => 1, write => 1, sql_type => 12, length => 50, is_blob => 0, is_numeric => 0, type => 'varchar(50)', default => ''},
+ {read => 1, write => 1, type => 'varchar(50)', default => ''},
PGPKey =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -865,7 +840,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/User_Overlay.pm b/rt/lib/RT/User_Overlay.pm
index db3964cd3..73c3bdd5b 100644
--- a/rt/lib/RT/User_Overlay.pm
+++ b/rt/lib/RT/User_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::User - RT User object
@@ -59,6 +58,11 @@
=head1 METHODS
+=begin testing
+
+ok(require RT::User);
+
+=end testing
=cut
@@ -69,12 +73,19 @@ package RT::User;
use strict;
no warnings qw(redefine);
+use vars qw(%_USERS_KEY_CACHE);
+
+%_USERS_KEY_CACHE = ();
+
use Digest::MD5;
use RT::Principals;
use RT::ACE;
use RT::Interface::Email;
use Encode;
+# {{{ sub _Accessible
+
+
sub _OverlayAccessible {
{
@@ -99,9 +110,62 @@ sub _OverlayAccessible {
+# }}}
+
+# {{{ sub Create
+
=head2 Create { PARAMHASH }
+=begin testing
+
+# Make sure we can create a user
+
+my $u1 = RT::User->new($RT::SystemUser);
+is(ref($u1), 'RT::User');
+my ($id, $msg) = $u1->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok ($id, "Creating user CreateTest1 - " . $msg );
+
+# Make sure we can't create a second user with the same name
+my $u2 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u2->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-2@example.com');
+ok (!$id, $msg);
+
+
+# Make sure we can't create a second user with the same EmailAddress address
+my $u3 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u3->Create(Name => 'CreateTest2'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok (!$id, $msg);
+
+# Make sure we can create a user with no EmailAddress address
+my $u4 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u4->Create(Name => 'CreateTest3'.$$);
+ok ($id, $msg);
+
+# make sure we can create a second user with no EmailAddress address
+my $u5 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u5->Create(Name => 'CreateTest4'.$$);
+ok ($id, $msg);
+
+# make sure we can create a user with a blank EmailAddress address
+my $u6 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u6->Create(Name => 'CreateTest6'.$$, EmailAddress => '');
+ok ($id, $msg);
+# make sure we can create a second user with a blankEmailAddress address
+my $u7 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u7->Create(Name => 'CreateTest7'.$$, EmailAddress => '');
+ok ($id, $msg);
+
+# Can we change the email address away from from "";
+($id,$msg) = $u7->SetEmailAddress('foo@bar'.$$);
+ok ($id, $msg);
+# can we change the address back to "";
+($id,$msg) = $u7->SetEmailAddress('');
+ok ($id, $msg);
+is ($u7->EmailAddress, '');
+
+
+=end testing
=cut
@@ -121,7 +185,7 @@ sub Create {
#Check the ACL
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
- return ( 0, $self->loc('Permission Denied') );
+ return ( 0, $self->loc('No permission to create users') );
}
@@ -136,7 +200,9 @@ sub Create {
- my $privileged = delete $args{'Privileged'};
+ # Privileged is no longer a column in users
+ my $privileged = $args{'Privileged'};
+ delete $args{'Privileged'};
if ($args{'CryptedPassword'} ) {
@@ -146,8 +212,8 @@ sub Create {
elsif ( !$args{'Password'} ) {
$args{'Password'} = '*NO-PASSWORD*';
}
- elsif ( length( $args{'Password'} ) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [_1] characters long",RT->Config->Get('MinimumPasswordLength')) );
+ elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long",$RT::MinimumPasswordLength) );
}
else {
@@ -157,6 +223,8 @@ sub Create {
#TODO Specify some sensible defaults.
unless ( $args{'Name'} ) {
+ use Data::Dumper;
+ $RT::Logger->crit(Dumper \%args);
return ( 0, $self->loc("Must specify 'Name' attribute") );
}
@@ -166,8 +234,8 @@ sub Create {
$TempUser->Load( $args{'Name'} );
return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
- my ($val, $message) = $self->ValidateEmailAddress( $args{'EmailAddress'} );
- return (0, $message) unless ( $val );
+ return ( 0, $self->loc('Email address in use') )
+ unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
}
else {
$RT::Logger->warning( "$self couldn't check for pre-existing users");
@@ -256,7 +324,7 @@ sub Create {
if ( $record_transaction ) {
- $self->_NewTransaction( Type => "Create" );
+ $self->_NewTransaction( Type => "Create" );
}
$RT::Handle->Commit;
@@ -264,6 +332,12 @@ sub Create {
return ( $id, $self->loc('User created') );
}
+# }}}
+
+
+
+# {{{ SetPrivileged
+
=head2 SetPrivileged BOOL
If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
@@ -271,6 +345,20 @@ Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
Returns a standard RT tuple of (val, msg);
+=begin testing
+
+
+ok(my $user = RT::User->new($RT::SystemUser));
+ok($user->Load('root'), "Loaded user 'root'");
+ok($user->Privileged, "User 'root' is privileged");
+ok(my ($v,$m) = $user->SetPrivileged(0));
+ok ($v ==1, "Set unprivileged suceeded ($m)");
+ok(!$user->Privileged, "User 'root' is no longer privileged");
+ok(my ($v2,$m2) = $user->SetPrivileged(1));
+ok ($v2 ==1, "Set privileged suceeded ($m2");
+ok($user->Privileged, "User 'root' is privileged again");
+
+=end testing
=cut
@@ -282,9 +370,9 @@ sub SetPrivileged {
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
return ( 0, $self->loc('Permission Denied') );
}
-
my $priv = RT::Group->new($self->CurrentUser);
$priv->LoadSystemInternalGroup('Privileged');
+
unless ($priv->Id) {
$RT::Logger->crit("Could not find Privileged pseudogroup");
return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
@@ -297,14 +385,13 @@ sub SetPrivileged {
return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
}
- my $principal = $self->PrincipalId;
if ($val) {
- if ($priv->HasMember($principal)) {
+ if ($priv->HasMember($self->PrincipalObj)) {
#$RT::Logger->debug("That user is already privileged");
return (0,$self->loc("That user is already privileged"));
}
- if ($unpriv->HasMember($principal)) {
- $unpriv->_DeleteMember($principal);
+ if ($unpriv->HasMember($self->PrincipalObj)) {
+ $unpriv->_DeleteMember($self->PrincipalId);
} else {
# if we had layered transactions, life would be good
# sadly, we have to just go ahead, even if something
@@ -312,7 +399,7 @@ sub SetPrivileged {
$RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
"unprivileged. something is drastically wrong.");
}
- my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $principal);
+ my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
if ($status) {
return (1, $self->loc("That user is now privileged"));
} else {
@@ -320,12 +407,12 @@ sub SetPrivileged {
}
}
else {
- if ($unpriv->HasMember($principal)) {
+ if ($unpriv->HasMember($self->PrincipalObj)) {
#$RT::Logger->debug("That user is already unprivileged");
return (0,$self->loc("That user is already unprivileged"));
}
- if ($priv->HasMember($principal)) {
- $priv->_DeleteMember( $principal );
+ if ($priv->HasMember($self->PrincipalObj)) {
+ $priv->_DeleteMember( $self->PrincipalId);
} else {
# if we had layered transactions, life would be good
# sadly, we have to just go ahead, even if something
@@ -333,7 +420,7 @@ sub SetPrivileged {
$RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
"unprivileged. something is drastically wrong.");
}
- my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $principal);
+ my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
if ($status) {
return (1, $self->loc("That user is now unprivileged"));
} else {
@@ -342,6 +429,10 @@ sub SetPrivileged {
}
}
+# }}}
+
+# {{{ Privileged
+
=head2 Privileged
Returns true if this user is privileged. Returns undef otherwise.
@@ -352,7 +443,7 @@ sub Privileged {
my $self = shift;
my $priv = RT::Group->new($self->CurrentUser);
$priv->LoadSystemInternalGroup('Privileged');
- if ( $priv->HasMember( $self->PrincipalId ) ) {
+ if ($priv->HasMember($self->PrincipalObj)) {
return(1);
}
else {
@@ -360,6 +451,10 @@ sub Privileged {
}
}
+# }}}
+
+# {{{ sub _BootstrapCreate
+
#create a user without validating _any_ data.
#To be used only on database init.
@@ -409,6 +504,10 @@ sub _BootstrapCreate {
return ( $id, 'User created' );
}
+# }}}
+
+# {{{ sub Delete
+
sub Delete {
my $self = shift;
@@ -416,35 +515,40 @@ sub Delete {
}
+# }}}
+
+# {{{ sub Load
+
=head2 Load
Load a user object from the database. Takes a single argument.
-If the argument is numerical, load by the column 'id'. If a user
-object or its subclass passed then loads the same user by id.
-Otherwise, load by the "Name" column which is the user's textual
-username.
+If the argument is numerical, load by the column 'id'. Otherwise, load by
+the "Name" column which is the user's textual username.
=cut
sub Load {
- my $self = shift;
+ my $self = shift;
my $identifier = shift || return undef;
+ #if it's an int, load by id. otherwise, load by name.
if ( $identifier !~ /\D/ ) {
- return $self->SUPER::LoadById( $identifier );
- }
- elsif ( UNIVERSAL::isa( $identifier, 'RT::User' ) ) {
- return $self->SUPER::LoadById( $identifier->Id );
+ $self->SUPER::LoadById($identifier);
}
else {
- return $self->LoadByCol( "Name", $identifier );
+ $self->LoadByCol( "Name", $identifier );
}
}
+# }}}
+
+# {{{ sub LoadByEmail
+
=head2 LoadByEmail
Tries to load this user object from the database by the user's email address.
+
=cut
sub LoadByEmail {
@@ -458,15 +562,18 @@ sub LoadByEmail {
$address = $self->CanonicalizeEmailAddress($address);
- #$RT::Logger->debug("Trying to load an email address: $address");
+ #$RT::Logger->debug("Trying to load an email address: $address\n");
return $self->LoadByCol( "EmailAddress", $address );
}
+# }}}
+
+# {{{ LoadOrCreateByEmail
+
=head2 LoadOrCreateByEmail ADDRESS
Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
-the provided email address and loads them. Address can be provided either as L<Email::Address> object
-or string which is parsed using the module.
+the provided email address. and loads them.
Returns a tuple of the user's id and a status message.
0 will be returned in place of the user's id in case of failure.
@@ -477,45 +584,54 @@ sub LoadOrCreateByEmail {
my $self = shift;
my $email = shift;
- my ($message, $name);
- if ( UNIVERSAL::isa( $email => 'Email::Address' ) ) {
- ($email, $name) = ($email->address, $email->phrase);
- } else {
- ($email, $name) = RT::Interface::Email::ParseAddressFromHeader( $email );
- }
-
- $self->LoadByEmail( $email );
- $self->Load( $email ) unless $self->Id;
- $message = $self->loc('User loaded');
-
- unless( $self->Id ) {
- my $val;
- ($val, $message) = $self->Create(
- Name => $email,
- EmailAddress => $email,
- RealName => $name,
- Privileged => 0,
- Comments => 'Autocreated when added as a watcher',
- );
- unless ( $val ) {
- # Deal with the race condition of two account creations at once
- $self->LoadByEmail( $email );
- unless ( $self->Id ) {
- sleep 5;
- $self->LoadByEmail( $email );
- }
- if ( $self->Id ) {
- $RT::Logger->error("Recovered from creation failure due to race condition");
- $message = $self->loc("User loaded");
- }
- else {
- $RT::Logger->crit("Failed to create user ". $email .": " .$message);
+ my ($val, $message);
+
+ my ( $Address, $Name ) =
+ RT::Interface::Email::ParseAddressFromHeader($email);
+ $email = $Address;
+
+ $self->LoadByEmail($email);
+ $message = $self->loc('User loaded');
+ unless ($self->Id) {
+ $self->Load($email);
+ }
+ unless($self->Id) {
+ ( $val, $message ) = $self->Create(
+ Name => $email,
+ EmailAddress => $email,
+ RealName => $Name,
+ Privileged => 0,
+ Comments => 'Autocreated when added as a watcher');
+ unless ($val) {
+ # Deal with the race condition of two account creations at once
+ $self->LoadByEmail($email);
+ unless ($self->Id) {
+ sleep 5;
+ $self->LoadByEmail($email);
+ }
+ if ($self->Id) {
+ $RT::Logger->error("Recovered from creation failure due to race condition");
+ $message = $self->loc("User loaded");
+ }
+ else {
+ $RT::Logger->crit("Failed to create user ".$email .": " .$message);
+ }
}
}
+
+ if ($self->Id) {
+ return($self->Id, $message);
+ }
+ else {
+ return(0, $message);
+ }
+
+
}
- return (0, $message) unless $self->id;
- return ($self->Id, $message);
-}
+
+# }}}
+
+# {{{ sub ValidateEmailAddress
=head2 ValidateEmailAddress ADDRESS
@@ -531,86 +647,24 @@ sub ValidateEmailAddress {
# if the email address is null, it's always valid
return (1) if ( !$Value || $Value eq "" );
- if ( RT->Config->Get('ValidateUserEmailAddresses') ) {
- # We only allow one valid email address
- my @addresses = Email::Address->parse($Value);
- return ( 0, $self->loc('Invalid syntax for email address') ) unless ( ( scalar (@addresses) == 1 ) && ( $addresses[0]->address ) );
- }
-
-
my $TempUser = RT::User->new($RT::SystemUser);
$TempUser->LoadByEmail($Value);
- if ( $TempUser->id && ( !$self->id || $TempUser->id != $self->id ) )
+ if ( $TempUser->id && ( $TempUser->id != $self->id ) )
{ # if we found a user with that address
# it's invalid to set this user's address to it
- return ( 0, $self->loc('Email address in use') );
+ return (undef);
}
else { #it's a valid email address
return (1);
}
}
-=head2 SetEmailAddress
-
-Check to make sure someone else isn't using this email address already
-so that a better email address can be returned
-
-=cut
-
-sub SetEmailAddress {
- my $self = shift;
- my $Value = shift;
-
- my ($val, $message) = $self->ValidateEmailAddress( $Value );
- if ( $val ) {
- return $self->_Set( Field => 'EmailAddress', Value => $Value );
- } else {
- return ( 0, $message )
- }
-
-}
-
-=head2 EmailFrequency
-
-Takes optional Ticket argument in paramhash. Returns 'no email',
-'squelched', 'daily', 'weekly' or empty string depending on
-user preferences.
-
-=over 4
-
-=item 'no email' - user has no email, so can not recieve notifications.
+# }}}
-=item 'squelched' - returned only when Ticket argument is provided and
-notifications to the user has been supressed for this ticket.
+# {{{ sub CanonicalizeEmailAddress
-=item 'daily' - retruned when user recieve daily messages digest instead
-of immediate delivery.
-=item 'weekly' - previous, but weekly.
-
-=item empty string returned otherwise.
-
-=back
-
-=cut
-
-sub EmailFrequency {
- my $self = shift;
- my %args = (
- Ticket => undef,
- @_
- );
- return '' unless $self->id && $self->id != $RT::Nobody->id
- && $self->id != $RT::SystemUser->id;
- return 'no email' unless my $email = $self->EmailAddress;
- return 'squelched' if $args{'Ticket'} &&
- grep lc $email eq lc $_->Content, $args{'Ticket'}->SquelchMailTo;
- my $frequency = RT->Config->Get( 'EmailFrequency', $self ) || '';
- return 'daily' if $frequency =~ /daily/i;
- return 'weekly' if $frequency =~ /weekly/i;
- return '';
-}
=head2 CanonicalizeEmailAddress ADDRESS
@@ -628,14 +682,19 @@ sub CanonicalizeEmailAddress {
# Example: the following rule would treat all email
# coming from a subdomain as coming from second level domain
# foo.com
- if ( my $match = RT->Config->Get('CanonicalizeEmailAddressMatch') and
- my $replace = RT->Config->Get('CanonicalizeEmailAddressReplace') )
- {
- $email =~ s/$match/$replace/gi;
+ if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
+ $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
}
return ($email);
}
+
+# }}}
+
+# {{{ sub CanonicalizeUserInfo
+
+
+
=head2 CanonicalizeUserInfo HASH of ARGS
CanonicalizeUserInfo can convert all User->Create options.
@@ -656,9 +715,14 @@ sub CanonicalizeUserInfo {
}
-=head2 Password and authentication related functions
+# }}}
+
+
+# {{{ Password related functions
-=head3 SetRandomPassword
+# {{{ sub SetRandomPassword
+
+=head2 SetRandomPassword
Takes no arguments. Returns a status code and a new password or an error message.
If the status is 1, the second value returned is the new password.
@@ -674,8 +738,8 @@ sub SetRandomPassword {
}
- my $min = ( RT->Config->Get('MinimumPasswordLength') > 6 ? RT->Config->Get('MinimumPasswordLength') : 6);
- my $max = ( RT->Config->Get('MinimumPasswordLength') > 8 ? RT->Config->Get('MinimumPasswordLength') : 8);
+ my $min = ( $RT::MinimumPasswordLength > 6 ? $RT::MinimumPasswordLength : 6);
+ my $max = ( $RT::MinimumPasswordLength > 8 ? $RT::MinimumPasswordLength : 8);
my $pass = $self->GenerateRandomPassword( $min, $max) ;
@@ -691,7 +755,11 @@ sub SetRandomPassword {
}
-=head3 ResetPassword
+# }}}
+
+# {{{ sub ResetPassword
+
+=head2 ResetPassword
Returns status, [ERROR or new password]. Resets this user\'s password to
a randomly generated pronouncable password and emails them, using a
@@ -713,13 +781,38 @@ sub ResetPassword {
return ( 0, "$pass" );
}
- my $ret = RT::Interface::Email::SendEmailUsingTemplate(
- To => $self->EmailAddress,
- Template => 'PasswordChange',
- Arguments => {
- NewPassword => $pass,
- },
- );
+ my $template = RT::Template->new( $self->CurrentUser );
+
+ if ( $self->Privileged ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
+ }
+ else {
+ $template->LoadGlobalTemplate('RT_PasswordChange_NonPrivileged');
+ }
+
+ unless ( $template->Id ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange');
+ }
+
+ unless ( $template->Id ) {
+ $RT::Logger->crit( "$self tried to send "
+ . $self->Name
+ . " a password reminder "
+ . "but couldn't find a password change template" );
+ }
+
+ my $notification = RT::Action::SendPasswordEmail->new(
+ TemplateObj => $template,
+ Argument => $pass
+ );
+
+ $notification->SetHeader( 'To', $self->EmailAddress );
+
+ my ($ret);
+ $ret = $notification->Prepare();
+ if ($ret) {
+ $ret = $notification->Commit();
+ }
if ($ret) {
return ( 1, $self->loc('New password notification sent') );
@@ -730,7 +823,11 @@ sub ResetPassword {
}
-=head3 GenerateRandomPassword MIN_LEN and MAX_LEN
+# }}}
+
+# {{{ sub GenerateRandomPassword
+
+=head2 GenerateRandomPassword MIN_LEN and MAX_LEN
Returns a random password between MIN_LEN and MAX_LEN characters long.
@@ -916,7 +1013,11 @@ sub _GenerateRandomNextChar {
return ($i);
}
-=head3 SetPassword
+# }}}
+
+# {{{ sub SetPassword
+
+=head2 SetPassword
Takes a string. Checks the string's length and sets this user's password
to that string.
@@ -934,8 +1035,8 @@ sub SetPassword {
if ( !$password ) {
return ( 0, $self->loc("No password set") );
}
- elsif ( length($password) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [_1] characters long", RT->Config->Get('MinimumPasswordLength')) );
+ elsif ( length($password) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long", $RT::MinimumPasswordLength) );
}
else {
my $new = !$self->HasPassword;
@@ -952,7 +1053,7 @@ sub SetPassword {
}
-=head3 _GeneratePassword PASSWORD
+=head2 _GeneratePassword PASSWORD
returns an MD5 hash of the password passed in, in hexadecimal encoding.
@@ -968,7 +1069,7 @@ sub _GeneratePassword {
}
-=head3 _GeneratePasswordBase64 PASSWORD
+=head2 _GeneratePasswordBase64 PASSWORD
returns an MD5 hash of the password passed in, in base64 encoding
(obsoleted now).
@@ -985,12 +1086,16 @@ sub _GeneratePasswordBase64 {
}
-=head3 HasPassword
+# }}}
+
+
+=head2 HasPassword
Returns true if the user has a valid password, otherwise returns false.
=cut
+
sub HasPassword {
my $self = shift;
my $pwd = $self->__Value('Password');
@@ -1000,7 +1105,10 @@ sub HasPassword {
return 1;
}
-=head3 IsPassword
+
+# {{{ sub IsPassword
+
+=head2 IsPassword
Returns true if the passed in value is this user's password.
Returns undef otherwise.
@@ -1047,80 +1155,13 @@ sub IsPassword {
return (undef);
}
-=head3 AuthToken
+# }}}
-Returns an authentication string associated with the user. This
-string can be used to generate passwordless URLs to integrate
-RT with services and programms like callendar managers, rss
-readers and other.
-
-=cut
-
-sub AuthToken {
- my $self = shift;
- my $secret = $self->FirstAttribute("AuthToken");
- return $secret->Content if $secret;
-
- my $id = $self->id;
- $self = RT::User->new( $RT::SystemUser );
- $self->Load( $id );
- $secret = substr(Digest::MD5::md5_hex(time . {} . rand()),0,16);
- my ($status, $msg) = $self->SetAttribute( Name => "AuthToken", Content => $secret );
- unless ( $status ) {
- $RT::Logger->error( "Couldn't set auth token: $msg" );
- return undef;
- }
- return $secret;
-}
-
-=head3 GenerateAuthToken
-
-Generate a random authentication string for the user.
-
-=cut
-
-sub GenerateAuthToken {
- my $self = shift;
- my $token = substr(Digest::MD5::md5_hex(time . {} . rand()),0,16);
- return $self->SetAttribute( Name => "AuthToken", Content => $token );
-}
-
-=head3 GenerateAuthString
-
-Takes a string and returns back a hex hash string. Later you can use
-this pair to make sure it's generated by this user using L</ValidateAuthString>
-
-=cut
-
-sub GenerateAuthString {
- my $self = shift;
- my $protect = shift;
-
- my $str = $self->AuthToken . $protect;
- utf8::encode($str);
-
- return substr(Digest::MD5::md5_hex($str),0,16);
-}
+# }}}
-=head3 ValidateAuthString
+# {{{ sub SetDisabled
-Takes auth string and protected string. Returns true is protected string
-has been protected by user's L</AuthToken>. See also L</GenerateAuthString>.
-
-=cut
-
-sub ValidateAuthString {
- my $self = shift;
- my $auth_string = shift;
- my $protected = shift;
-
- my $str = $self->AuthToken . $protected;
- utf8::encode( $str );
-
- return $auth_string eq substr(Digest::MD5::md5_hex($str),0,16);
-}
-
-=head2 SetDisabled
+=head2 Sub SetDisabled
Toggles the user's disabled flag.
If this flag is
@@ -1129,69 +1170,53 @@ user will fail. The user will appear in no user listings.
=cut
+# }}}
+
sub SetDisabled {
my $self = shift;
- my $val = shift;
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
return (0, $self->loc('Permission Denied'));
}
-
- $RT::Handle->BeginTransaction();
- my $set_err = $self->PrincipalObj->SetDisabled($val);
- unless ($set_err) {
- $RT::Handle->Rollback();
- $RT::Logger->warning(sprintf("Couldn't %s user %s", ($val == 1) ? "disable" : "enable", $self->PrincipalObj->Id));
- return (undef);
- }
- $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" );
-
- $RT::Handle->Commit();
-
- if ( $val == 1 ) {
- return (1, $self->loc("User disabled"));
- } else {
- return (1, $self->loc("User enabled"));
- }
-
+ return $self->PrincipalObj->SetDisabled(@_);
}
-=head2 Disabled
-
-Returns true if user is disabled or false otherwise
-
-=cut
-
sub Disabled {
my $self = shift;
return $self->PrincipalObj->Disabled(@_);
}
+
+# {{{ Principal related routines
+
=head2 PrincipalObj
Returns the principal object for this user. returns an empty RT::Principal
if there's no principal object matching this user.
The response is cached. PrincipalObj should never ever change.
+=begin testing
+
+ok(my $u = RT::User->new($RT::SystemUser));
+ok($u->Load(1), "Loaded the first user");
+ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
+is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group");
+
+=end testing
+
=cut
+
sub PrincipalObj {
my $self = shift;
+ unless ($self->{'PrincipalObj'} &&
+ ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
+ ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
- unless ( $self->id ) {
- $RT::Logger->error("Couldn't get principal for not loaded object");
- return undef;
- }
-
- my $obj = RT::Principal->new( $self->CurrentUser );
- $obj->LoadById( $self->id );
- unless ( $obj->id ) {
- $RT::Logger->crit( 'No principal for user #'. $self->id );
- return undef;
- } elsif ( $obj->PrincipalType ne 'User' ) {
- $RT::Logger->crit( 'User #'. $self->id .' has principal of '. $obj->PrincipalType .' type' );
- return undef;
- }
- return $obj;
+ $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
+ $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
+ 'PrincipalType' => 'User') ;
+ }
+ return($self->{'PrincipalObj'});
}
@@ -1206,6 +1231,12 @@ sub PrincipalId {
return $self->Id;
}
+# }}}
+
+
+
+# {{{ sub HasGroupRight
+
=head2 HasGroupRight
Takes a paramhash which can contain
@@ -1236,11 +1267,14 @@ sub HasGroupRight {
$args{'GroupObj'}->Load( $args{'Group'} );
}
- # Validate and load up the GroupId
+ # {{{ Validate and load up the GroupId
unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
return undef;
}
+ # }}}
+
+
# Figure out whether a user has the right we're asking about.
my $retval = $self->HasRight(
Object => $args{'GroupObj'},
@@ -1248,8 +1282,14 @@ sub HasGroupRight {
);
return ($retval);
+
+
}
+# }}}
+
+# {{{ sub OwnGroups
+
=head2 OwnGroups
Returns a group collection object containing the groups of which this
@@ -1262,21 +1302,533 @@ sub OwnGroups {
my $groups = RT::Groups->new($self->CurrentUser);
$groups->LimitToUserDefinedGroups;
$groups->WithMember(PrincipalId => $self->Id,
- Recursively => 1);
+ Recursively => 1);
return $groups;
}
+# }}}
+
+# {{{ Links
+
+#much false laziness w/Ticket_Overlay.pm
+
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
+ MemberOf => { Base => 'MemberOf',
+ Target => 'HasMember', },
+ RefersTo => { Base => 'RefersTo',
+ Target => 'ReferredToBy', },
+ DependsOn => { Base => 'DependsOn',
+ Target => 'DependedOnBy', },
+ MergedInto => { Base => 'MergedInto',
+ Target => 'MergedInto', },
+
+);
+
+sub LINKDIRMAP { return \%LINKDIRMAP }
+
+#sub _Links {
+# my $self = shift;
+#
+# #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
+# #tobias meant by $f
+# my $field = shift;
+# my $type = shift || "";
+#
+# unless ( $self->{"$field$type"} ) {
+# $self->{"$field$type"} = new RT::Links( $self->CurrentUser );
+# if ( $self->CurrentUserHasRight('ShowTicket') ) {
+# # Maybe this ticket is a merged ticket
+# my $Tickets = new RT::Tickets( $self->CurrentUser );
+# # at least to myself
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $self->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# $Tickets->Limit( FIELD => 'EffectiveId',
+# VALUE => $self->EffectiveId );
+# while (my $Ticket = $Tickets->Next) {
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $Ticket->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# }
+# $self->{"$field$type"}->Limit( FIELD => 'Type',
+# VALUE => $type )
+# if ($type);
+# }
+# }
+# return ( $self->{"$field$type"} );
+#}
+
+=head2 DeleteLink
+
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
+
+=cut
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ #check acls
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
+
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
+
+ my ($direction, $remote_link);
+
+ if ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
+
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => 'DeleteLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0
+ );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
+}
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ return $self->_AddLink(%args);
+}
+
+#sub __GetTicketFromURI {
+# my $self = shift;
+# my %args = ( URI => '', @_ );
+#
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my $uri_obj = RT::URI->new( $self->CurrentUser );
+# $uri_obj->FromURI( $args{'URI'} );
+#
+# unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
+# my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+# $RT::Logger->warning( "$msg\n" );
+# return( 0, $msg );
+# }
+# my $obj = $uri_obj->Resolver->Object;
+# unless ( UNIVERSAL::isa($obj, 'RT::Ticket') && $obj->id ) {
+# return (1, 'Found not a ticket', undef);
+# }
+# return (1, 'Found ticket', $obj);
+#}
+
+=head2 _AddLink
+
+Private non-acled variant of AddLink so that links can be added during create.
+
+=cut
+
+sub _AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
+ return ($val, $msg) if !$val || $exist;
+
+ my ($direction, $remote_link);
+ if ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction = 'Base';
+ } elsif ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
+ }
+
+}
+
+
+
+# }}}
+
+
+# {{{ sub Rights testing
+
+=head1 Rights testing
+
+
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok($root->Id, "Found the root user");
+my $rootq = RT::Queue->new($root);
+$rootq->Load(1);
+ok($rootq->Id, "Loaded the first queue");
+
+ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
+
+my $new_user = RT::User->new($RT::SystemUser);
+my ($id, $msg) = $new_user->Create(Name => 'ACLTest'.$$);
+
+ok ($id, "Created a new user for acl test $msg");
+
+my $q = RT::Queue->new($new_user);
+$q->Load(1);
+ok($q->Id, "Loaded the first queue");
+
+
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
+ok ($gval, "Grant succeeded - $gmsg");
+
+
+ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
+ok ($gval, "Revocation succeeded - $gmsg");
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
+
+
+
+
+
+# Create a ticket in the queue
+my $new_tick = RT::Ticket->new($RT::SystemUser);
+my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
+ok($tickid, "Created ticket: $tickid");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+# Create a new group
+my $group = RT::Group->new($RT::SystemUser);
+$group->CreateUserDefinedGroup(Name => 'ACLTest'.$$);
+ok($group->Id, "Created a new group Ok");
+# Grant a group the right to modify tickets in a queue
+ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"Grant succeeed - $gm");
+# Add the user to the group
+ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
+ok ($aid, "Member added to group: $amsg");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
+
+
+# Remove the user from the group
+ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
+ok ($did,"Deleted the group member: $dmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+my $q_as_system = RT::Queue->new($RT::SystemUser);
+$q_as_system->Load(1);
+ok($q_as_system->Id, "Loaded the first queue");
+
+# Create a ticket in the queue
+my $new_tick2 = RT::Ticket->new($RT::SystemUser);
+my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
+ok($tick2id, "Created ticket: $tick2id");
+is($new_tick2->QueueObj->id, $q_as_system->Id, "Created a new ticket in queue 1");
+
+
+# make sure that the user can't do this without subgroup membership
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# Create a subgroup
+my $subgroup = RT::Group->new($RT::SystemUser);
+$subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest',$$);
+ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
+#Add the subgroup as a subgroup of the group
+my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
+ok ($said, "Added the subgroup as a member of the group");
+# Add the user to a subgroup of the group
+
+my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
+ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
+
+# {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
+
+my ($id, $msg);
+($id, $msg) = $group->SetDisabled(1);
+ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
+ ($id, $msg) = $group->SetDisabled(0);
+ok($id,$msg);
+# Test what happens when we disable the group the user is a member of directly
+
+($id, $msg) = $subgroup->SetDisabled(1);
+ ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
+ ($id, $msg) = $subgroup->SetDisabled(0);
+ ok ($id,$msg);
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
+
+# }}}
+
+
+my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
+ok($usrid,"removed the user from the group - $usrmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+#revoke the right to modify tickets in a queue
+ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"revoke succeeed - $gm");
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $q_as_system, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $q_as_system, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
+
+# Before we start Make sure the user does not have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Make sure the user can't modify the ticket before they're added as a watcher
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+
+# Grant "privileged users" the system right to create users
+# Create a privileged user.
+# have that user create another user
+# Revoke the right for privileged users to create users
+# have the privileged user try to create another user and fail the ACL check
+
+=end testing
+
+=cut
+
+# }}}
+
+
+# {{{ sub HasRight
+
=head2 HasRight
-Shim around PrincipalObj->HasRight. See L<RT::Principal>.
+Shim around PrincipalObj->HasRight. See RT::Principal
=cut
sub HasRight {
+
my $self = shift;
return $self->PrincipalObj->HasRight(@_);
}
+# }}}
+
+# {{{ sub CurrentUserCanModify
+
=head2 CurrentUserCanModify RIGHT
If the user has rights for this object, either because
@@ -1314,6 +1866,10 @@ sub CurrentUserCanModify {
}
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
=head2 CurrentUserHasRight
Takes a single argument. returns 1 if $Self->CurrentUser
@@ -1331,17 +1887,19 @@ sub CurrentUserHasRight {
sub _PrefName {
my $name = shift;
if (ref $name) {
- $name = ref($name).'-'.$name->Id;
+ $name = ref ($name).'-'.$name->Id;
}
return 'Pref-'.$name;
}
+# {{{ sub Preferences
+
=head2 Preferences NAME/OBJ DEFAULT
-Obtain user preferences associated with given object or name.
-Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
-override the entries with user preferences.
+ Obtain user preferences associated with given object or name.
+ Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
+ override the entries with user preferences.
=cut
@@ -1350,49 +1908,53 @@ sub Preferences {
my $name = _PrefName (shift);
my $default = shift;
- my $attr = RT::Attribute->new( $self->CurrentUser );
- $attr->LoadByNameAndObject( Object => $self, Name => $name );
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
my $content = $attr->Id ? $attr->Content : undef;
- unless ( ref $content eq 'HASH' ) {
- return defined $content ? $content : $default;
+ if (ref ($content) eq 'HASH') {
+ if (ref ($default) eq 'HASH') {
+ for (keys %$default) {
+ exists $content->{$_} or $content->{$_} = $default->{$_};
+ }
+ }
+ elsif (defined $default) {
+ $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ }
+ return $content;
}
-
- if (ref $default eq 'HASH') {
- for (keys %$default) {
- exists $content->{$_} or $content->{$_} = $default->{$_};
- }
- }
- elsif (defined $default) {
- $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ else {
+ return defined $content ? $content : $default;
}
- return $content;
}
+# }}}
+
+# {{{ sub SetPreferences
+
=head2 SetPreferences NAME/OBJ VALUE
-Set user preferences associated with given object or name.
+ Set user preferences associated with given object or name.
=cut
sub SetPreferences {
- my $self = shift;
- my $name = _PrefName( shift );
+ my $self = shift;
+ my $name = _PrefName (shift);
my $value = shift;
-
- return (0, $self->loc("No permission to set preferences"))
- unless $self->CurrentUserCanModify('Preferences');
-
- my $attr = RT::Attribute->new( $self->CurrentUser );
- $attr->LoadByNameAndObject( Object => $self, Name => $name );
- if ( $attr->Id ) {
- return $attr->SetContent( $value );
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
+ if ($attr->Id) {
+ return $attr->SetContent ($value);
}
else {
- return $self->AddAttribute( Name => $name, Content => $value );
+ return $self->AddAttribute ( Name => $name, Content => $value );
}
}
+# }}}
+
+
=head2 WatchedQueues ROLE_LIST
Returns a RT::Queues object containing every queue watched by the user.
@@ -1462,6 +2024,9 @@ sub WatchedQueues {
}
+
+# {{{ sub _CleanupInvalidDelegations
+
=head2 _CleanupInvalidDelegations { InsideTransaction => undef }
Revokes all ACE entries delegated by this user which are inconsistent
@@ -1484,59 +2049,63 @@ and logs an internal error if the deletion fails (should not happen).
sub _CleanupInvalidDelegations {
my $self = shift;
my %args = ( InsideTransaction => undef,
- @_ );
+ @_ );
unless ( $self->Id ) {
- $RT::Logger->warning("User not loaded.");
- return (undef);
+ $RT::Logger->warning("User not loaded.");
+ return (undef);
}
my $in_trans = $args{InsideTransaction};
return(1) if ($self->HasRight(Right => 'DelegateRights',
- Object => $RT::System));
+ Object => $RT::System));
# Look up all delegation rights currently posessed by this user.
my $deleg_acl = RT::ACL->new($RT::SystemUser);
$deleg_acl->LimitToPrincipal(Type => 'User',
- Id => $self->PrincipalId,
- IncludeGroupMembership => 1);
+ Id => $self->PrincipalId,
+ IncludeGroupMembership => 1);
$deleg_acl->Limit( FIELD => 'RightName',
- OPERATOR => '=',
- VALUE => 'DelegateRights' );
+ OPERATOR => '=',
+ VALUE => 'DelegateRights' );
my @allowed_deleg_objects = map {$_->Object()}
- @{$deleg_acl->ItemsArrayRef()};
+ @{$deleg_acl->ItemsArrayRef()};
# Look up all rights delegated by this principal which are
# inconsistent with the allowed delegation objects.
my $acl_to_del = RT::ACL->new($RT::SystemUser);
$acl_to_del->DelegatedBy(Id => $self->Id);
foreach (@allowed_deleg_objects) {
- $acl_to_del->LimitNotObject($_);
+ $acl_to_del->LimitNotObject($_);
}
# Delete all disallowed delegations
while ( my $ace = $acl_to_del->Next() ) {
- my $ret = $ace->_Delete(InsideTransaction => 1);
- unless ($ret) {
- $RT::Handle->Rollback() unless $in_trans;
- $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
- return (undef);
- }
+ my $ret = $ace->_Delete(InsideTransaction => 1);
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
+ return (undef);
+ }
}
$RT::Handle->Commit() unless $in_trans;
return (1);
}
+# }}}
+
+# {{{ sub _Set
+
sub _Set {
my $self = shift;
my %args = (
Field => undef,
Value => undef,
- TransactionType => 'Set',
- RecordTransaction => 1,
+ TransactionType => 'Set',
+ RecordTransaction => 1,
@_
);
@@ -1553,7 +2122,7 @@ sub _Set {
my $Old = $self->SUPER::_Value("$args{'Field'}");
my ($ret, $msg) = $self->SUPER::_Set( Field => $args{'Field'},
- Value => $args{'Value'} );
+ Value => $args{'Value'} );
#If we can't actually set the field to the value, don't record
# a transaction. instead, get out of here.
@@ -1575,6 +2144,10 @@ sub _Set {
}
}
+# }}}
+
+# {{{ sub _Value
+
=head2 _Value
Takes the name of a table column.
@@ -1602,7 +2175,7 @@ sub _Value {
#If the user wants to see their own values, let them
# TODO figure ouyt a better way to deal with this
- elsif ( defined($self->Id) && $self->CurrentUser->Id == $self->Id ) {
+ elsif ( $self->CurrentUser->Id == $self->Id ) {
return ( $self->SUPER::_Value($field) );
}
@@ -1616,96 +2189,14 @@ sub _Value {
}
-=head2 FriendlyName
-
-Return the friendly name
-
-=cut
-
-sub FriendlyName {
- my $self = shift;
- return $self->RealName if defined($self->RealName);
- return $self->Name if defined($self->Name);
- return "";
-}
-
-=head2 PreferredKey
-
-Returns the preferred key of the user. If none is set, then this will query
-GPG and set the preferred key to the maximally trusted key found (and then
-return it). Returns C<undef> if no preferred key can be found.
-
-=cut
-
-sub PreferredKey
-{
- my $self = shift;
- return undef unless RT->Config->Get('GnuPG')->{'Enable'};
- my $prefkey = $self->FirstAttribute('PreferredKey');
- return $prefkey->Content if $prefkey;
-
- # we don't have a preferred key for this user, so now we must query GPG
- require RT::Crypt::GnuPG;
- my %res = RT::Crypt::GnuPG::GetKeysForEncryption($self->EmailAddress);
- return undef unless defined $res{'info'};
- my @keys = @{ $res{'info'} };
- return undef if @keys == 0;
-
- if (@keys == 1) {
- $prefkey = $keys[0]->{'Fingerprint'};
- }
- else {
- # prefer the maximally trusted key
- @keys = sort { $b->{'TrustLevel'} <=> $a->{'TrustLevel'} } @keys;
- $prefkey = $keys[0]->{'Fingerprint'};
- }
-
- $self->SetAttribute(Name => 'PreferredKey', Content => $prefkey);
- return $prefkey;
-}
-
-sub PrivateKey {
- my $self = shift;
-
- my $key = $self->FirstAttribute('PrivateKey') or return undef;
- return $key->Content;
-}
-
-sub SetPrivateKey {
- my $self = shift;
- my $key = shift;
- # XXX: ACL
- unless ( $key ) {
- my ($status, $msg) = $self->DeleteAttribute('PrivateKey');
- unless ( $status ) {
- $RT::Logger->error( "Couldn't delete attribute: $msg" );
- return ($status, $self->loc("Couldn't unset private key"));
- }
- return ($status, $self->loc("Unset private key"));
- }
-
- # check that it's really private key
- {
- my %tmp = RT::Crypt::GnuPG::GetKeysForSigning( $key );
- return (0, $self->loc("No such key or it's not suitable for signing"))
- if $tmp{'exit_code'} || !$tmp{'info'};
- }
-
- my ($status, $msg) = $self->SetAttribute(
- Name => 'PrivateKey',
- Content => $key,
- );
- return ($status, $self->loc("Couldn't set private key"))
- unless $status;
- return ($status, $self->loc("Unset private key"));
-}
+# }}}
sub BasicColumns {
(
- [ Name => 'User Id' ],
- [ EmailAddress => 'Email' ],
- [ RealName => 'Name' ],
- [ Organization => 'Organization' ],
+ [ Name => 'User Id' ],
+ [ EmailAddress => 'Email' ],
+ [ RealName => 'Name' ],
+ [ Organization => 'Organization' ],
);
}
diff --git a/rt/lib/RT/Users.pm b/rt/lib/RT/Users.pm
index 3a6eefff7..d58f69653 100755
--- a/rt/lib/RT/Users.pm
+++ b/rt/lib/RT/Users.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (Except where explictly superceded by other copyright notices)
#
# 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
@@ -20,32 +14,13 @@
# 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.
-#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
#
-# 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 }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::User item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
no warnings qw(redefine);
diff --git a/rt/lib/RT/Users_Overlay.pm b/rt/lib/RT/Users_Overlay.pm
index a2d235c23..bdc7074ff 100644
--- a/rt/lib/RT/Users_Overlay.pm
+++ b/rt/lib/RT/Users_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Users - Collection of RT::User objects
@@ -60,6 +59,11 @@
=head1 METHODS
+=begin testing
+
+ok(require RT::Users);
+
+=end testing
=cut
@@ -228,6 +232,68 @@ sub LimitToPrivileged {
=head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
+=begin testing
+
+ok(my $users = RT::Users->new($RT::SystemUser));
+$users->WhoHaveRight(Object =>$RT::System, Right =>'SuperUser');
+ok($users->Count == 1, "There is one privileged superuser - Found ". $users->Count );
+# TODO: this wants more testing
+
+my $RTxUser = RT::User->new($RT::SystemUser);
+($id, $msg) = $RTxUser->Create( Name => 'RTxUser', Comments => "RTx extension user", Privileged => 1);
+ok ($id,$msg);
+
+my $group = RT::Group->new($RT::SystemUser);
+$group->LoadACLEquivalenceGroup($RTxUser->PrincipalObj);
+
+my $RTxSysObj = {};
+bless $RTxSysObj, 'RTx::System';
+*RTx::System::Id = sub { 1; };
+*RTx::System::id = *RTx::System::Id;
+my $ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System', ObjectId => 1 );
+ok ($id, "ACL for RTxSysObj created");
+
+my $RTxObj = {};
+bless $RTxObj, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 4; };
+*RTx::System::Record::id = *RTx::System::Record::Id;
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxSysObj);
+is($users->Count, 1, "RTxUserRight found for RTxSysObj");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj);
+is($users->Count, 0, "RTxUserRight not found for RTxObj");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
+is($users->Count, 1, "RTxUserRight found for RTxObj using EquivObjects");
+
+$ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
+ok ($id, "ACL for RTxObj created");
+
+my $RTxObj2 = {};
+bless $RTxObj2, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 5; };
+*RTx::System::Record::id = sub { 5; };
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2);
+is($users->Count, 1, "RTxUserRight found for RTxObj2");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
+is($users->Count, 1, "RTxUserRight found for RTxObj2");
+
+
+=end testing
find all users who the right Right for this group, either individually
or as members of groups
@@ -297,16 +363,6 @@ sub _JoinACL
@_,
);
- if ( $args{'Right'} ) {
- my $canonic = RT::ACE->CanonicalizeRightName( $args{'Right'} );
- unless ( $canonic ) {
- $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'");
- }
- else {
- $args{'Right'} = $canonic;
- }
- }
-
my $acl = $self->NewAlias('ACL');
$self->Limit(
ALIAS => $acl,
@@ -347,7 +403,7 @@ sub _GetEquivObjects
# XXX: This should be abstracted into object itself
if( $args{'Object'}->id ) {
- push @objects, $args{'Object'}->ACLEquivalenceObjects;
+ push @objects, $args{'Object'}->QueueObj;
} else {
push @objects, 'RT::Queue';
}
@@ -384,6 +440,7 @@ sub WhoHaveRight {
$from_group->WhoHaveGroupRight( %args );
#XXX: DIRTY HACK
+ use DBIx::SearchBuilder 1.50; #no version on ::Union :(
use DBIx::SearchBuilder::Union;
my $union = new DBIx::SearchBuilder::Union;
$union->add( $_ ) foreach @from_role;
diff --git a/rt/lib/RTx/Statistics.pm b/rt/lib/RTx/Statistics.pm
new file mode 100755
index 000000000..8b9d6e4f0
--- /dev/null
+++ b/rt/lib/RTx/Statistics.pm
@@ -0,0 +1,239 @@
+package Statistics;
+
+use vars qw(
+$MultiQueueStatus $MultiQueueDateFormat @MultiQueueQueueList $MultiQueueMaxRows $MultiQueueWeekends $MultiQueueLabelDateFormat
+$PerDayStatus $PerDayDateFormat $PerDayQueue $PerDayMaxRows $PerDayWeekends $PerDayLabelDateFormat $PerDayPeriod
+$DayOfWeekQueue
+@OpenStalledQueueList $OpenStalledWeekends
+$TimeToResolveDateFormat $TimeToResolveQueue $TimeToResolveMaxRows $TimeToResolveWeekends $TimeToResolveLabelDateFormat
+$TimeToResolveGraphQueue
+@years @months %monthsMaxDay
+$secsPerDay
+$RestrictAccess
+$GraphWidth $GraphHeight
+);
+
+use Time::Local;
+
+# I couldn't figure out a way to override these in RT_SiteConfig, which would be
+# preferable.
+
+# Width and Height of all graphics
+$GraphWidth=500;
+$GraphHeight=400;
+
+# Initial settings for the CallsMultiQueue stat page
+$MultiQueueStatus = "resolved";
+$MultiQueueDateFormat = "%a %b %d %Y"; # format for dates on Multi Queue report, see "man strftime" for options
+@MultiQueueQueueList = ("General"); # list of queues to start Multi Queue per day reports
+$MultiQueueMaxRows = 10;
+$MultiQueueWeekends = 1;
+$MultiQueueLabelDateFormat = "%a";
+
+# Initial settings for the CallsQueueDay stat page
+$PerDayStatus = "resolved";
+$PerDayDateFormat = "%a %b %d %Y";
+$PerDayQueue = "General";
+$PerDayMaxRows = 10;
+$PerDayWeekends = 1;
+$PerDayLabelDateFormat = "%a";
+$PerDayPeriod = 10;
+
+# Initial settings for the DayOfWeek stat page
+$DayOfWeekQueue = "General";
+
+# Initial settings for the OpenStalled stat page
+@OpenStalledQueueList = ("General");
+$OpenStalledWeekends = 1;
+
+# Initial settings for the TimeToResolve stat page
+$TimeToResolveDateFormat = "%a %b %d";
+$TimeToResolveQueue = "General";
+$TimeToResolveMaxRows = 10;
+$TimeToResolveWeekends = 1;
+$TimeToResolveLabelDateFormat = "%a";
+
+# Initial settings for the TimeToResolve Graph page
+$TimeToResolveGraphQueue = "General";
+
+$secsPerDay = 86400;
+
+# List of years and months to populate drop down lists
+@years =('2010', '2009', '2008', '2007', '2006', '2005', '2004', '2003' ,'2003' ,'2002');
+@months=qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
+%monthsMaxDay = (
+ 0 => 31, # January
+ 1 => 29, # February, allow for leap year
+ 2 => 31, # March
+ 3 => 30, # April
+ 4 => 31, # May
+ 5 => 30, # June
+ 6 => 31, # July
+ 7 => 31, # August
+ 8 => 30, # September
+ 9 => 31, # October
+ 10=> 30, # November
+ 11=> 31 # December
+ );
+
+# Set to one to prevent users without the ShowConfigTab right from seeing Statistics
+$RestrictAccess = 0;
+
+# Variables to control debugging
+my $debugging=0; # set to 1 to enable debugging
+my $debugtext="";
+
+=head2 FormatDate
+
+Returns a string representing the specified date formatted by the specified string
+
+=cut
+sub FormatDate {
+ my $fmt = shift;
+ my $self = shift;
+ return POSIX::strftime($fmt, localtime($self->Unix));
+}
+
+
+=head2 RTDateSetToLocalMidnight
+
+Sets the date to midnight (at the beginning of the day) local time
+Returns the unixtime at midnight.
+
+=cut
+sub RTDateSetToLocalMidnight {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ $self->Unix(timelocal (0,0,0,$mday,$mon,$year,$wday,$yday));
+
+ return ($self->Unix);
+}
+
+=head2 RTDateIsWeekend
+
+Returns 1 if the date is on saturday or sunday
+
+=cut
+sub RTDateIsWeekend {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ return 1 if (($wday==6) || ($wday==0));
+ 0;
+}
+
+=head2 RTDateGetDateWeekday
+
+Returns the localized name of the day specified by date
+
+=cut
+sub RTDateGetDateWeekday {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ return $self->GetWeekday($wday);
+}
+
+=head2 RTDateSubDay
+
+Subtracts 24 hours from the current time
+
+=cut
+
+sub RTDateSubDay {
+ my $self = shift;
+ $self->AddSeconds(0 - $DAY);
+}
+
+=head2 RTDateSubDays $DAYS
+
+Subtracts 24 hours * $DAYS from the current time
+
+=cut
+
+sub RTDateSubDays {
+ my $self = shift;
+ my $days = shift;
+ $self->AddSeconds(0 - ($days * $DAY));
+}
+
+=head2 DebugInit
+
+Creates a text area on the page if debugging is on.
+
+=cut
+
+sub DebugInit {
+ if($debugging) {
+ my $m = shift;
+ $m->print("<TEXTAREA NAME=debugarea COLS=120 ROWS=50>$debugtext</TEXTAREA>\n");
+ }
+}
+
+=head2 DebugLog $logmsg
+
+Adds a message to the debug area
+
+=cut
+
+sub DebugLog {
+ if($debugging) {
+ my $line = shift;
+ $debugtext .= $line;
+ $RT::Logger->debug($line);
+ }
+}
+
+=head2 DebugClear
+
+Clears the current debug string, otherwise it builds from page to page
+
+=cut
+
+sub DebugClear {
+ if($debugging) {
+ $debugtext = undef;
+ }
+}
+
+=head2 DurationAsString
+
+Returns a string representing the specified duration
+
+=cut
+
+sub DurationAsString {
+ my $Duration = shift;
+ my $MINUTE = 60;
+ my $HOUR = $MINUTE*60;
+ my $DAY = $HOUR * 24;
+ my $WEEK = $DAY * 7;
+ my $days = int($Duration / $DAY);
+ $Duration = $Duration % $DAY;
+ my $hours = int($Duration / $HOUR);
+ $hours = sprintf("%02d", $hours);
+ $Duration = $Duration % $HOUR;
+ my $minutes = int($Duration/$MINUTE);
+ $minutes = sprintf("%02d", $minutes);
+ $Duration = $Duration % $MINUTE;
+ my $secs = sprintf("%02d", $Duration);
+
+ if(!$days) {
+ $days = "00";
+ }
+ if(!$hours) {
+ $hours = "00";
+ }
+ if(!$minutes) {
+ $minutes = "00";
+ }
+ if(!$secs) {
+ $secs = "00";
+ }
+ return "$days days $hours:$minutes:$secs";
+}
+
+1;
+
+
diff --git a/rt/lib/RTx/WebCronTool.pm b/rt/lib/RTx/WebCronTool.pm
new file mode 100644
index 000000000..5f086a279
--- /dev/null
+++ b/rt/lib/RTx/WebCronTool.pm
@@ -0,0 +1,41 @@
+package RTx::WebCronTool;
+$RTx::WebCronTool::VERSION = "0.01";
+
+1;
+
+__END__
+
+=head1 NAME
+
+RTx::WebCronTool - Web interface to rt-crontool
+
+=head1 VERSION
+
+This document describes version 0.01 of RTx::WebCronTool, released
+July 11, 2004.
+
+=head1 DESCRIPTION
+
+This RT extension provides a web interface for the built-in F<rt-crontool>
+utility, allowing scheduled processes to be launched remotely.
+
+After installation, log in as superuser, and click on the "Web CronTool" menu
+on the bottom of the navigation pane.
+
+To use it, simply submit the modules and arguments. All progress, error messages
+and debug information will then be displayed online.
+
+=head1 AUTHORS
+
+Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2004 by Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
diff --git a/rt/lib/t/00smoke.t.in b/rt/lib/t/00smoke.t.in
deleted file mode 100644
index 288dd4aae..000000000
--- a/rt/lib/t/00smoke.t.in
+++ /dev/null
@@ -1,14 +0,0 @@
-#!@PERL@
-
-use Test::More qw(no_plan);
-
-use lib "@RT_LIB_PATH@";
-use RT;
-ok(RT::LoadConfig);
-ok(RT::Init, "Basic initialization and DB connectivity");
-
-use File::Find;
-File::Find::find({wanted => \&wanted}, 'lib/');
-sub wanted { /^*\.pm\z/s && ok(require $_, "Requiring '$_'"); }
-
-
diff --git a/rt/lib/t/01harness.t.in b/rt/lib/t/01harness.t.in
deleted file mode 100644
index d132330c2..000000000
--- a/rt/lib/t/01harness.t.in
+++ /dev/null
@@ -1,12 +0,0 @@
-#!@PERL@
-
-use Test::More qw(no_plan);
-
-use lib "@RT_LIB_PATH@";
-use RT;
-ok(RT::LoadConfig);
-ok(RT::Init, "Basic initialization and DB connectivity");
-
-my $test = shift @ARGV;
-require $test;
-
diff --git a/rt/lib/t/02regression.t b/rt/lib/t/02regression.t
index 4504cc76a..4cc131815 100644
--- a/rt/lib/t/02regression.t
+++ b/rt/lib/t/02regression.t
@@ -34,11 +34,14 @@ is($q2->CommentAddress, 'comment@a');
use File::Find;
-File::Find::find({wanted => \&wanted_autogen}, 'lib/t/autogen');
+File::Find::find({wanted => \&wanted_autogen,
+ preprocess => sub {return sort @_}}, 'lib/t/autogen');
sub wanted_autogen { /^autogen.*\.t\z/s && require $_; }
-File::Find::find({wanted => \&wanted_regression}, 'lib/t/regression');
+File::Find::find({wanted => \&wanted_regression,
+ preprocess => sub {return sort @_}}, 'lib/t/regression');
sub wanted_regression { /^*\.t\z/s && require $_; }
require "/opt/rt3/lib/t/03web.pl";
require "/opt/rt3/lib/t/04_send_email.pl";
+require "/opt/rt3/lib/t/05cronsupport.pl";
diff --git a/rt/lib/t/02regression.t.in b/rt/lib/t/02regression.t.in
deleted file mode 100644
index c2e3277a9..000000000
--- a/rt/lib/t/02regression.t.in
+++ /dev/null
@@ -1,47 +0,0 @@
-#!@PERL@
-
-use Test::More qw(no_plan);
-
-use lib "@RT_LIB_PATH@";
-use RT;
-ok(RT::LoadConfig);
-ok(RT::Init, "Basic initialization and DB connectivity");
-
-# Create a new queue
-use_ok(RT::Queue);
-my $q = RT::Queue->new($RT::SystemUser);
-
-$q->Load('regression');
-if ($q->id != 0) {
- die "Regression tests not starting with a clean DB. Bailing";
-}
-
-my ($id, $msg) = $q->Create( Name => 'Regression',
- Description => 'A regression test queue',
- CorrespondAddress => 'correspond@a',
- CommentAddress => 'comment@a');
-
-isnt($id, 0, "Queue was created sucessfully - $msg");
-
-my $q2 = RT::Queue->new($RT::SystemUser);
-
-ok($q2->Load($id));
-is($q2->id, $id, "Sucessfully loaded the queue again");
-is($q2->Name, 'Regression');
-is($q2->Description, 'A regression test queue');
-is($q2->CorrespondAddress, 'correspond@a');
-is($q2->CommentAddress, 'comment@a');
-
-
-use File::Find;
-File::Find::find({wanted => \&wanted_autogen,
- preprocess => sub {return sort @_}}, 'lib/t/autogen');
-sub wanted_autogen { /^autogen.*\.t\z/s && require $_; }
-
-File::Find::find({wanted => \&wanted_regression,
- preprocess => sub {return sort @_}}, 'lib/t/regression');
-sub wanted_regression { /^*\.t\z/s && require $_; }
-
-require "@RT_LIB_PATH@/t/03web.pl";
-require "@RT_LIB_PATH@/t/04_send_email.pl";
-require "@RT_LIB_PATH@/t/05cronsupport.pl";
diff --git a/rt/lib/t/03web.pl b/rt/lib/t/03web.pl
index 94ad3e97e..597ad109e 100644
--- a/rt/lib/t/03web.pl
+++ b/rt/lib/t/03web.pl
@@ -67,7 +67,83 @@ ok( $agent->{'content'} =~ qr{$string} , "Found the content");
# }}}
+# {{{ Query Builder tests
+
+my $response = $agent->get($url."Search/Build.html");
+ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
+
+# Parsing TicketSQL
+#
+# Adding items
+
+# set the first value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "aaa");
+$agent->submit();
+
+# set the next value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "bbb");
+$agent->submit();
+
+ok($agent->form_name('BuildQuery'));
+
+# get the query
+my $query = $agent->current_form->find_input("Query")->value;
+# strip whitespace from ends
+$query =~ s/^\s*//g;
+$query =~ s/\s*$//g;
+
+# collapse other whitespace
+$query =~ s/\s+/ /g;
+
+is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'");
+
+# - new items go one level down
+# - add items at currently selected level
+# - if nothing is selected, add at end, one level down
+#
+# move left
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move left if you're at the top level
+#
+# move right
+# - error if nothing selected
+# - same item should be selected after move
+# - can always move right (no max depth...should there be?)
+#
+# move up
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move up if you're first in the list
+#
+# move down
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move down if you're last in the list
+#
+# toggle
+# - error if nothing selected
+# - change all aggregators in the grouping
+# - don't change any others
+#
+# delete
+# - error if nothing selected
+# - delete currently selected item
+# - delete all children of a grouping
+# - if delete leaves a node with no children, delete that, too
+# - what should be selected?
+#
+# Clear
+# - clears entire query
+# - clears it from the session, too
+# }}}
use File::Find;
find ( \&wanted , 'html/');
@@ -83,7 +159,7 @@ sub test_get {
$file =~ s#^html/##;
ok ($agent->get("$url/$file", "GET $url/$file"));
is ($agent->{'status'}, 200, "Loaded $file");
- ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
+# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file");
ok( $agent->{'content'} !~ /System error/i, "Didn't get a Mason compilation error on $file");
diff --git a/rt/lib/t/03web.pl.in b/rt/lib/t/03web.pl.in
deleted file mode 100644
index 25c26e711..000000000
--- a/rt/lib/t/03web.pl.in
+++ /dev/null
@@ -1,170 +0,0 @@
-#!@PERL@
-
-use strict;
-use WWW::Mechanize;
-use HTTP::Request::Common;
-use HTTP::Cookies;
-use LWP;
-use Encode;
-
-my $cookie_jar = HTTP::Cookies->new;
-my $agent = WWW::Mechanize->new();
-
-# give the agent a place to stash the cookies
-
-$agent->cookie_jar($cookie_jar);
-
-
-# get the top page
-my $url = "http://localhost".$RT::WebPath."/";
-$agent->get($url);
-
-is ($agent->{'status'}, 200, "Loaded a page");
-
-
-# {{{ test a login
-
-# follow the link marked "Login"
-
-ok($agent->{form}->find_input('user'));
-
-ok($agent->{form}->find_input('pass'));
-ok ($agent->{'content'} =~ /username:/i);
-$agent->field( 'user' => 'root' );
-$agent->field( 'pass' => 'password' );
-# the field isn't named, so we have to click link 0
-$agent->click(0);
-is($agent->{'status'}, 200, "Fetched the page ok");
-ok( $agent->{'content'} =~ /Logout/i, "Found a logout link");
-
-
-
-$agent->get($url."Ticket/Create.html?Queue=1");
-is ($agent->{'status'}, 200, "Loaded Create.html");
-$agent->form(3);
-# Start with a string containing characters in latin1
-my $string = "I18N Web Testing æøå";
-Encode::from_to($string, 'iso-8859-1', 'utf8');
-$agent->field('Subject' => "Foo");
-$agent->field('Content' => $string);
-ok($agent->submit(), "Created new ticket with $string");
-
-ok( $agent->{'content'} =~ qr{$string} , "Found the content");
-
-$agent->get($url."Ticket/Create.html?Queue=1");
-is ($agent->{'status'}, 200, "Loaded Create.html");
-$agent->form(3);
-# Start with a string containing characters in latin1
-my $string = "I18N Web Testing æøå";
-Encode::from_to($string, 'iso-8859-1', 'utf8');
-$agent->field('Subject' => $string);
-$agent->field('Content' => "BAR");
-ok($agent->submit(), "Created new ticket with $string");
-
-ok( $agent->{'content'} =~ qr{$string} , "Found the content");
-
-
-
-# }}}
-
-# {{{ Query Builder tests
-
-my $response = $agent->get($url."Search/Build.html");
-ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
-
-# Parsing TicketSQL
-#
-# Adding items
-
-# set the first value
-ok($agent->form_name('BuildQuery'));
-$agent->field("AttachmentField", "Subject");
-$agent->field("AttachmentOp", "LIKE");
-$agent->field("ValueOfAttachment", "aaa");
-$agent->submit();
-
-# set the next value
-ok($agent->form_name('BuildQuery'));
-$agent->field("AttachmentField", "Subject");
-$agent->field("AttachmentOp", "LIKE");
-$agent->field("ValueOfAttachment", "bbb");
-$agent->submit();
-
-ok($agent->form_name('BuildQuery'));
-
-# get the query
-my $query = $agent->current_form->find_input("Query")->value;
-# strip whitespace from ends
-$query =~ s/^\s*//g;
-$query =~ s/\s*$//g;
-
-# collapse other whitespace
-$query =~ s/\s+/ /g;
-
-is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'");
-
-# - new items go one level down
-# - add items at currently selected level
-# - if nothing is selected, add at end, one level down
-#
-# move left
-# - error if nothing selected
-# - same item should be selected after move
-# - can't move left if you're at the top level
-#
-# move right
-# - error if nothing selected
-# - same item should be selected after move
-# - can always move right (no max depth...should there be?)
-#
-# move up
-# - error if nothing selected
-# - same item should be selected after move
-# - can't move up if you're first in the list
-#
-# move down
-# - error if nothing selected
-# - same item should be selected after move
-# - can't move down if you're last in the list
-#
-# toggle
-# - error if nothing selected
-# - change all aggregators in the grouping
-# - don't change any others
-#
-# delete
-# - error if nothing selected
-# - delete currently selected item
-# - delete all children of a grouping
-# - if delete leaves a node with no children, delete that, too
-# - what should be selected?
-#
-# Clear
-# - clears entire query
-# - clears it from the session, too
-
-# }}}
-
-use File::Find;
-find ( \&wanted , 'html/');
-
-sub wanted {
- -f && /\.html$/ && $_ !~ /Logout.html$/ && test_get($File::Find::name);
-}
-
-sub test_get {
- my $file = shift;
-
-
- $file =~ s#^html/##;
- ok ($agent->get("$url/$file", "GET $url/$file"));
- is ($agent->{'status'}, 200, "Loaded $file");
-# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
- ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file");
- ok( $agent->{'content'} !~ /System error/i, "Didn't get a Mason compilation error on $file");
-
-}
-
-# }}}
-
-1;
diff --git a/rt/lib/t/04_send_email.pl b/rt/lib/t/04_send_email.pl
index c384eedfa..973d9d2e2 100644
--- a/rt/lib/t/04_send_email.pl
+++ b/rt/lib/t/04_send_email.pl
@@ -476,6 +476,31 @@ sub crashes_redef_sendmessage {
# }}}
+# {{{ test a multi-line RT-Send-CC header
+
+my $content = `cat /opt/rt3/lib/t/data/rt-send-cc` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+my $cc = $tick->Transactions->First->Attachments->First->GetHeader('RT-Send-Cc');
+ok ($cc =~ /test1/, "Found test 1");
+ok ($cc =~ /test2/, "Found test 2");
+ok ($cc =~ /test3/, "Found test 3");
+ok ($cc =~ /test4/, "Found test 4");
+ok ($cc =~ /test5/, "Found test 5");
+
+# }}}
+
# Don't taint the environment
$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');
1;
diff --git a/rt/lib/t/04_send_email.pl.in b/rt/lib/t/04_send_email.pl.in
deleted file mode 100644
index 39ab0d271..000000000
--- a/rt/lib/t/04_send_email.pl.in
+++ /dev/null
@@ -1,506 +0,0 @@
-#!@PERL@ -w
-
-use strict;
-use RT::EmailParser;
-use RT::Tickets;
-use RT::Action::SendEmail;
-
-my @_outgoing_messages;
-my @scrips_fired;
-
-#We're not testing acls here.
-my $everyone = RT::Group->new($RT::SystemUser);
-$everyone->LoadSystemInternalGroup('Everyone');
-$everyone->PrincipalObj->GrantRight(Right =>'SuperUser');
-
-
-is (__PACKAGE__, 'main', "We're operating in the main package");
-
-
-{
-no warnings qw/redefine/;
-sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
-
- main::_fired_scrip($self->ScripObj);
- main::ok(ref($MIME) eq 'MIME::Entity', "hey, look. it's a mime entity");
-}
-
-}
-
-# instrument SendEmail to pass us what it's about to send.
-# create a regular ticket
-
-my $parser = RT::EmailParser->new();
-
-
-# Let's test to make sure a multipart/report is processed correctly
-my $content = `cat @RT_LIB_PATH@/t/data/multipart-report` || die "couldn't find new content";
-# be as much like the mail gateway as possible.
-use RT::Interface::Email;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /The original message was received/, "It's the bounce");
-
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-
-undef @scrips_fired;
-
-
-
-
-$parser->ParseMIMEEntityFromScalar('From: root@localhost
-To: rt@example.com
-Subject: This is a test of new ticket creation as an unknown user
-
-Blah!
-Foob!');
-
-
-use Data::Dumper;
-
-my $ticket = RT::Ticket->new($RT::SystemUser);
-my ($id, $tid, $msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity);
-ok ($id,$msg);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-ok ($tick->Subject eq 'I18NTest', "failed to create the new ticket from an unprivileged account");
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-# make sure it sends an autoreply
-# make sure it sends a notification to adminccs
-
-
-# we need to swap out SendMessage to test the new things we care about;
-&utf8_redef_sendmessage;
-
-# create an iso 8859-1 ticket
-@scrips_fired = ();
-
-my $content = `cat @RT_LIB_PATH@/t/data/new-ticket-from-iso-8859-1` || die "couldn't find new content";
-
-
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-use RT::Interface::Email;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
-
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-# make sure it sends an autoreply
-
-
-# make sure it sends a notification to adminccs
-
-# If we correspond, does it do the right thing to the outbound messages?
-
-$parser->ParseMIMEEntityFromScalar($content);
-my ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-$parser->ParseMIMEEntityFromScalar($content);
-($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-
-
-
-
-# we need to swap out SendMessage to test the new things we care about;
-&iso8859_redef_sendmessage;
-$RT::EmailOutputEncoding = 'iso-8859-1';
-# create an iso 8859-1 ticket
-@scrips_fired = ();
-
-my $content = `cat @RT_LIB_PATH@/t/data/new-ticket-from-iso-8859-1` || die "couldn't find new content";
-# be as much like the mail gateway as possible.
-use RT::Interface::Email;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
-
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-# make sure it sends an autoreply
-
-
-# make sure it sends a notification to adminccs
-
-
-# If we correspond, does it do the right thing to the outbound messages?
-
-$parser->ParseMIMEEntityFromScalar($content);
-my ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-$parser->ParseMIMEEntityFromScalar($content);
-($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-
-sub _fired_scrip {
- my $scrip = shift;
- push @scrips_fired, $scrip;
-}
-
-sub utf8_redef_sendmessage {
- no warnings qw/redefine/;
- eval '
- sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
-
- my $scrip = $self->ScripObj->id;
- ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
- main::_fired_scrip($self->ScripObj);
- $MIME->make_singlepart;
- main::ok( ref($MIME) eq \'MIME::Entity\',
- "hey, look. it\'s a mime entity" );
- main::ok( ref( $MIME->head ) eq \'MIME::Head\',
- "its mime header is a mime header. yay" );
- main::ok( $MIME->head->get(\'Content-Type\') =~ /utf-8/,
- "Its content type is utf-8" );
- my $message_as_string = $MIME->bodyhandle->as_string();
- use Encode;
- $message_as_string = Encode::decode_utf8($message_as_string);
- main::ok(
- $message_as_string =~ /H\x{e5}vard/,
-"The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
-
- }';
-}
-
-sub iso8859_redef_sendmessage {
- no warnings qw/redefine/;
- eval '
- sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
-
- my $scrip = $self->ScripObj->id;
- ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
- main::_fired_scrip($self->ScripObj);
- $MIME->make_singlepart;
- main::ok( ref($MIME) eq \'MIME::Entity\',
- "hey, look. it\'s a mime entity" );
- main::ok( ref( $MIME->head ) eq \'MIME::Head\',
- "its mime header is a mime header. yay" );
- main::ok( $MIME->head->get(\'Content-Type\') =~ /iso-8859-1/,
- "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") );
- my $message_as_string = $MIME->bodyhandle->as_string();
- use Encode;
- $message_as_string = Encode::decode("iso-8859-1",$message_as_string);
- main::ok(
- $message_as_string =~ /H\x{e5}vard/, "The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
-
- }';
-}
-
-# {{{ test a multipart alternative containing a text-html part with an umlaut
-
-my $content = `cat @RT_LIB_PATH@/t/data/multipart-alternative-with-umlaut` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&umlauts_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /causes Error/, "We recorded the content right as text-plain");
-is ($tick->Transactions->First->Attachments->Count , 3 , "Has three attachments, presumably a text-plain, a text-html and a multipart alternative");
-
-sub umlauts_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage { }';
-}
-
-# }}}
-
-# {{{ test a text-html message with an umlaut
-
-my $content = `cat @RT_LIB_PATH@/t/data/text-html-with-umlaut` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_html_umlauts_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Attachments->First->Content =~ /causes Error/, "We recorded the content as containing 'causes error'");
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/html/, "We recorded the content as text/html");
-ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
-
-sub text_html_umlauts_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- use Data::Dumper;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
- is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
- is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
- is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
- }';
-}
-
-# }}}
-
-# {{{ test a text-html message with russian characters
-
-my $content = `cat @RT_LIB_PATH@/t/data/text-html-in-russian` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_html_russian_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/html/, "We recorded the content right as text-html");
-ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
-
-sub text_html_russian_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- use Data::Dumper;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
- is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
- is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
- is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
- my $content_1251;
- $content_1251 = $MIME->parts(1)->bodyhandle->as_string();
- ok ($content_1251 =~ qr{Ó÷eáíûé Öeíòp "ÊÀÄÐÛ ÄÅËÎÂÎÃÎ ÌÈÐÀ" ïpèãëaøaeò ía òpeíèíã:},
-"Content matches drugim in codepage 1251" );
- }';
-}
-
-# }}}
-
-# {{{ test a message containing a russian subject and NO content type
-
-unshift (@RT::EmailInputEncodings, 'koi8-r');
-$RT::EmailOutputEncoding = 'koi8-r';
-my $content = `cat @RT_LIB_PATH@/t/data/russian-subject-no-content-type` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_plain_russian_redef_sendmessage;
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/plain/, "We recorded the content type right");
-ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-plain");
-is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right");
-sub text_plain_russian_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- is ($MIME->head->mime_type , "text/plain", "The only part is text/plain ");
- my $subject = $MIME->head->get("subject");
- chomp($subject);
- #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly");
- };
- ';
-}
-
-shift @RT::EmailInputEncodings;
-$RT::EmailOutputEncoding = 'utf-8';
-# }}}
-
-
-# {{{ test a message containing a nested RFC 822 message
-
-my $content = `cat @RT_LIB_PATH@/t/data/nested-rfc-822` || die "couldn't find new content";
-ok ($content, "Loaded nested-rfc-822 to test");
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_plain_nested_redef_sendmessage;
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-is ($tick->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?");
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /multipart\/mixed/, "We recorded the content type right");
-is ($tick->Transactions->First->Attachments->Count , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain");
-sub text_plain_nested_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart");
- my $subject = $MIME->head->get("subject");
- $subject = MIME::Base64::decode_base64( $subject);
- chomp($subject);
- # TODO, why does this test fail
- #ok($subject =~ qr{Niv\x{e5}er}, "The subject matches the word - $subject");
- 1;
- }';
-}
-
-# }}}
-
-
-# {{{ test a multipart alternative containing a uuencoded mesage generated by lotus notes
-
-my $content = `cat @RT_LIB_PATH@/t/data/notes-uuencoded` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&notes_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /from Lotus Notes/, "We recorded the content right");
-is ($tick->Transactions->First->Attachments->Count , 3 , "Has three attachments");
-
-sub notes_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage { }';
-}
-
-# }}}
-
-# {{{ test a multipart that crashes the file-based mime-parser works
-
-my $content = `cat @RT_LIB_PATH@/t/data/crashes-file-based-parser` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&crashes_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /FYI/, "We recorded the content right");
-is ($tick->Transactions->First->Attachments->Count , 5 , "Has three attachments");
-
-sub crashes_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage { }';
-}
-
-
-
-# }}}
-
-# {{{ test a multi-line RT-Send-CC header
-
-my $content = `cat @RT_LIB_PATH@/t/data/rt-send-cc` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-my $cc = $tick->Transactions->First->Attachments->First->GetHeader('RT-Send-Cc');
-ok ($cc =~ /test1/, "Found test 1");
-ok ($cc =~ /test2/, "Found test 2");
-ok ($cc =~ /test3/, "Found test 3");
-ok ($cc =~ /test4/, "Found test 4");
-ok ($cc =~ /test5/, "Found test 5");
-
-# }}}
-
-# Don't taint the environment
-$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');
-1;
diff --git a/rt/lib/t/05cronsupport.pl.in b/rt/lib/t/05cronsupport.pl.in
deleted file mode 100644
index a6b3d7451..000000000
--- a/rt/lib/t/05cronsupport.pl.in
+++ /dev/null
@@ -1,84 +0,0 @@
-#!@PERL@ -w
-
-use strict;
-
-### Set up some testing data. Test the testing data because why not?
-
-# Create a user with rights, a queue, and some tickets.
-my $user_obj = RT::User->new($RT::SystemUser);
-my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('tara@example.com');
-ok($ret, 'record test user creation');
-$user_obj->SetName('tara');
-$user_obj->PrincipalObj->GrantRight(Right => 'SuperUser');
-my $CurrentUser = RT::CurrentUser->new('tara');
-
-# Create our template, which will be used for tests of RT::Action::Record*.
-
-my $template_content = 'RT-Send-Cc: tla@example.com
-RT-Send-Bcc: jesse@example.com
-
-This is a content string with no content.';
-
-my $template_obj = RT::Template->new($CurrentUser);
-$template_obj->Create(Queue => '0',
- Name => 'recordtest',
- Description => 'testing Record actions',
- Content => $template_content,
- );
-
-# Create a queue and some tickets.
-
-my $queue_obj = RT::Queue->new($CurrentUser);
-($ret, $msg) = $queue_obj->Create(Name => 'recordtest', Description => 'queue for Action::Record testing');
-ok($ret, 'record test queue creation');
-
-my $ticket1 = RT::Ticket->new($CurrentUser);
-my ($id, $tobj, $msg2) = $ticket1->Create(Queue => $queue_obj,
- Requestor => ['tara@example.com'],
- Subject => 'bork bork bork',
- Priority => 22,
- );
-ok($id, 'record test ticket creation 1');
-my $ticket2 = RT::Ticket->new($CurrentUser);
-($id, $tobj, $msg2) = $ticket2->Create(Queue => $queue_obj,
- Requestor => ['root@localhost'],
- Subject => 'hurdy gurdy'
- );
-ok($id, 'record test ticket creation 2');
-
-
-### OK. Have data, will travel.
-
-# First test the search.
-
-ok(require RT::Search::FromSQL, "Search::FromSQL loaded");
-my $ticketsqlstr = "Requestor.EmailAddress = '" . $CurrentUser->EmailAddress .
- "' AND Priority > '20'";
-my $search = RT::Search::FromSQL->new(Argument => $ticketsqlstr, TicketsObj => RT::Tickets->new($CurrentUser),
- );
-is(ref($search), 'RT::Search::FromSQL', "search created");
-ok($search->Prepare(), "fromsql search run");
-my $counter = 0;
-while(my $t = $search->TicketsObj->Next() ) {
- is($t->Id(), $ticket1->Id(), "fromsql search results 1");
- $counter++;
-}
-is ($counter, 1, "fromsql search results 2");
-
-# Right. Now test the actions.
-
-ok(require RT::Action::RecordComment);
-ok(require RT::Action::RecordCorrespondence);
-
-my ($comment_act, $correspond_act);
-ok($comment_act = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordComment created");
-ok($correspond_act = RT::Action::RecordCorrespondence->new(TicketObj => $ticket2, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordCorrespondence created");
-ok($comment_act->Prepare(), "Comment prepared");
-ok($correspond_act->Prepare(), "Correspond prepared");
-ok($comment_act->Commit(), "Comment committed");
-ok($correspond_act->Commit(), "Correspondence committed");
-
-# Now test for loop suppression.
-my ($trans, $desc, $transaction) = $ticket2->Comment(MIMEObj => $template_obj->MIMEObj);
-my $bogus_action = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, TransactionObj => $transaction, CurrentUser => $CurrentUser);
-ok(!$bogus_action->Prepare(), "Comment aborted to prevent loop");
diff --git a/rt/lib/t/regression/00placeholder b/rt/lib/t/regression/00placeholder
deleted file mode 100644
index 0afc6045c..000000000
--- a/rt/lib/t/regression/00placeholder
+++ /dev/null
@@ -1 +0,0 @@
-1;