From 0ea23112cfa0d82738b0f08d60d90579721b7524 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Tue, 27 May 2014 15:20:05 -0700 Subject: [PATCH] rt 4.0.20 (RT#13852) --- rt/configure | 25 +-- rt/configure.ac | 1 + rt/docs/UPGRADING-4.0 | 3 + rt/docs/extending/clickable_links.pod | 11 +- rt/etc/RT_Config.pm.in | 5 +- rt/etc/upgrade/3.8.9/content | 1 + rt/lib/RT.pm | 4 +- rt/lib/RT/Config.pm | 13 +- rt/lib/RT/Date.pm | 3 +- rt/lib/RT/Generated.pm | 2 +- rt/lib/RT/Handle.pm | 41 +++- rt/lib/RT/Interface/REST.pm | 2 +- rt/lib/RT/Interface/Web.pm | 2 +- rt/lib/RT/Interface/Web/Handler.pm | 2 +- rt/lib/RT/Lifecycle.pm | 4 +- rt/lib/RT/Shredder/Plugin/SQLDump.pm | 4 +- rt/lib/RT/StyleGuide.pod | 207 ++++++--------------- rt/lib/RT/Template.pm | 6 + rt/lib/RT/Test.pm | 70 +++++++ rt/lib/RT/User.pm | 24 ++- rt/lib/RT/Users.pm | 22 ++- rt/sbin/rt-server.fcgi.in | 1 + rt/sbin/rt-server.in | 1 + rt/sbin/rt-setup-fulltext-index.in | 10 +- rt/sbin/rt-test-dependencies.in | 6 +- rt/sbin/rt-validator.in | 4 +- rt/sbin/standalone_httpd.in | 1 + rt/share/html/Dashboards/Queries.html | 2 +- .../html/Elements/CollectionAsTable/ParseFormat | 3 +- rt/share/html/Elements/EditCustomFieldAutocomplete | 9 +- rt/share/html/Elements/EditCustomFieldDateTime | 3 +- rt/share/html/Elements/EditCustomFieldFreeform | 15 +- rt/share/html/Elements/EditCustomFieldSelect | 4 +- rt/share/html/Elements/EditCustomFieldText | 18 +- rt/share/html/Elements/EditCustomFieldWikitext | 18 +- rt/share/html/Elements/MakeClicky | 48 ++--- rt/share/html/Elements/MyRT | 8 +- rt/share/html/Elements/ShowLink | 2 +- rt/share/html/Elements/SimpleSearch | 3 +- rt/share/html/Install/DatabaseDetails.html | 1 + rt/share/html/NoAuth/js/cascaded.js | 4 +- rt/share/html/Prefs/MyRT.html | 20 +- rt/share/html/Prefs/Search.html | 15 ++ rt/share/html/Search/Bulk.html | 29 +++ rt/share/html/Search/Chart | 2 +- rt/share/html/Search/Chart.html | 2 +- rt/share/html/Search/Elements/BuildFormatString | 10 +- rt/share/html/Search/Elements/Chart | 2 +- rt/share/html/Search/Results.html | 4 +- rt/t/api/date.t | 10 +- rt/t/api/password-types.t | 11 ++ rt/t/fts/indexed_mysql.t | 2 +- rt/t/pod.t | 2 +- rt/t/validator/group_members.t | 116 ++++-------- rt/t/web/path-traversal.t | 11 +- 55 files changed, 491 insertions(+), 358 deletions(-) diff --git a/rt/configure b/rt/configure index 3abb324ba..60d9fb1ea 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 . # @@ -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 47ec7c954..e9f7d5fd6 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 63dd2eecc..766964f42 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 set to `perl-script`. F 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 91e9eec22..dd80ff10f 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 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 a52965a9d..dace2d74c 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 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 898c19ebf..d7d64f599 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 0f0c79a55..e71d6c926 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 18f2b7ae3..23441934f 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 db56cfeb9..52bdc01df 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 5edd7e3f8..eee892dd9 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 4ea1576dc..e6ecdda77 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 17fe44669..06d7f83d2 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 409cbdc45..59d315431 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 37031b18d..07e770724 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 accef228f..bdb2ba6d8 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 cc0d4cc08..d12cf0b5c 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 d958c87d4..8fdfc7b1e 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 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, 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. 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 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 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 <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--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--bugs@fsck.com for I 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 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--bugs@fsck.com, too. Use C for patches. +Send patches to rt-bugs@bestpractical.com, too. Use C 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 d15c1cdcb..050799714 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 2a1f52b90..b15c03d23 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 018ac8a62..af4a6ad99 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 1c75f4250..f377d470c 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 0d11f0124..5bd8f3ed2 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 0d11f0124..5bd8f3ed2 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 ade728f8f..7a1ede816 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://:/", - 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 868105431..66215ad29 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 128e60af0..f0f1c59db 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 0d11f0124..5bd8f3ed2 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 61f6195e5..c489f1c18 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 e56e9ce0f..5d55ffbeb 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 7bfe91114..8eb7b427a 100644 --- a/rt/share/html/Elements/EditCustomFieldAutocomplete +++ b/rt/share/html/Elements/EditCustomFieldAutocomplete @@ -46,7 +46,14 @@ %# %# END BPS TAGGED BLOCK }}} % if ( $Multiple ) { - +