summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2014-05-27 15:20:05 -0700
committerIvan Kohler <ivan@freeside.biz>2014-05-27 15:20:05 -0700
commitc5922713bc199600bc8c203155a4016c9d24cbbc (patch)
tree1b9ec8323fdab801c4ca6aaf74234bfe8f74c56c
parent7c841dec307feed06ee532ac18e4114ef68243a8 (diff)
rt 4.0.20 (RT#13852)
-rwxr-xr-xrt/configure25
-rw-r--r--rt/configure.ac1
-rw-r--r--rt/docs/UPGRADING-4.03
-rw-r--r--rt/docs/extending/clickable_links.pod11
-rw-r--r--rt/etc/RT_Config.pm.in5
-rw-r--r--rt/etc/upgrade/3.8.9/content1
-rw-r--r--rt/lib/RT.pm4
-rw-r--r--rt/lib/RT/Config.pm13
-rw-r--r--rt/lib/RT/Date.pm3
-rw-r--r--rt/lib/RT/Generated.pm2
-rw-r--r--rt/lib/RT/Handle.pm41
-rw-r--r--rt/lib/RT/Interface/REST.pm2
-rw-r--r--rt/lib/RT/Interface/Web.pm2
-rw-r--r--rt/lib/RT/Interface/Web/Handler.pm2
-rw-r--r--rt/lib/RT/Lifecycle.pm4
-rw-r--r--rt/lib/RT/Shredder/Plugin/SQLDump.pm4
-rw-r--r--rt/lib/RT/StyleGuide.pod207
-rwxr-xr-xrt/lib/RT/Template.pm6
-rw-r--r--rt/lib/RT/Test.pm70
-rwxr-xr-xrt/lib/RT/User.pm24
-rwxr-xr-xrt/lib/RT/Users.pm22
-rw-r--r--rt/sbin/rt-server.fcgi.in1
-rw-r--r--rt/sbin/rt-server.in1
-rw-r--r--rt/sbin/rt-setup-fulltext-index.in10
-rw-r--r--rt/sbin/rt-test-dependencies.in6
-rw-r--r--rt/sbin/rt-validator.in4
-rw-r--r--rt/sbin/standalone_httpd.in1
-rw-r--r--rt/share/html/Dashboards/Queries.html2
-rw-r--r--rt/share/html/Elements/CollectionAsTable/ParseFormat3
-rw-r--r--rt/share/html/Elements/EditCustomFieldAutocomplete9
-rw-r--r--rt/share/html/Elements/EditCustomFieldDateTime3
-rw-r--r--rt/share/html/Elements/EditCustomFieldFreeform15
-rw-r--r--rt/share/html/Elements/EditCustomFieldSelect4
-rw-r--r--rt/share/html/Elements/EditCustomFieldText18
-rw-r--r--rt/share/html/Elements/EditCustomFieldWikitext18
-rw-r--r--rt/share/html/Elements/MakeClicky48
-rw-r--r--rt/share/html/Elements/MyRT8
-rw-r--r--rt/share/html/Elements/ShowLink2
-rwxr-xr-xrt/share/html/Elements/SimpleSearch3
-rw-r--r--rt/share/html/Install/DatabaseDetails.html1
-rw-r--r--rt/share/html/NoAuth/js/cascaded.js4
-rw-r--r--rt/share/html/Prefs/MyRT.html20
-rw-r--r--rt/share/html/Prefs/Search.html15
-rwxr-xr-xrt/share/html/Search/Bulk.html29
-rw-r--r--rt/share/html/Search/Chart2
-rw-r--r--rt/share/html/Search/Chart.html2
-rw-r--r--rt/share/html/Search/Elements/BuildFormatString10
-rw-r--r--rt/share/html/Search/Elements/Chart2
-rwxr-xr-xrt/share/html/Search/Results.html4
-rw-r--r--rt/t/api/date.t10
-rw-r--r--rt/t/api/password-types.t11
-rw-r--r--rt/t/fts/indexed_mysql.t2
-rw-r--r--rt/t/pod.t2
-rw-r--r--rt/t/validator/group_members.t116
-rw-r--r--rt/t/web/path-traversal.t11
55 files changed, 491 insertions, 358 deletions
diff --git a/rt/configure b/rt/configure
index 3abb324..60d9fb1 100755
--- a/rt/configure
+++ b/rt/configure
@@ -1,7 +1,7 @@
#! /bin/sh
# From configure.ac Revision.
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for RT rt-4.0.19.
+# Generated by GNU Autoconf 2.68 for RT rt-4.0.20.
#
# Report bugs to <rt-bugs@bestpractical.com>.
#
@@ -560,8 +560,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='RT'
PACKAGE_TARNAME='rt'
-PACKAGE_VERSION='rt-4.0.19'
-PACKAGE_STRING='RT rt-4.0.19'
+PACKAGE_VERSION='rt-4.0.20'
+PACKAGE_STRING='RT rt-4.0.20'
PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
PACKAGE_URL=''
@@ -1311,7 +1311,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures RT rt-4.0.19 to adapt to many kinds of systems.
+\`configure' configures RT rt-4.0.20 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1372,7 +1372,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of RT rt-4.0.19:";;
+ short | recursive ) echo "Configuration of RT rt-4.0.20:";;
esac
cat <<\_ACEOF
@@ -1496,7 +1496,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-RT configure rt-4.0.19
+RT configure rt-4.0.20
generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1597,7 +1597,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by RT $as_me rt-4.0.19, which was
+It was created by RT $as_me rt-4.0.20, which was
generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@
@@ -1954,7 +1954,7 @@ rt_version_major=4
rt_version_minor=0
-rt_version_patch=19
+rt_version_patch=20
test "x$rt_version_major" = 'x' && rt_version_major=0
test "x$rt_version_minor" = 'x' && rt_version_minor=0
@@ -3923,7 +3923,7 @@ RT_LOG_PATH_R=${exp_logfiledir}
fi
-ac_config_files="$ac_config_files etc/upgrade/3.8-ical-extension etc/upgrade/split-out-cf-categories etc/upgrade/generate-rtaddressregexp etc/upgrade/upgrade-articles etc/upgrade/vulnerable-passwords sbin/rt-attributes-viewer sbin/rt-preferences-viewer sbin/rt-session-viewer sbin/rt-dump-metadata sbin/rt-setup-database sbin/rt-test-dependencies sbin/rt-email-digest sbin/rt-email-dashboards sbin/rt-clean-sessions sbin/rt-shredder sbin/rt-validator sbin/rt-validate-aliases sbin/rt-email-group-admin sbin/rt-server sbin/rt-server.fcgi sbin/standalone_httpd sbin/rt-setup-fulltext-index sbin/rt-fulltext-indexer bin/rt-crontool bin/rt-mailgate bin/rt"
+ac_config_files="$ac_config_files etc/upgrade/3.8-ical-extension etc/upgrade/4.0-customfield-checkbox-extension etc/upgrade/split-out-cf-categories etc/upgrade/generate-rtaddressregexp etc/upgrade/upgrade-articles etc/upgrade/vulnerable-passwords sbin/rt-attributes-viewer sbin/rt-preferences-viewer sbin/rt-session-viewer sbin/rt-dump-metadata sbin/rt-setup-database sbin/rt-test-dependencies sbin/rt-email-digest sbin/rt-email-dashboards sbin/rt-clean-sessions sbin/rt-shredder sbin/rt-validator sbin/rt-validate-aliases sbin/rt-email-group-admin sbin/rt-server sbin/rt-server.fcgi sbin/standalone_httpd sbin/rt-setup-fulltext-index sbin/rt-fulltext-indexer bin/rt-crontool bin/rt-mailgate bin/rt"
ac_config_files="$ac_config_files Makefile etc/RT_Config.pm lib/RT/Generated.pm t/data/configs/apache2.2+mod_perl.conf t/data/configs/apache2.2+fastcgi.conf"
@@ -4482,7 +4482,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by RT $as_me rt-4.0.19, which was
+This file was extended by RT $as_me rt-4.0.20, which was
generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -4535,7 +4535,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-RT config.status rt-4.0.19
+RT config.status rt-4.0.20
configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\"
@@ -4647,6 +4647,7 @@ for ac_config_target in $ac_config_targets
do
case $ac_config_target in
"etc/upgrade/3.8-ical-extension") CONFIG_FILES="$CONFIG_FILES etc/upgrade/3.8-ical-extension" ;;
+ "etc/upgrade/4.0-customfield-checkbox-extension") CONFIG_FILES="$CONFIG_FILES etc/upgrade/4.0-customfield-checkbox-extension" ;;
"etc/upgrade/split-out-cf-categories") CONFIG_FILES="$CONFIG_FILES etc/upgrade/split-out-cf-categories" ;;
"etc/upgrade/generate-rtaddressregexp") CONFIG_FILES="$CONFIG_FILES etc/upgrade/generate-rtaddressregexp" ;;
"etc/upgrade/upgrade-articles") CONFIG_FILES="$CONFIG_FILES etc/upgrade/upgrade-articles" ;;
@@ -5099,6 +5100,8 @@ which seems to be undefined. Please make sure it is defined" >&2;}
case $ac_file$ac_mode in
"etc/upgrade/3.8-ical-extension":F) chmod ug+x $ac_file
;;
+ "etc/upgrade/4.0-customfield-checkbox-extension":F) chmod ug+x $ac_file
+ ;;
"etc/upgrade/split-out-cf-categories":F) chmod ug+x $ac_file
;;
"etc/upgrade/generate-rtaddressregexp":F) chmod ug+x $ac_file
diff --git a/rt/configure.ac b/rt/configure.ac
index 47ec7c9..e9f7d5f 100644
--- a/rt/configure.ac
+++ b/rt/configure.ac
@@ -408,6 +408,7 @@ dnl Configure the output files, and generate them.
dnl Binaries that should be +x
AC_CONFIG_FILES([
etc/upgrade/3.8-ical-extension
+ etc/upgrade/4.0-customfield-checkbox-extension
etc/upgrade/split-out-cf-categories
etc/upgrade/generate-rtaddressregexp
etc/upgrade/upgrade-articles
diff --git a/rt/docs/UPGRADING-4.0 b/rt/docs/UPGRADING-4.0
index 63dd2ee..766964f 100644
--- a/rt/docs/UPGRADING-4.0
+++ b/rt/docs/UPGRADING-4.0
@@ -32,6 +32,9 @@ If you deploy RT with mod_perl, Apache will no longer start with C<SetHandler>
set to `perl-script`. F<docs/web_deployment.pod> contains the
new configuration.
+RT::Extension::CustomField::Checkbox has been integrated into core, so you
+MUST uninstall it before upgrading. In addition, you must run
+etc/upgrade/4.0-customfield-checkbox-extension script to convert old data.
=head2 RT_SiteConfig.pm
diff --git a/rt/docs/extending/clickable_links.pod b/rt/docs/extending/clickable_links.pod
index 91e9eec..dd80ff1 100644
--- a/rt/docs/extending/clickable_links.pod
+++ b/rt/docs/extending/clickable_links.pod
@@ -89,11 +89,20 @@ add action types. Values are subroutine references which will get
called when needed. They should return the modified string. Note that
subroutine B<must escape> HTML.
-=item handler
+=item handle
A subroutine reference; modify it only if you have to. This can be used
to add pre- or post-processing around all actions.
+=item cache
+
+An undefined variable that should be replaced with a subroutine
+reference. This subroutine will be called twice, once with the arguments
+fetch => content_ref and once with store => content_ref. In the fetch
+case, if a cached copy is found, return the cached content, otherwise
+return a false value. When passed store, you should populate your cache
+with the content. The return value is ignored in this case.
+
=back
=head2 Actions' arguments
diff --git a/rt/etc/RT_Config.pm.in b/rt/etc/RT_Config.pm.in
index a52965a..dace2d7 100644
--- a/rt/etc/RT_Config.pm.in
+++ b/rt/etc/RT_Config.pm.in
@@ -296,8 +296,9 @@ Set(@LogToSyslogConf, ());
=item C<$EmailSubjectTagRegex>
This regexp controls what subject tags RT recognizes as its own. If
-you're not dealing with historical C<$rtname> values, you'll likely
-never have to change this configuration.
+you're not dealing with historical C<$rtname> values, or historical
+queue-specific subject tags, you'll likely never have to change this
+configuration.
Be B<very careful> with it. Note that it overrides C<$rtname> for
subject token matching and that you should use only "non-capturing"
diff --git a/rt/etc/upgrade/3.8.9/content b/rt/etc/upgrade/3.8.9/content
index 898c19e..d7d64f5 100644
--- a/rt/etc/upgrade/3.8.9/content
+++ b/rt/etc/upgrade/3.8.9/content
@@ -56,6 +56,7 @@
s!(?<=Your ticket has been (?:approved|rejected) by { eval { )\$Approval->OwnerObj->Name!\$Approver->Name!
)
{
+ $template->SetType('Perl');
$template->SetContent($content);
}
}
diff --git a/rt/lib/RT.pm b/rt/lib/RT.pm
index 0f0c79a..e71d6c9 100644
--- a/rt/lib/RT.pm
+++ b/rt/lib/RT.pm
@@ -707,7 +707,9 @@ sub InitPluginPaths {
my @tmp_inc;
my $added;
for (@INC) {
- if ( Cwd::realpath($_) eq $RT::LocalLibPath) {
+ my $realpath = Cwd::realpath($_);
+ next unless defined $realpath;
+ if ( $realpath eq $RT::LocalLibPath) {
push @tmp_inc, $_, @lib_dirs;
$added = 1;
} else {
diff --git a/rt/lib/RT/Config.pm b/rt/lib/RT/Config.pm
index fee6c51..62aae1c 100644
--- a/rt/lib/RT/Config.pm
+++ b/rt/lib/RT/Config.pm
@@ -1219,11 +1219,14 @@ sub SetFromConfig {
my $entry = ${$pack}{$k};
next unless $entry;
- # get entry for type we are looking for
- # XXX skip references to scalars or other references.
- # Otherwie 5.10 goes boom. maybe we should skip any
- # reference
- next if ref($entry) eq 'SCALAR' || ref($entry) eq 'REF';
+ # Inlined constants are simplified in the symbol table --
+ # namely, when possible, you only get a reference back in
+ # $entry, rather than a full GLOB. In 5.10, scalar
+ # constants began being inlined this way; starting in 5.20,
+ # list constants are also inlined. Notably, ref(GLOB) is
+ # undef, but inlined constants are currently either REF,
+ # SCALAR, or ARRAY.
+ next if ref($entry);
my $ref_type = ref($ref);
diff --git a/rt/lib/RT/Date.pm b/rt/lib/RT/Date.pm
index db56cfe..52bdc01 100644
--- a/rt/lib/RT/Date.pm
+++ b/rt/lib/RT/Date.pm
@@ -509,7 +509,8 @@ Returns new unix time.
sub AddDays {
my $self = shift;
- my $days = shift || 1;
+ my $days = shift;
+ $days = 1 unless defined $days;
return $self->AddSeconds( $days * $DAY );
}
diff --git a/rt/lib/RT/Generated.pm b/rt/lib/RT/Generated.pm
index 5edd7e3..eee892d 100644
--- a/rt/lib/RT/Generated.pm
+++ b/rt/lib/RT/Generated.pm
@@ -50,7 +50,7 @@ package RT;
use warnings;
use strict;
-our $VERSION = '4.0.19';
+our $VERSION = '4.0.20';
diff --git a/rt/lib/RT/Handle.pm b/rt/lib/RT/Handle.pm
index 4ea1576..e6ecdda 100644
--- a/rt/lib/RT/Handle.pm
+++ b/rt/lib/RT/Handle.pm
@@ -246,7 +246,7 @@ sub CheckIntegrity {
return (0, 'no nobody user', "Couldn't find Nobody user in the DB '". $self->DSN ."'");
}
- return $RT::Handle->dbh;
+ return 1;
}
sub CheckCompatibility {
@@ -768,9 +768,9 @@ sub InsertData {
);
# Slurp in stuff to insert from the datafile. Possible things to go in here:-
- our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
+ our (@Groups, @Users, @Members, @ACL, @Queues, @ScripActions, @ScripConditions,
@Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
- local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
+ local (@Groups, @Users, @Members, @ACL, @Queues, @ScripActions, @ScripConditions,
@Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
local $@;
@@ -790,7 +790,9 @@ sub InsertData {
$RT::Logger->debug("Creating groups...");
foreach my $item (@Groups) {
my $new_entry = RT::Group->new( RT->SystemUser );
+ $item->{Domain} ||= 'UserDefined';
my $member_of = delete $item->{'MemberOf'};
+ my $members = delete $item->{'Members'};
my ( $return, $msg ) = $new_entry->_Create(%$item);
unless ( $return ) {
$RT::Logger->error( $msg );
@@ -829,6 +831,12 @@ sub InsertData {
}
}
}
+ push @Members, map { +{Group => $new_entry->id,
+ Class => "RT::User", Name => $_} }
+ @{ $members->{Users} || [] };
+ push @Members, map { +{Group => $new_entry->id,
+ Class => "RT::Group", Name => $_} }
+ @{ $members->{Groups} || [] };
}
$RT::Logger->debug("done.");
}
@@ -848,6 +856,33 @@ sub InsertData {
}
$RT::Logger->debug("done.");
}
+ if ( @Members ) {
+ $RT::Logger->debug("Adding users and groups to groups...");
+ for my $item (@Members) {
+ my $group = RT::Group->new(RT->SystemUser);
+ $group->LoadUserDefinedGroup( delete $item->{Group} );
+ unless ($group->Id) {
+ RT->Logger->error("Unable to find group '$group' to add members to");
+ next;
+ }
+
+ my $class = delete $item->{Class} || 'RT::User';
+ my $member = $class->new( RT->SystemUser );
+ $item->{Domain} = 'UserDefined' if $member->isa("RT::Group");
+ $member->LoadByCols( %$item );
+ unless ($member->Id) {
+ RT->Logger->error("Unable to find $class '".($item->{id} || $item->{Name})."' to add to ".$group->Name);
+ next;
+ }
+
+ my ( $return, $msg) = $group->AddMember( $member->PrincipalObj->Id );
+ unless ( $return ) {
+ $RT::Logger->error( $msg );
+ } else {
+ $RT::Logger->debug( $return ."." );
+ }
+ }
+ }
if ( @Queues ) {
$RT::Logger->debug("Creating queues...");
for my $item (@Queues) {
diff --git a/rt/lib/RT/Interface/REST.pm b/rt/lib/RT/Interface/REST.pm
index 17fe446..06d7f83 100644
--- a/rt/lib/RT/Interface/REST.pm
+++ b/rt/lib/RT/Interface/REST.pm
@@ -328,7 +328,7 @@ sub process_attachments {
Path => $tmp_fn,
Type => $info->{'Content-Type'} || guess_media_type($tmp_fn),
Filename => $file,
- Disposition => "attachment",
+ Disposition => $info->{'Content-Disposition'} || "attachment",
);
$new_entity->bodyhandle->{'_dirty_hack_to_save_a_ref_tmp_fh'} = $tmp_fh;
$i++;
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
index 409cbdc..59d3154 100644
--- a/rt/lib/RT/Interface/Web.pm
+++ b/rt/lib/RT/Interface/Web.pm
@@ -962,7 +962,7 @@ not contain a slash-dot C</.>, and does not contain any nulls.
sub ComponentPathIsSafe {
my $self = shift;
my $path = shift;
- return $path !~ m{(?:^|/)\.} and $path !~ m{\0};
+ return($path !~ m{(?:^|/)\.} and $path !~ m{\0});
}
=head2 PathIsSafe
diff --git a/rt/lib/RT/Interface/Web/Handler.pm b/rt/lib/RT/Interface/Web/Handler.pm
index 37031b1..07e7707 100644
--- a/rt/lib/RT/Interface/Web/Handler.pm
+++ b/rt/lib/RT/Interface/Web/Handler.pm
@@ -278,7 +278,7 @@ sub PSGIApp {
# CGI.pm normalizes .. out of paths so when you requested
# /NoAuth/../Ticket/Display.html we saw Ticket/Display.html
# PSGI doesn't normalize .. so we have to deal ourselves.
- if ( $req->path_info =~ m{/\.} ) {
+ if ( $req->path_info =~ m{(^|/)\.\.?(/|$)} ) {
$RT::Logger->crit("Invalid request for ".$req->path_info." aborting");
my $res = Plack::Response->new(400);
return $self->_psgi_response_cb($res->finalize,sub { $self->CleanupRequest });
diff --git a/rt/lib/RT/Lifecycle.pm b/rt/lib/RT/Lifecycle.pm
index accef22..bdb2ba6 100644
--- a/rt/lib/RT/Lifecycle.pm
+++ b/rt/lib/RT/Lifecycle.pm
@@ -298,7 +298,7 @@ sub IsActive {
return 0;
}
-=head3 inactive
+=head3 Inactive
Returns an array of all inactive statuses for this lifecycle.
@@ -309,7 +309,7 @@ sub Inactive {
return $self->Valid('inactive');
}
-=head3 is_inactive
+=head3 IsInactive
Takes a value and returns true if value is a valid inactive status.
Otherwise, returns false.
diff --git a/rt/lib/RT/Shredder/Plugin/SQLDump.pm b/rt/lib/RT/Shredder/Plugin/SQLDump.pm
index cc0d4cc..d12cf0b 100644
--- a/rt/lib/RT/Shredder/Plugin/SQLDump.pm
+++ b/rt/lib/RT/Shredder/Plugin/SQLDump.pm
@@ -89,8 +89,8 @@ sub Run
my $query = $args{'Object'}->_AsInsertQuery;
$query .= "\n" unless $query =~ /\n$/;
- return print $fh $query or return (0, "Couldn't write to filehandle");
- return 1;
+ return 1 if print $fh $query;
+ return (0, "Couldn't write to filehandle");
}
1;
diff --git a/rt/lib/RT/StyleGuide.pod b/rt/lib/RT/StyleGuide.pod
index d958c87..8fdfc7b 100644
--- a/rt/lib/RT/StyleGuide.pod
+++ b/rt/lib/RT/StyleGuide.pod
@@ -2,6 +2,10 @@
RT::StyleGuide - RT Style Guide
+=head1 CAVEATS
+
+This file is somewhat out of date; L<hacking> takes precedence over it.
+
=head1 INTRODUCTION
All code and documentation that is submitted to be included in the RT
@@ -92,21 +96,9 @@ versions. Examples:
1.1.0 First development release of RT 1.2 (or 2.0)
2.0.0 First release of RT 2
-Versions can be modified with a hyphen followed by some text, for
-special versions, or to give extra information. Examples:
-
- 2.0.0-pre1 Notes that this is not final, but preview
-
-In perl 5.6.0, you can have versions like C<v2.0.0>, but this is not
-allowed in previous versions of perl. So to convert a tuple version
-string to a string to use with $VERSION, use a regular integer for
-the revision, and three digits for version and subversion. Examples:
+Versions may end in "rc" and a number if they are release candidates:
- 1.1.6 -> 1.001006
- 2.0.0 -> 2.000000
-
-This way, perl can use the version strings in greater-than and
-less-than comparisons.
+ 2.0.0rc1 First release candiate for real 2.0.0
=head2 Comments
@@ -152,14 +144,6 @@ local() may also be used on elements of arrays and hashes, though there
is seldom a need to do it, and you shouldn't.
-=head2 Exporting
-
-Do not export anything from a module by default. Feel free to put
-anything you want to in @EXPORT_OK, so users of your modules can
-explicitly ask for symbols (e.g., "use Something::Something qw(getFoo
-setFoo)"), but do not export them by default.
-
-
=head2 Pass by Reference
Arrays and hashes should be passed to and from functions by reference
@@ -185,58 +169,6 @@ Although, usually, this is better (faster, easier to read, etc.):
We need to talk about Class::ReturnValue here.
-=head2 Garbage Collection
-
-Perl does pretty good garbage collection for you. It will automatically
-clean up lexical variables that have gone out of scope and objects whose
-references have gone away. Normally you don't need to worry about
-cleaning up after yourself, if using lexicals.
-
-However, some glue code, code compiled in C and linked to Perl, might
-not automatically clean up for you. In such cases, clean up for
-yourself. If there is a method in that glue to dispose or destruct,
-then use it as appropriate.
-
-Also, if you have a long-running function that has a large data
-structure in it, it is polite to free up the memory as soon as you are
-done with it, if possible.
-
- my $huge_data_structure = get_huge_data_structure();
- do_something_with($huge_data_structure);
- undef $huge_data_structure;
-
-=head2 DESTROY
-
-All object classes must provide a DESTROY method. If it won't do
-anything, provide it anyway:
-
- sub DESTROY { }
-
-
-
-=head2 die() and exit()
-
-Don't do it. Do not die() or exit() from a web template or module. Do
-not call C<kill 9, $$>. Don't do it.
-
-In command-line programs, do as you please.
-
-
-=head2 shift and @_
-
-Do not use @_. Use shift. shift may take more lines, but Jesse thinks it
-leads to cleaner code.
-
- my $var = shift; # right
- my($var) = @_; # ick. no
- sub foo { uc $_[0] } # icky. sometimes ok.
-
-
- my($var1, $var2) = (shift, shift); # Um, no.
-
- my $var1 = shift; # right
- my $var2 = shift;
-
=head2 Method parameters
If a method takes exactly one mandatory argument, the argument should be
@@ -249,15 +181,17 @@ In all other cases, the method needs to take named parameters, usually
using a C<%args> hash to store them:
my $self = shift;
- my %args = ( Name => undef,
- Description => undef,
- @_ );
+ my %args = (
+ Name => undef,
+ Description => undef,
+ @_
+ );
You may specify defaults to those named parameters instead of using
C<undef> above, as long as it is documented as such.
It is worth noting that the existing RT codebase had not followed this
-style perfectly; we are trying to fix it without breaking exsiting APIs.
+style perfectly; we are trying to fix it without breaking existing APIs.
=head2 Tests
@@ -332,17 +266,6 @@ document, too.
=over 4
-=item RT the name
-
-"RT" is the name of the project. "RT" is, optionally, the
-specific name for the actual file distribution. That's it.
-
-While we sometimes use "RT2" or "RT3", that's shortand that's really
-not recommended. The name of the project is "RT".
-
-To specify a major version, use "RT 3.0".
-To specify a specific release, use "RT 3.0.12"
-
=item function vs. sub(routine) vs. method
Just because it is the Perl Way (not necessarily right for all
@@ -435,9 +358,9 @@ clear what is going on, or when it is required (such as with
map() and grep()).
for (@list) {
- print; # OK; everyone knows this one
- print uc; # wrong; few people know this
- print uc $_; # better
+ print; # OK; everyone knows this one
+ print uc; # wrong; few people know this
+ print uc $_; # better
}
Note that the special variable C<_> I<should> be used when possible.
@@ -448,9 +371,9 @@ C<_> for subsequent uses, is a performance hit. You should be
careful that the last-tested file is what you think it is, though.
if (-d $file) { # $file is a directory
- # ...
+ # ...
} elsif (-l _) { # $file is a symlink
- # ...
+ # ...
}
Package names begin with a capital letter in each word, followed by
@@ -461,20 +384,16 @@ lower case letters (for the most part). Multiple words should be StudlyCapped.
RT::Display::Provider # good
RT::CustomField # not so good, but OK
-Plugin modules should begin with "RTx::", followed by the name
+Plugin modules should begin with "RT::Extension::", followed by the name
of the plugin.
=head1 Code formatting
-Use perltidy. Anything we say here is wrong if it conflicts with what
-perltidy does. Your perltidyrc should read:
-
--lp -vt=2 -vtc=2 -nsfs -bar
+When in doubt, use perltidy; RT includes a F<.perltidyrc>.
=head2 Indents and Blank Space
-All indents should be tabs. Set your tab stops whatever you want them
-to be; I use 8 spaces per tabs.
+All indents should be four spaces; hard tabs are forbidden.
No space before a semicolon that closes a statement.
@@ -507,15 +426,14 @@ An example:
# this is my function!
sub foo {
- my $val = shift;
- my $obj = new Constructor;
- my($var1, $var2);
-
- $obj->SetFoo($val);
- $var1 = $obj->Foo();
+ my $val = shift;
+ my $obj = new Constructor;
+ my($var1, $var2);
+ $obj->SetFoo($val);
+ $var1 = $obj->Foo();
- return($val);
+ return($val);
}
print 1;
@@ -555,14 +473,13 @@ the opening statement, or the opening parenthesis, whichever works best.
Examples:
@list = qw(
- bar
- baz
+ bar
+ baz
); # right
if ($foo && $bar && $baz
- && $buz && $xyzzy
- ) {
- print $foo;
+ && $buz && $xyzzy) {
+ print $foo;
}
Whether or not there is space following a closing parenthesis is
@@ -620,26 +537,16 @@ opening curly on the first line, and the ending curly lined up with the
keyword at the end.
for (@list) {
- print;
- smell();
+ print;
+ smell();
}
-Generally, we prefer "uncuddled elses":
+Generally, we prefer "cuddled elses":
if ($foo) {
- print;
- }
- else {
- die;
- }
-
-_If_ the if statement is very brief, sometimes "cuddling" the else makes code more readable. Feel free to cuddle them in that case:
-
-
- if ($foo) {
- print;
+ print;
} else {
- die;
+ die;
}
=head2 Operators
@@ -678,21 +585,21 @@ normally, you should, if there is any question at all -- then it doesn't
matter which you use. Use whichever is most readable and aesthetically
pleasing to you at the time, and be consistent within your block of code.
-Break long lines AFTER operators, except for "and", "or", "&&", "||".
+Break long lines AFTER operators, except for ".", "and", "or", "&&", "||".
Try to keep the two parts to a binary operator (an operator that
has two operands) together when possible.
- print "foo" . "bar" . "baz"
- . "buz"; # wrong
-
print "foo" . "bar" . "baz" .
- "buz"; # right
+ "buz"; # wrong
+
+ print "foo" . "bar" . "baz"
+ . "buz"; # right
print $foo unless $x == 3 && $y ==
- 4 && $z == 5; # wrong
+ 4 && $z == 5; # wrong
print $foo unless $x == 3 && $y == 4
- && $z == 5; # right
+ && $z == 5; # right
=head2 Other
@@ -722,7 +629,7 @@ When making compound statements, put the primary action first.
Use here-docs instead of repeated print statements.
- print <<EOT;
+ print <<EOT;
This is a whole bunch of text.
I like it. I don't need to worry about messing
with lots of print statements and lining them up.
@@ -754,7 +661,7 @@ grep the codebase for strings to be localized
The string Foo
Bar
Baz
-
+
Should become <&|/l&>Foo Bar Baz</&>
@@ -788,9 +695,8 @@ should become <& /Elements/TitleBoxStart,
titleright => loc("RT [_1] for [_2]",$RT::VERSION, RT->Config->Get('rtname')),
title => loc('Login'),
&>
-
-=item Library code
+=item Library code
@@ -855,19 +761,21 @@ guide contained in this document.
=item Finish it up
After the code is done (possibly going through multiple code reviews),
-if you do not have repository access, submit it to rt-<major-version>-bugs@fsck.com as a unified diff. From that point on, it'll be handled by someone with repository access.
+if you do not have repository access, submit it to rt-bugs@fsck.com as a
+unified diff. From that point on, it'll be handled by someone with
+repository access.
=back
=head1 BUG REPORTS, PATCHES
-Use rt-<major-version>-bugs@fsck.com for I<any> bug that is not
-being fixed immediately. If it is not in RT, there
-is a good chance it will not be dealt with.
+Use rt-bugs@bestpractical.com for I<any> bug that is not being fixed
+immediately. If it is not in RT, there is a good chance it will not be
+dealt with.
-Send patches to rt-<major-version>-bugs@fsck.com, too. Use C<diff
--u> for patches.
+Send patches to rt-bugs@bestpractical.com, too. Use C<diff -u> for
+patches.
=head1 SCHEMA DESIGN
@@ -919,12 +827,3 @@ Talk about mason
Talk about adding a new translation
Talk more about logging
-
-=head1 CHANGES
-
- Adapted from Slash Styleguide by jesse - 20 Dec, 2002
-
-
-=head1 VERSION
-
-0.1
diff --git a/rt/lib/RT/Template.pm b/rt/lib/RT/Template.pm
index d15c1cd..0507997 100755
--- a/rt/lib/RT/Template.pm
+++ b/rt/lib/RT/Template.pm
@@ -470,6 +470,12 @@ sub _ParseContentPerl {
TYPE => 'STRING',
SOURCE => $args{Content},
);
+ my ($ok) = $template->compile;
+ unless ($ok) {
+ $RT::Logger->error("Template parsing error in @{[$self->Name]} (#@{[$self->id]}): $Text::Template::ERROR");
+ return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) );
+ }
+
my $is_broken = 0;
my $retval = $template->fill_in(
HASH => $args{TemplateArgs},
diff --git a/rt/lib/RT/Test.pm b/rt/lib/RT/Test.pm
index 2a1f52b..b15c03d 100644
--- a/rt/lib/RT/Test.pm
+++ b/rt/lib/RT/Test.pm
@@ -709,6 +709,39 @@ sub load_or_create_user {
return $obj;
}
+
+sub load_or_create_group {
+ my $self = shift;
+ my $name = shift;
+ my %args = (@_);
+
+ my $group = RT::Group->new( RT->SystemUser );
+ $group->LoadUserDefinedGroup( $name );
+ unless ( $group->id ) {
+ my ($id, $msg) = $group->CreateUserDefinedGroup(
+ Name => $name,
+ );
+ die "$msg" unless $id;
+ }
+
+ if ( $args{Members} ) {
+ my $cur = $group->MembersObj;
+ while ( my $entry = $cur->Next ) {
+ my ($status, $msg) = $entry->Delete;
+ die "$msg" unless $status;
+ }
+
+ foreach my $new ( @{ $args{Members} } ) {
+ my ($status, $msg) = $group->AddMember(
+ ref($new)? $new->id : $new,
+ );
+ die "$msg" unless $status;
+ }
+ }
+
+ return $group;
+}
+
=head2 load_or_create_queue
=cut
@@ -997,6 +1030,43 @@ sub run_mailgate {
$self->run_and_capture(%args);
}
+sub run_validator {
+ my $self = shift;
+ my %args = (check => 1, resolve => 0, force => 1, timeout => 0, @_ );
+
+ my $validator_path = "$RT::SbinPath/rt-validator";
+
+ my $cmd = $validator_path;
+ die "Couldn't find $cmd command" unless -f $cmd;
+
+ my $timeout = delete $args{timeout};
+
+ while( my ($k,$v) = each %args ) {
+ next unless $v;
+ $cmd .= " --$k '$v'";
+ }
+ $cmd .= ' 2>&1';
+
+ require IPC::Open2;
+ my ($child_out, $child_in);
+ my $pid = IPC::Open2::open2($child_out, $child_in, $cmd);
+ close $child_in;
+
+ local $SIG{ALRM} = sub { kill KILL => $pid; die "Timeout!" };
+
+ alarm $timeout if $timeout;
+ my $result = eval { local $/; <$child_out> };
+ warn $@ if $@;
+ close $child_out;
+ waitpid $pid, 0;
+ alarm 0;
+
+ DBIx::SearchBuilder::Record::Cachable->FlushCache
+ if $args{'resolve'};
+
+ return ($?, $result);
+}
+
sub run_and_capture {
my $self = shift;
my %args = @_;
diff --git a/rt/lib/RT/User.pm b/rt/lib/RT/User.pm
index 018ac8a..af4a6ad 100755
--- a/rt/lib/RT/User.pm
+++ b/rt/lib/RT/User.pm
@@ -957,7 +957,7 @@ sub IsPassword {
my $hash = MIME::Base64::decode_base64($stored);
# Decoding yields 30 byes; first 4 are the salt, the rest are substr(SHA256,0,26)
my $salt = substr($hash, 0, 4, "");
- return 0 unless substr(Digest::SHA::sha256($salt . Digest::MD5::md5($value)), 0, 26) eq $hash;
+ return 0 unless substr(Digest::SHA::sha256($salt . Digest::MD5::md5(encode_utf8($value))), 0, 26) eq $hash;
} elsif (length $stored == 32) {
# Hex nonsalted-md5
return 0 unless Digest::MD5::md5_hex(encode_utf8($value)) eq $stored;
@@ -1390,6 +1390,28 @@ sub SetPreferences {
}
}
+=head2 DeletePreferences NAME/OBJ VALUE
+
+Delete user preferences associated with given object or name.
+
+=cut
+
+sub DeletePreferences {
+ my $self = shift;
+ my $name = _PrefName( 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->Delete;
+ }
+
+ return (0, $self->loc("Preferences were not found"));
+}
+
=head2 Stylesheet
Returns a list of valid stylesheets take from preferences.
diff --git a/rt/lib/RT/Users.pm b/rt/lib/RT/Users.pm
index 1c75f42..f377d47 100755
--- a/rt/lib/RT/Users.pm
+++ b/rt/lib/RT/Users.pm
@@ -543,21 +543,31 @@ sub WhoHaveGroupRight
}
-=head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1 }
+=head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1, IncludeUnprivileged => 0 }
+
+Return members who belong to any of the groups passed in the groups whose IDs
+are included in the Groups arrayref.
+
+If IncludeSubgroupMembers is true (default) then members of any group that's a
+member of one of the passed groups are returned. If it's cleared then only
+direct member users are returned.
+
+If IncludeUnprivileged is false (default) then only privileged members are
+returned; otherwise either privileged or unprivileged group members may be
+returned.
=cut
-# XXX: should be generalized
sub WhoBelongToGroups {
my $self = shift;
my %args = ( Groups => undef,
IncludeSubgroupMembers => 1,
+ IncludeUnprivileged => 0,
@_ );
- # Unprivileged users can't be granted real system rights.
- # is this really the right thing to be saying?
- $self->LimitToPrivileged();
-
+ if (!$args{'IncludeUnprivileged'}) {
+ $self->LimitToPrivileged();
+ }
my $group_members = $self->_JoinGroupMembers( %args );
foreach my $groupid (@{$args{'Groups'}}) {
diff --git a/rt/sbin/rt-server.fcgi.in b/rt/sbin/rt-server.fcgi.in
index 0d11f01..5bd8f3e 100644
--- a/rt/sbin/rt-server.fcgi.in
+++ b/rt/sbin/rt-server.fcgi.in
@@ -138,6 +138,7 @@ EOF
# we must disconnect DB before fork
if ($RT::Handle) {
+ $RT::Handle->dbh->disconnect if $RT::Handle->dbh;
$RT::Handle->dbh(undef);
undef $RT::Handle;
}
diff --git a/rt/sbin/rt-server.in b/rt/sbin/rt-server.in
index 0d11f01..5bd8f3e 100644
--- a/rt/sbin/rt-server.in
+++ b/rt/sbin/rt-server.in
@@ -138,6 +138,7 @@ EOF
# we must disconnect DB before fork
if ($RT::Handle) {
+ $RT::Handle->dbh->disconnect if $RT::Handle->dbh;
$RT::Handle->dbh(undef);
undef $RT::Handle;
}
diff --git a/rt/sbin/rt-setup-fulltext-index.in b/rt/sbin/rt-setup-fulltext-index.in
index ade728f..7a1ede8 100644
--- a/rt/sbin/rt-setup-fulltext-index.in
+++ b/rt/sbin/rt-setup-fulltext-index.in
@@ -146,10 +146,16 @@ if ( $DB{'type'} eq 'mysql' ) {
default => $DEFAULT{'table'},
silent => !$OPT{'ask'},
);
- my $url = $OPT{'url'} || prompt(
+
+ my $url = 'sphinx://localhost:3312/rt';
+ my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
+ $url = 'sphinx://127.0.0.1:3312/rt'
+ if $version and $version =~ /^(\d+\.\d+)/ and $1 >= 5.5;
+
+ $url = $OPT{'url'} || prompt(
message => "Enter URL of the sphinx search server; this should be of the form\n"
. "sphinx://<server>:<port>/<index name>",
- default => 'sphinx://localhost:3312/rt',
+ default => $url,
silent => !$OPT{'ask'},
);
my $maxmatches = $OPT{'maxmatches'} || prompt(
diff --git a/rt/sbin/rt-test-dependencies.in b/rt/sbin/rt-test-dependencies.in
index 8681054..66215ad 100644
--- a/rt/sbin/rt-test-dependencies.in
+++ b/rt/sbin/rt-test-dependencies.in
@@ -55,9 +55,13 @@ use strict;
use warnings;
no warnings qw(numeric redefine);
use Getopt::Long;
+use Cwd qw(abs_path);
my %args;
my %deps;
my @orig_argv = @ARGV;
+# Save our path because installers or tests can change cwd
+my $script_path = abs_path($0);
+
GetOptions(
\%args, 'v|verbose',
'install!', 'with-MYSQL',
@@ -417,7 +421,7 @@ foreach my $type (sort grep $args{$_}, keys %args) {
}
if ( $args{'install'} && keys %Missing_By_Type ) {
- exec($0, @orig_argv, '--no-install');
+ exec($script_path, @orig_argv, '--no-install');
}
else {
conclude(%Missing_By_Type);
diff --git a/rt/sbin/rt-validator.in b/rt/sbin/rt-validator.in
index 128e60a..f0f1c59 100644
--- a/rt/sbin/rt-validator.in
+++ b/rt/sbin/rt-validator.in
@@ -222,7 +222,7 @@ foreach my $table ( qw(Users Groups) ) {
bind_values => [ $type ],
action => sub {
my $id = shift;
- return unless my $a = prompt_action( ['Delete', 'create'], $msg );
+ return unless my $a = prompt_action( ['Create', 'delete'], $msg );
if ( $a eq 'd' ) {
delete_record( $table, $id );
@@ -1104,7 +1104,7 @@ sub prompt_action {
my $token = shift || join ':', caller;
return '' unless $opt{'resolve'};
- return '' if $opt{'force'};
+ return lc substr $actions->[0], 0, 1 if $opt{'force'};
return $cached_answer{ $token } if exists $cached_answer{ $token };
print $msg, "\n";
diff --git a/rt/sbin/standalone_httpd.in b/rt/sbin/standalone_httpd.in
index 0d11f01..5bd8f3e 100644
--- a/rt/sbin/standalone_httpd.in
+++ b/rt/sbin/standalone_httpd.in
@@ -138,6 +138,7 @@ EOF
# we must disconnect DB before fork
if ($RT::Handle) {
+ $RT::Handle->dbh->disconnect if $RT::Handle->dbh;
$RT::Handle->dbh(undef);
undef $RT::Handle;
}
diff --git a/rt/share/html/Dashboards/Queries.html b/rt/share/html/Dashboards/Queries.html
index 61f6195..c489f1c 100644
--- a/rt/share/html/Dashboards/Queries.html
+++ b/rt/share/html/Dashboards/Queries.html
@@ -190,7 +190,7 @@ $m->callback(
);
my @panes;
-for my $pane (keys %pane_name) {
+for my $pane (sort keys %pane_name) {
my $sel = $m->comp(
'/Widgets/SelectionBox:new',
Action => 'Queries.html',
diff --git a/rt/share/html/Elements/CollectionAsTable/ParseFormat b/rt/share/html/Elements/CollectionAsTable/ParseFormat
index e56e9ce..5d55ffb 100644
--- a/rt/share/html/Elements/CollectionAsTable/ParseFormat
+++ b/rt/share/html/Elements/CollectionAsTable/ParseFormat
@@ -55,6 +55,7 @@ my @Columns;
while ($Format =~ /($RE{delimited}{-delim=>qq{\'"}}|[{}\w.]+)/go) {
my $col = $1;
+ my $colref = { original_string => $col };
if ($col =~ /^$RE{quoted}$/o) {
substr($col,0,1) = "";
@@ -62,8 +63,6 @@ while ($Format =~ /($RE{delimited}{-delim=>qq{\'"}}|[{}\w.]+)/go) {
$col =~ s/\\(.)/$1/g;
}
- my $colref = { };
-
$m->callback(CallbackName => 'PreColumn', Column => $colref, col => \$col);
while ( $col =~ s{/(STYLE|CLASS|TITLE|ALIGN|SPAN|ATTRIBUTE):([^/]*)}{}i ) {
diff --git a/rt/share/html/Elements/EditCustomFieldAutocomplete b/rt/share/html/Elements/EditCustomFieldAutocomplete
index 7bfe911..8eb7b42 100644
--- a/rt/share/html/Elements/EditCustomFieldAutocomplete
+++ b/rt/share/html/Elements/EditCustomFieldAutocomplete
@@ -46,7 +46,14 @@
%#
%# END BPS TAGGED BLOCK }}}
% if ( $Multiple ) {
-<textarea cols="<% $Cols %>" rows="<% $Rows %>" name="<% $name %>-Values" id="<% $name %>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
+<textarea \
+% if ( defined $Cols ) {
+cols="<% $Cols %>" \
+% }
+% if ( defined $Rows ) {
+rows="<% $Rows %>" \
+% }
+name="<% $name %>-Values" id="<% $name %>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
<script type="text/javascript">
var id = <% "$name-Values" |n,j%>;
diff --git a/rt/share/html/Elements/EditCustomFieldDateTime b/rt/share/html/Elements/EditCustomFieldDateTime
index 25d1ce1..edf125e 100644
--- a/rt/share/html/Elements/EditCustomFieldDateTime
+++ b/rt/share/html/Elements/EditCustomFieldDateTime
@@ -50,7 +50,7 @@
<%INIT>
my $DateObj = RT::Date->new ( $session{'CurrentUser'} );
-$DateObj->Set( Format => 'ISO', Value => $Default );
+$DateObj->Set( Format => $Format, Value => $Default );
</%INIT>
<%ARGS>
$Object => undef
@@ -59,4 +59,5 @@ $NamePrefix => undef
$Default => undef
$Values => undef
$MaxValues => 1
+$Format => 'ISO'
</%ARGS>
diff --git a/rt/share/html/Elements/EditCustomFieldFreeform b/rt/share/html/Elements/EditCustomFieldFreeform
index 6724873..f0f8ee6 100644
--- a/rt/share/html/Elements/EditCustomFieldFreeform
+++ b/rt/share/html/Elements/EditCustomFieldFreeform
@@ -47,9 +47,20 @@
%# END BPS TAGGED BLOCK }}}
% my $name = $NamePrefix . $CustomField->Id . '-Value';
% if ($Multiple) {
-<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>s" id="<%$name%>s" wrap="off" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
+<textarea \
+% if ( defined $Cols ) {
+cols="<% $Cols %>" \
+% }
+% if ( defined $Rows ) {
+rows="<% $Rows %>" \
+% }
+name="<%$name%>s" id="<%$name%>s" wrap="off" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
% } else {
-<input name="<%$name%>" id="<%$name%>" size="<%$Cols%>" class="CF-<%$CustomField->id%>-Edit" value="<% defined($Default) ? $Default : ''%>" />
+<input type="text" name="<%$name%>" id="<%$name%>" \
+% if ( defined $Cols ) {
+size="<% $Cols %>" \
+% }
+class="CF-<%$CustomField->id%>-Edit" value="<% defined($Default) ? $Default : ''%>" />
% }
<%INIT>
if ( $Multiple and $Values ) {
diff --git a/rt/share/html/Elements/EditCustomFieldSelect b/rt/share/html/Elements/EditCustomFieldSelect
index 87400ab..b343d82 100644
--- a/rt/share/html/Elements/EditCustomFieldSelect
+++ b/rt/share/html/Elements/EditCustomFieldSelect
@@ -104,7 +104,7 @@ jQuery( function () {
% if ( $RenderType eq 'List' ) {
<fieldset class="cfedit">
-<div name="<%$id%>-Values" id="<%$id%>-Values">
+<div data-name="<%$id%>-Values" id="<%$id%>-Values">
% if ( $checktype eq 'radio' ) {
<div class="none">
<input class="none" type="<% $checktype %>" name="<% $name %>" id="<% $name %>-none" value="" <% keys %default ? '' : ' checked="checked"' |n%> />
@@ -115,7 +115,7 @@ jQuery( function () {
% while ( my $value = $CFVs->Next ) {
% my $content = $value->Name;
% my $labelid = "$name-". $value->id;
-<div name="<% $value->Category %>">
+<div data-name="<% $value->Category || '' %>">
<input type="<% $checktype %>" name="<% $name %>" id="<% $labelid %>" value="<% $content %>" <% $default{ lc $content }? ' checked="checked"' : '' |n%> />
<label for="<% $labelid %>"><% $content %></label><br />
</div>
diff --git a/rt/share/html/Elements/EditCustomFieldText b/rt/share/html/Elements/EditCustomFieldText
index 8664604..ca7a266 100644
--- a/rt/share/html/Elements/EditCustomFieldText
+++ b/rt/share/html/Elements/EditCustomFieldText
@@ -46,10 +46,24 @@
%#
%# END BPS TAGGED BLOCK }}}
% while ($Values and my $value = $Values->Next ) {
-<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
+<textarea \
+% if ( defined $Cols ) {
+cols="<% $Cols %>" \
+% }
+% if ( defined $Rows ) {
+rows="<% $Rows %>" \
+% }
+name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
% }
% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
-<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
+<textarea \
+% if ( defined $Cols ) {
+cols="<% $Cols %>" \
+% }
+% if ( defined $Rows ) {
+rows="<% $Rows %>" \
+% }
+name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
% }
<%INIT>
# XXX - MultiValue textarea is for now outlawed.
diff --git a/rt/share/html/Elements/EditCustomFieldWikitext b/rt/share/html/Elements/EditCustomFieldWikitext
index 1a36ae3..d4b79cd 100644
--- a/rt/share/html/Elements/EditCustomFieldWikitext
+++ b/rt/share/html/Elements/EditCustomFieldWikitext
@@ -46,10 +46,24 @@
%#
%# END BPS TAGGED BLOCK }}}
% while ($Values and my $value = $Values->Next ) {
-<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
+<textarea \
+% if ( defined $Cols ) {
+cols="<% $Cols %>" \
+% }
+% if ( defined $Rows ) {
+rows="<% $Rows %>" \
+% }
+name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
% }
% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
-<textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default %></textarea>
+<textarea \
+% if ( defined $Cols ) {
+cols="<% $Cols %>" \
+% }
+% if ( defined $Rows ) {
+rows="<% $Rows %>" \
+% }
+name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default %></textarea>
% }
<%INIT>
# XXX - MultiValue textarea is for now outlawed.
diff --git a/rt/share/html/Elements/MakeClicky b/rt/share/html/Elements/MakeClicky
index e22e75f..8efe78c 100644
--- a/rt/share/html/Elements/MakeClicky
+++ b/rt/share/html/Elements/MakeClicky
@@ -96,6 +96,8 @@ my $handle = sub {
}
};
+my $cache; # only defined via callback
+
# Hook to add more Clicky types
# XXX Have to have Page argument, as Mason gets caller wrong in Callback?
# This happens as we are in <%ONCE> block
@@ -104,6 +106,7 @@ $m->callback(
types => \@types,
actions => \%actions,
handle => \$handle,
+ cache => \$cache,
);
@@ -131,6 +134,15 @@ $html => undef
</%ARGS>
<%INIT>
return unless defined $$content;
+if ( defined $cache ) {
+ my $cached_content = $cache->(fetch => $content);
+ if ( $cached_content ) {
+ RT->Logger->debug("Found MakeClicky cache");
+ $$content = $cached_content;
+ return;
+ }
+}
+
unless ( $regexp ) {
RT::Interface::Web::EscapeUTF8( $content ) unless $html;
return;
@@ -165,40 +177,6 @@ substr( $$content, $pos ) = $escaper->( substr( $$content, $pos ) ) unless
($pos == length $$content) || $html;
pos($$content) = 0;
+$cache->(store => $content) if defined $cache;
</%INIT>
-<%doc>
-
-MakeClicky detects various formats of data in headers and email
-messages, and extends them with supporting links. By default, RT
-provides two formats:
-
- * 'httpurl': detects http:// and https:// URLs and adds '[Open URL]'
- link after the URL.
-
- * 'httpurl_overwrite': also detects URLs as 'httpurl' format, but
- replace URL with link.
-
-To extend this with your own types of data, use the callback.
-It will be provided with:
-
- * 'types': An array reference of hash references. Modify this array
- reference to add your own types; the first matching type will be
- used. Each hashref should contain:
- - 'name': The name of the data format; this is used in the
- configuration file to enable the format.
- - 'regex': A regular expression to match against
- - 'action': The name of the action to run (see "actions", below)
-
- * 'actions': A hash reference of 'actions'. Modify this hash
- reference to change or add action types. Values are subroutine
- references which will get called when needed. They should return
- the modified string. Note that subroutine must escape HTML.
-
- * 'handler': A reference to a subroutine reference; modify it if you
- have to. This can be used to add pre- or post-processing around
- all actions.
-
-Read more about writing new actions in docs/extending/clickable_links.pod
-
-</%doc>
diff --git a/rt/share/html/Elements/MyRT b/rt/share/html/Elements/MyRT
index c59ec1c..84db949 100644
--- a/rt/share/html/Elements/MyRT
+++ b/rt/share/html/Elements/MyRT
@@ -67,12 +67,10 @@
my %allowed_components = map {$_ => 1} @{RT->Config->Get('HomepageComponents')};
my $user = $session{'CurrentUser'}->UserObj;
-$Portlets ||= $session{'my_rt_portlets'};
+$Portlets ||= $user->Preferences('HomepageSettings');
unless ( $Portlets ) {
- my ($default_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
- $Portlets = $session{'my_rt_portlets'} = $user->Preferences(
- HomepageSettings => $default_portlets? $default_portlets->Content: {},
- );
+ my ($defaults) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
+ $Portlets = $defaults ? $defaults->Content : {};
}
$m->callback( CallbackName => 'MassagePortlets', Portlets => $Portlets );
diff --git a/rt/share/html/Elements/ShowLink b/rt/share/html/Elements/ShowLink
index cccc3d8..35b2638 100644
--- a/rt/share/html/Elements/ShowLink
+++ b/rt/share/html/Elements/ShowLink
@@ -49,7 +49,7 @@
% if ($URI->IsLocal) {
% my $member = $URI->Object;
% my $has_name = UNIVERSAL::can($member, 'Name') || (UNIVERSAL::can($member, '_Accessible') && $member->_Accessible('Name', 'read'));
-% if (UNIVERSAL::isa($member, "RT::Ticket")) {
+% if (UNIVERSAL::isa($member, "RT::Ticket") and $member->CurrentUserHasRight('ShowTicket')) {
% my $inactive = $member->QueueObj->IsInactiveStatus($member->Status);
<span class="<% $inactive ? 'ticket-inactive' : '' %>">
diff --git a/rt/share/html/Elements/SimpleSearch b/rt/share/html/Elements/SimpleSearch
index bd8a876..d9f34fa 100755
--- a/rt/share/html/Elements/SimpleSearch
+++ b/rt/share/html/Elements/SimpleSearch
@@ -46,8 +46,9 @@
%#
%# END BPS TAGGED BLOCK }}}
<form action="<% RT->Config->Get('WebPath') %><% $SendTo %>" id="simple-search">
- <input size="12" name="q" autocomplete="off" accesskey="0" class="field" value="<&|/l&>Search</&>..." onfocus="if (this.value=='<&|/l&>Search</&>...') this.value=''" />
+ <input size="12" name="q" autocomplete="off" accesskey="0" class="field" value="<% $Placeholder %>..." onfocus="if (this.value==(<% $Placeholder, |n,j %>+'...')) this.value=''" />
</form>
<%ARGS>
$SendTo => '/Search/Simple.html'
+$Placeholder => loc('Search')
</%ARGS>
diff --git a/rt/share/html/Install/DatabaseDetails.html b/rt/share/html/Install/DatabaseDetails.html
index 78672db..b4d3f8c 100644
--- a/rt/share/html/Install/DatabaseDetails.html
+++ b/rt/share/html/Install/DatabaseDetails.html
@@ -165,6 +165,7 @@ if ( $Run ) {
);
my $sth = $dbh->prepare('select * from Users');
+ $sth->execute();
};
unless ( $@ ) {
diff --git a/rt/share/html/NoAuth/js/cascaded.js b/rt/share/html/NoAuth/js/cascaded.js
index b72a33f..1611fd1 100644
--- a/rt/share/html/NoAuth/js/cascaded.js
+++ b/rt/share/html/NoAuth/js/cascaded.js
@@ -64,10 +64,10 @@ function filter_cascade (id, vals) {
}
else {
jQuery(element).find('div').hide().find('input').attr('disabled', 'disabled');
- jQuery(element).find('div[name=]').show().find('input').attr('disabled', '');
+ jQuery(element).find('div[data-name=]').show().find('input').attr('disabled', '');
jQuery(element).find('div.none').show().find('input').attr('disabled','');
for ( var j = 0; j < vals.length; j++ ) {
- jQuery(element).find('div[name^=' + vals[j] + ']').show().find('input').attr('disabled', '');
+ jQuery(element).find('div[data-name^=' + vals[j] + ']').show().find('input').attr('disabled', '');
}
}
}
diff --git a/rt/share/html/Prefs/MyRT.html b/rt/share/html/Prefs/MyRT.html
index a595ccf..288df0b 100644
--- a/rt/share/html/Prefs/MyRT.html
+++ b/rt/share/html/Prefs/MyRT.html
@@ -97,18 +97,19 @@ if ( $ARGS{'UpdateSummaryRows'} ) {
$ARGS{'SummaryRows'} ||= $user->Preferences('SummaryRows', RT->Config->Get('DefaultSummaryRows'));
if ($ARGS{Reset}) {
- my ($ok, $msg) = $user->SetPreferences('HomepageSettings', {});
- push @results, $ok ? loc('Preferences saved.') : $msg;
- delete $session{'my_rt_portlets'};
+ for my $pref_name ('HomepageSettings', 'SummaryRows') {
+ next unless $user->Preferences($pref_name);
+ my ($ok, $msg) = $user->DeletePreferences($pref_name);
+ push @results, $msg unless $ok;
+ }
+ push @results, loc('Preferences saved.') unless @results;
}
-unless (exists $session{'my_rt_portlets'}) {
- my ($default_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
- my $portlets = $default_portlets ? $default_portlets->Content : {};
-
- $session{'my_rt_portlets'} = $user->Preferences('HomepageSettings', $portlets);
+my $portlets = $user->Preferences('HomepageSettings');
+unless ($portlets) {
+ my ($defaults) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
+ $portlets = $defaults ? $defaults->Content : {};
}
-my $portlets = $session{'my_rt_portlets'};
my %seen;
my @items = map ["component-$_", loc($_)], grep !$seen{$_}++, @{RT->Config->Get('HomepageComponents')};
@@ -157,7 +158,6 @@ my @panes = $m->comp(
my ( $conf, $pane ) = @_;
my ($ok, $msg) = $user->SetPreferences( 'HomepageSettings', $conf );
push @results, $ok ? loc('Preferences saved for [_1].', $pane) : $msg;
- delete $session{'my_rt_portlets'};
}
);
diff --git a/rt/share/html/Prefs/Search.html b/rt/share/html/Prefs/Search.html
index bb52a62..3f2c404 100644
--- a/rt/share/html/Prefs/Search.html
+++ b/rt/share/html/Prefs/Search.html
@@ -68,6 +68,14 @@
</form>
+<&|/Widgets/TitleBox, title => loc("Reset") &>
+<form method="post" name="ResetSearchOptions" action="Search.html">
+<input type="hidden" name="Reset" value="1" />
+<input type="hidden" name="name" value="<%$ARGS{name}%>" class="hidden" />
+<input type="submit" class="button" name="ResetSearchOptions" value="<% loc('Reset to default') %>">
+</form>
+</&>
+
<%INIT>
my @actions;
my $title = loc("Customize").' ';
@@ -81,6 +89,13 @@ Abort('No search specified')
my $search = $class->new ($session{'CurrentUser'});
$search->LoadById ($id);
+
+# If we are resetting prefs, do so before attempting to load them
+if ($ARGS{'Reset'}) {
+ my ($ok, $msg) = $session{'CurrentUser'}->UserObj->DeletePreferences($ARGS{name});
+ push @actions, $ok ? loc('Preferences reset.') : $msg;
+}
+
$title .= loc (RT::SavedSearch->EscapeDescription($search->Description), loc ('"N"'));
my $user = $session{'CurrentUser'}->UserObj;
my $SearchArg = $user->Preferences($search, $search->Content);
diff --git a/rt/share/html/Search/Bulk.html b/rt/share/html/Search/Bulk.html
index a215eac..38ca642 100755
--- a/rt/share/html/Search/Bulk.html
+++ b/rt/share/html/Search/Bulk.html
@@ -202,6 +202,13 @@ $cfs->LimitToQueue($_) for keys %$seen_queues;
% } elsif ($cf->Type eq 'Text') {
<td><& /Elements/EditCustomFieldText, @add &></td>
<td>&nbsp;</td>
+% } elsif ($cf->Type eq 'Date') {
+<td><& /Elements/EditCustomFieldDate, @add, Default => undef &></td>
+<td><& /Elements/EditCustomFieldDate, @del, Default => undef &></td>
+% } elsif ($cf->Type eq 'DateTime') {
+% # Pass datemanip format to prevent another tz date conversion
+<td><& /Elements/EditCustomFieldDateTime, @add, Default => undef, Format => 'datemanip' &></td>
+<td><& /Elements/EditCustomFieldDateTime, @del, Default => undef, Format => 'datemanip' &></td>
% } else {
% $RT::Logger->crit("Unknown CustomField type: " . $cf->Type);
% }
@@ -372,7 +379,27 @@ unless ( $ARGS{'AddMoreAttach'} ) {
unless ( $cf->SingleValue );
my $current_values = $Ticket->CustomFieldValues($cfid);
+
+ if ( $cf->Type eq 'DateTime' || $cf->Type eq 'Date' ){
+ # Clear out empty string submissions to avoid
+ # Not set changed to Not set
+ @values = grep length, @values;
+ }
+
foreach my $value (@values) {
+
+ # Convert for timezone. Without converstion,
+ # HasEntry and DeleteCustomFieldValue fail because
+ # the value in the DB is converted.
+ if ( $op eq 'del'
+ && ($cf->Type eq 'DateTime' || $cf->Type eq 'Date') ){
+ my $DateObj = RT::Date->new( $session{'CurrentUser'} );
+ $DateObj->Set( Format => 'unknown',
+ Value => $value );
+ $value = $cf->Type eq 'DateTime' ? $DateObj->ISO
+ : $DateObj->ISO(Time => 0, Seconds => 0);
+ }
+
if ( $op eq 'del' && $current_values->HasEntry($value) ) {
my ( $id, $msg ) = $Ticket->DeleteCustomFieldValue(
Field => $cfid,
@@ -411,6 +438,8 @@ unless ( $ARGS{'AddMoreAttach'} ) {
# Cleanup WebUI
delete $session{'Attachments'};
+
+ $Tickets->RedoSearch();
}
my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );
diff --git a/rt/share/html/Search/Chart b/rt/share/html/Search/Chart
index 4be28ab..7256106 100644
--- a/rt/share/html/Search/Chart
+++ b/rt/share/html/Search/Chart
@@ -48,7 +48,7 @@
<%args>
$Query => "id > 0"
$PrimaryGroupBy => 'Queue'
-$ChartStyle => 'bars'
+$ChartStyle => 'bar'
</%args>
<%init>
my $chart_class;
diff --git a/rt/share/html/Search/Chart.html b/rt/share/html/Search/Chart.html
index 952f09c..ab25745 100644
--- a/rt/share/html/Search/Chart.html
+++ b/rt/share/html/Search/Chart.html
@@ -47,7 +47,7 @@
%# END BPS TAGGED BLOCK }}}
<%args>
$PrimaryGroupBy => 'Queue'
-$ChartStyle => 'bars'
+$ChartStyle => 'bar'
$Description => undef
</%args>
<%init>
diff --git a/rt/share/html/Search/Elements/BuildFormatString b/rt/share/html/Search/Elements/BuildFormatString
index 14e3a71..66fd147 100644
--- a/rt/share/html/Search/Elements/BuildFormatString
+++ b/rt/share/html/Search/Elements/BuildFormatString
@@ -109,6 +109,8 @@ my @fields = (
)
); # loc_qw
+# This callback will only run once and will be removed in 4.4
+# If you want to add a new item to @fields, use the Default callback below.
$m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fields );
my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
@@ -227,17 +229,17 @@ my @format_string;
foreach my $field (@seen) {
next unless $field;
my $row = "";
- if ( $field->{'output'} ) {
- $row = join '', @{$field->{'output'}};
+ if ( $field->{'original_string'} ) {
+ $row = $field->{'original_string'};
}
else {
$row .= $field->{'Prefix'} if defined $field->{'Prefix'};
$row .= "__$field->{'Column'}__"
unless ( $field->{'Column'} eq "<blank>" );
$row .= $field->{'Suffix'} if defined $field->{'Suffix'};
+ $row =~ s!([\\'])!\\$1!g;
+ $row = "'$row'";
}
- $row =~ s!([\\'])!\\$1!g;
- $row = "'$row'";
push( @format_string, $row );
}
diff --git a/rt/share/html/Search/Elements/Chart b/rt/share/html/Search/Elements/Chart
index 05a0422..f0d1e4a 100644
--- a/rt/share/html/Search/Elements/Chart
+++ b/rt/share/html/Search/Elements/Chart
@@ -48,7 +48,7 @@
<%args>
$Query => "id > 0"
$PrimaryGroupBy => 'Queue'
-$ChartStyle => 'bars'
+$ChartStyle => 'bar'
</%args>
<%init>
use RT::Report::Tickets;
diff --git a/rt/share/html/Search/Results.html b/rt/share/html/Search/Results.html
index 601786f..3c3187c 100755
--- a/rt/share/html/Search/Results.html
+++ b/rt/share/html/Search/Results.html
@@ -111,7 +111,6 @@ if ( !defined($Rows) ) {
}
$Page = 1 unless $Page && $Page > 0;
-my ($title, $ticketcount);
$session{'i'}++;
$session{'tickets'} = RT::Tickets->new($session{'CurrentUser'}) ;
my ($ok, $msg) = $Query ? $session{'tickets'}->FromSQL($Query) : (1, "Vacuously OK");
@@ -141,11 +140,10 @@ $session{'CurrentSearchHash'} = {
};
+my ($title, $ticketcount) = (loc("Found tickets"), 0);
if ( $session{'tickets'}->Query()) {
$ticketcount = $session{tickets}->CountAll();
$title = loc('Found [quant,_1,ticket]', $ticketcount);
-} else {
- $title = loc("Find tickets");
}
my $QueryString = "?".$m->comp('/Elements/QueryString',
diff --git a/rt/t/api/date.t b/rt/t/api/date.t
index cc1c694..22c6f1b 100644
--- a/rt/t/api/date.t
+++ b/rt/t/api/date.t
@@ -4,7 +4,7 @@ use DateTime;
use warnings;
use strict;
-use RT::Test tests => 173;
+use RT::Test tests => 175;
use RT::User;
use Test::Warn;
@@ -440,6 +440,14 @@ my $year = (localtime(time))[5] + 1900;
$date->Unix(0);
$date->AddDays(31);
is($date->ISO, '1970-02-01 00:00:00', "added one month");
+
+ $date->Unix(0);
+ $date->AddDays(0);
+ is($date->ISO, '1970-01-01 00:00:00', "added no days");
+
+ $date->Unix(0);
+ $date->AddDays();
+ is($date->ISO, '1970-01-02 00:00:00', "added one day with no argument");
}
{
diff --git a/rt/t/api/password-types.t b/rt/t/api/password-types.t
index e5155e3..10a874a 100644
--- a/rt/t/api/password-types.t
+++ b/rt/t/api/password-types.t
@@ -3,6 +3,8 @@ use warnings;
use RT::Test;
use Digest::MD5;
+use Encode 'encode_utf8';
+use utf8;
my $default = "sha512";
@@ -38,3 +40,12 @@ my $trunc = MIME::Base64::encode_base64(
$root->_Set( Field => "Password", Value => $trunc);
ok($root->IsPassword("secret"), "Unsalted MD5 base64 works");
like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default");
+
+# Non-ASCII salted truncated SHA-256
+my $non_ascii_trunc = MIME::Base64::encode_base64(
+ "salt" . substr(Digest::SHA::sha256("salt".Digest::MD5::md5(encode_utf8("áěšý"))),0,26),
+ ""
+);
+$root->_Set( Field => "Password", Value => $non_ascii_trunc);
+ok($root->IsPassword("áěšý"), "Unsalted MD5 base64 works");
+like($root->__Value("Password"), qr/^\!$default\!/, "And is now upgraded to salted $default");
diff --git a/rt/t/fts/indexed_mysql.t b/rt/t/fts/indexed_mysql.t
index a54382f..0a4f026 100644
--- a/rt/t/fts/indexed_mysql.t
+++ b/rt/t/fts/indexed_mysql.t
@@ -32,7 +32,7 @@ sub setup_indexing {
command => $RT::SbinPath .'/rt-setup-fulltext-index',
dba => $ENV{'RT_DBA_USER'},
'dba-password' => $ENV{'RT_DBA_PASSWORD'},
- url => "sphinx://localhost:$port/rt",
+ url => "sphinx://127.0.0.1:$port/rt",
);
ok(!$exit_code, "setted up index");
diag "output: $output" if $ENV{'TEST_VERBOSE'};
diff --git a/rt/t/pod.t b/rt/t/pod.t
index d11a497..697a30b 100644
--- a/rt/t/pod.t
+++ b/rt/t/pod.t
@@ -4,4 +4,4 @@ use warnings;
use Test::More;
eval "use Test::Pod 1.14";
plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
-all_pod_files_ok();
+all_pod_files_ok( all_pod_files("lib","docs","etc","bin","sbin"));
diff --git a/rt/t/validator/group_members.t b/rt/t/validator/group_members.t
index fbe7580..af93c51 100644
--- a/rt/t/validator/group_members.t
+++ b/rt/t/validator/group_members.t
@@ -2,104 +2,45 @@
use strict;
use warnings;
-use RT::Test tests => 60;
-
-sub load_or_create_group {
- my $name = shift;
- my %args = (@_);
-
- my $group = RT::Group->new( RT->SystemUser );
- $group->LoadUserDefinedGroup( $name );
- unless ( $group->id ) {
- my ($id, $msg) = $group->CreateUserDefinedGroup(
- Name => $name,
- );
- die "$msg" unless $id;
- }
-
- if ( $args{Members} ) {
- my $cur = $group->MembersObj;
- while ( my $entry = $cur->Next ) {
- my ($status, $msg) = $entry->Delete;
- die "$msg" unless $status;
- }
-
- foreach my $new ( @{ $args{Members} } ) {
- my ($status, $msg) = $group->AddMember(
- ref($new)? $new->id : $new,
- );
- die "$msg" unless $status;
- }
- }
-
- return $group;
-}
-
-my $validator_path = "$RT::SbinPath/rt-validator";
-sub run_validator {
- my %args = (check => 1, resolve => 0, force => 1, @_ );
-
- my $cmd = $validator_path;
- die "Couldn't find $cmd command" unless -f $cmd;
-
- while( my ($k,$v) = each %args ) {
- next unless $v;
- $cmd .= " --$k '$v'";
- }
- $cmd .= ' 2>&1';
-
- require IPC::Open2;
- my ($child_out, $child_in);
- my $pid = IPC::Open2::open2($child_out, $child_in, $cmd);
- close $child_in;
-
- my $result = do { local $/; <$child_out> };
- close $child_out;
- waitpid $pid, 0;
-
- DBIx::SearchBuilder::Record::Cachable->FlushCache
- if $args{'resolve'};
-
- return ($?, $result);
-}
+use RT::Test tests => 63;
{
- my ($ecode, $res) = run_validator();
+ my ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
}
{
- my $group = load_or_create_group('test', Members => [] );
+ my $group = RT::Test->load_or_create_group('test', Members => [] );
ok $group, "loaded or created a group";
- my ($ecode, $res) = run_validator();
+ my ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
}
# G1 -> G2
{
- my $group1 = load_or_create_group( 'test1', Members => [] );
+ my $group1 = RT::Test->load_or_create_group( 'test1', Members => [] );
ok $group1, "loaded or created a group";
- my $group2 = load_or_create_group( 'test2', Members => [ $group1 ]);
+ my $group2 = RT::Test->load_or_create_group( 'test2', Members => [ $group1 ]);
ok $group2, "loaded or created a group";
ok $group2->HasMember( $group1->id ), "has member";
ok $group2->HasMemberRecursively( $group1->id ), "has member";
- my ($ecode, $res) = run_validator();
+ my ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
$RT::Handle->dbh->do("DELETE FROM CachedGroupMembers");
DBIx::SearchBuilder::Record::Cachable->FlushCache;
ok !$group2->HasMemberRecursively( $group1->id ), "has no member, broken DB";
- ($ecode, $res) = run_validator(resolve => 1);
+ ($ecode, $res) = RT::Test->run_validator(resolve => 1);
ok $group2->HasMember( $group1->id ), "has member";
ok $group2->HasMemberRecursively( $group1->id ), "has member";
- ($ecode, $res) = run_validator();
+ ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
}
@@ -109,7 +50,7 @@ sub run_validator {
for (1..5) {
my $child = @groups? $groups[-1]: undef;
- my $group = load_or_create_group( 'test'. $_, Members => [ $child? ($child): () ] );
+ my $group = RT::Test->load_or_create_group( 'test'. $_, Members => [ $child? ($child): () ] );
ok $group, "loaded or created a group";
ok $group->HasMember( $child->id ), "has member"
@@ -120,7 +61,7 @@ sub run_validator {
push @groups, $group;
}
- my ($ecode, $res) = run_validator();
+ my ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
$RT::Handle->dbh->do("DELETE FROM CachedGroupMembers");
@@ -128,7 +69,7 @@ sub run_validator {
ok !$groups[1]->HasMemberRecursively( $groups[0]->id ), "has no member, broken DB";
- ($ecode, $res) = run_validator(resolve => 1);
+ ($ecode, $res) = RT::Test->run_validator(resolve => 1);
for ( my $i = 1; $i < @groups; $i++ ) {
ok $groups[$i]->HasMember( $groups[$i-1]->id ), "has member";
@@ -136,7 +77,7 @@ sub run_validator {
foreach 0..$i-1;
}
- ($ecode, $res) = run_validator();
+ ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
}
@@ -144,34 +85,51 @@ sub run_validator {
{
my @groups;
for (2..5) {
- my $group = load_or_create_group( 'test'. $_, Members => [] );
+ my $group = RT::Test->load_or_create_group( 'test'. $_, Members => [] );
ok $group, "loaded or created a group";
push @groups, $group;
}
- my $parent = load_or_create_group( 'test1', Members => \@groups );
+ my $parent = RT::Test->load_or_create_group( 'test1', Members => \@groups );
ok $parent, "loaded or created a group";
- my ($ecode, $res) = run_validator();
+ my ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
}
# G1 <- (G2, G3, G4) <- G5
{
- my $gchild = load_or_create_group( 'test5', Members => [] );
+ my $gchild = RT::Test->load_or_create_group( 'test5', Members => [] );
ok $gchild, "loaded or created a group";
my @groups;
for (2..4) {
- my $group = load_or_create_group( 'test'. $_, Members => [ $gchild ] );
+ my $group = RT::Test->load_or_create_group( 'test'. $_, Members => [ $gchild ] );
ok $group, "loaded or created a group";
push @groups, $group;
}
- my $parent = load_or_create_group( 'test1', Members => \@groups );
+ my $parent = RT::Test->load_or_create_group( 'test1', Members => \@groups );
ok $parent, "loaded or created a group";
- my ($ecode, $res) = run_validator();
+ my ($ecode, $res) = RT::Test->run_validator();
is $res, '', 'empty result';
}
+# group without principal record and cgm records
+# was causing infinite loop as principal was not created
+{
+ my $group = RT::Test->load_or_create_group('Test');
+ ok $group && $group->id, 'loaded or created group';
+
+ my $dbh = $group->_Handle->dbh;
+ $dbh->do('DELETE FROM Principals WHERE id = ?', {RaiseError => 1}, $group->id);
+ $dbh->do('DELETE FROM CachedGroupMembers WHERE GroupId = ?', {RaiseError => 1}, $group->id);
+ DBIx::SearchBuilder::Record::Cachable->FlushCache;
+
+ my ($ecode, $res) = RT::Test->run_validator(resolve => 1, timeout => 30);
+ ok $res;
+
+ ($ecode, $res) = RT::Test->run_validator();
+ is $res, '', 'empty result';
+}
diff --git a/rt/t/web/path-traversal.t b/rt/t/web/path-traversal.t
index 5d5c954..01302e6 100644
--- a/rt/t/web/path-traversal.t
+++ b/rt/t/web/path-traversal.t
@@ -1,9 +1,10 @@
use strict;
use warnings;
-use RT::Test tests => 22;
+use RT::Test tests => undef;
my ($baseurl, $agent) = RT::Test->started_ok;
+ok($agent->login);
$agent->get("$baseurl/NoAuth/../Elements/HeaderJavascript");
is($agent->status, 400);
@@ -31,6 +32,12 @@ SKIP: {
$agent->warning_like(qr/Invalid request.*aborting/,);
};
+# Do not reject a simple /. in the URL, for downloading uploaded
+# dotfiles, for example.
+$agent->get("$baseurl/Ticket/Attachment/28/9/.bashrc");
+is($agent->status, 200); # Even for a file not found, we return 200
+$agent->content_contains("Bad attachment id");
+
# do not reject these URLs, even though they contain /. outside the path
$agent->get("$baseurl/index.html?ignored=%2F%2E");
is($agent->status, 200);
@@ -44,3 +51,5 @@ is($agent->status, 200);
$agent->get("$baseurl/index.html#/.");
is($agent->status, 200);
+undef $agent;
+done_testing;