[Rt-commit] rt branch, 4.2.3-releng, created. rt-4.2.2-129-gcce632b

Alex Vandiver alexmv at bestpractical.com
Fri Feb 14 16:31:40 EST 2014


The branch, 4.2.3-releng has been created
        at  cce632b20ca0e16a67945bab3a1e81f1ffdb9216 (commit)

- Log -----------------------------------------------------------------
commit 3caa4236abfd97a4fdded3da0d5c0005589bac4b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jan 10 18:03:38 2014 -0500

    Stop leaking an extra dbh during server startup
    
    RT::Handle->CheckIntegrity is called during rt-server startup, and its
    return value is is stored in $integrity.  Additionally, $RT::Handle is
    explicitly destroyed, to ensure that the database is disconnected before
    forking.
    
    Unfortunately, this replies on the dbh going out of scope, and
    disconnecting during its DESTROY.  As the dbh is stored in the
    $integrity variable in addition to $RT::Handle->dbh, it never goes out
    of scope, and is thus never disconnected.  For fastcgi deployments, this
    causes every fastcgi process to hold open an extra database handle;
    standalone servers only ever gain one extra handle.
    
    As no callsite of CheckIntegrity relies on the $dbh return value, simply
    return true.  This allows the database handle to be disconnected using
    $RT::Handle->dbh(undef).

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index a8652e4..daa748e 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -250,7 +250,7 @@ sub CheckIntegrity {
         return (0, 'no nobody user', "Couldn't find Nobody user in the DB '". $RT::Handle->DSN ."'");
     }
 
-    return $RT::Handle->dbh;
+    return 1;
 }
 
 sub CheckCompatibility {

commit a5873d29214fbfe0099d3203803b5365b224a402
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Jan 10 18:41:51 2014 -0500

    Explicitly disconnect the dbh before forking, if it exists
    
    This remoes the reliance on disconnect-on-DESTROY, and ensures that the
    database handle is disconnected even if outstanding references to
    $RT::Handle->dbh still exist.

diff --git a/sbin/rt-server.in b/sbin/rt-server.in
index 68ec109..ab6d5fe 100644
--- a/sbin/rt-server.in
+++ b/sbin/rt-server.in
@@ -131,6 +131,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;
 }

commit 45b597283548ec3e3b9cdb26bb7026659ace2455
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Wed Feb 5 15:46:38 2014 -0500

    Show static roots in system configuration, along with Mason roots

diff --git a/share/html/Admin/Tools/Configuration.html b/share/html/Admin/Tools/Configuration.html
index 3a0e1f3..295e25e 100644
--- a/share/html/Admin/Tools/Configuration.html
+++ b/share/html/Admin/Tools/Configuration.html
@@ -180,6 +180,15 @@ for my $type (qw/Tickets Queues Transactions Groups PrivilegedUsers Unprivileged
 </ol>
 </&>
 
+<&|/Widgets/TitleBox, title => loc("Static file search order") &>
+<ol>
+% foreach my $path ( (map {$_->{root}} RT->Config->Get('StaticRoots')),
+%                    RT::Interface::Web->StaticRoots ) {
+<li><% $path %></li>
+% }
+</ol>
+</&>
+
 <&|/Widgets/TitleBox, title => loc("Perl library search order") &>
 <ol>
 % foreach my $inc (@INC) {

commit caa19de0df55392ef3c34c138a53470abbe545f9
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Tue Feb 11 18:44:03 2014 -0500

    Fix precedence errors found by perl 5.19.4 of return ... and ...
    
    Other errors are fixed in 3156d1b9, and will be resolved on merge-up
    from 4.0-trunk.

diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index ce20489..a2c1fbf 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -446,8 +446,9 @@ sub Create {
         AdminCc   => sub {
             my $principal = shift;
             return 1 if $self->CurrentUserHasRight('ModifyTicket');
-            return $principal->id == $self->CurrentUser->PrincipalId
-                and $self->CurrentUserHasRight("WatchAsAdminCc");
+            return unless $self->CurrentUserHasRight("WatchAsAdminCc");
+            return unless $principal->id == $self->CurrentUser->PrincipalId;
+            return 1;
         },
         Owner     => sub {
             my $principal = shift;

commit ecf2050678917e6fdb5b43fd6a77920f9dfc693b
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Nov 22 12:47:56 2013 -0500

    Crypt: Make clearer that Auth::Crypt needs Auth::MailFrom as well

diff --git a/lib/RT/Crypt.pm b/lib/RT/Crypt.pm
index e275295..d89cdcd 100644
--- a/lib/RT/Crypt.pm
+++ b/lib/RT/Crypt.pm
@@ -84,7 +84,9 @@ additional options to fine-tune behaviour.
 
 However, note that you B<must> add the
 L<Auth::Crypt|RT::Interface::Email::Auth::Crypt> email filter to enable
-the handling of incoming encrypted/signed messages.
+the handling of incoming encrypted/signed messages.  It should be added
+in addition to the standard
+L<Auth::MailFrom|RT::Interface::Email::Auth::Crypt> plugin.
 
 =head2 %Crypt
 
diff --git a/lib/RT/Interface/Email/Auth/Crypt.pm b/lib/RT/Interface/Email/Auth/Crypt.pm
index 2703af9..ac12a05 100644
--- a/lib/RT/Interface/Email/Auth/Crypt.pm
+++ b/lib/RT/Interface/Email/Auth/Crypt.pm
@@ -66,6 +66,9 @@ it put the module in the mail plugins list:
 
     Set(@MailPlugins, 'Auth::MailFrom', 'Auth::Crypt', ...other filters...);
 
+C<Auth::Crypt> will not function without C<Auth::MailFrom> listed before
+it.
+
 =head3 GnuPG
 
 To use the gnupg-secured mail gateway, you need to do the following:

commit d3a56ec6b802245f132a55b621238a7d78bab2fb
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Nov 22 12:50:27 2013 -0500

    Crypt: Warn if we are altering an explicitly provided Incoming or Outgoing list

diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index a6b3269..a25aabb 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -665,11 +665,18 @@ our %META;
             $opt->{'Incoming'} = [ $opt->{'Incoming'} ]
                 if $opt->{'Incoming'} and not ref $opt->{'Incoming'};
             if ( $opt->{'Incoming'} && @{ $opt->{'Incoming'} } ) {
+                $RT::Logger->warning("$_ explicitly set as incoming Crypt plugin, but not marked Enabled; removing")
+                    for grep {not $enabled{$_}} @{$opt->{'Incoming'}};
                 $opt->{'Incoming'} = [ grep {$enabled{$_}} @{$opt->{'Incoming'}} ];
             } else {
                 $opt->{'Incoming'} = \@enabled;
             }
             if ( $opt->{'Outgoing'} ) {
+                if (not $enabled{$opt->{'Outgoing'}}) {
+                    $RT::Logger->warning($opt->{'Outgoing'}.
+                                             " explicitly set as outgoing Crypt plugin, but not marked Enabled; "
+                                             . (@enabled ? "using $enabled[0]" : "removing"));
+                }
                 $opt->{'Outgoing'} = $enabled[0] unless $enabled{$opt->{'Outgoing'}};
             } else {
                 $opt->{'Outgoing'} = $enabled[0];

commit c55a7ce7873a1a312c8437ff4dd5a4aa97f9c49e
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Nov 22 12:50:50 2013 -0500

    Crypt: Warn if Auth::Crypt is enabled but no Incoming plugins exist

diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index a25aabb..bef4435 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -619,6 +619,10 @@ our %META;
         Type => 'ARRAY',
         PostLoadCheck => sub {
             my $self = shift;
+
+            # Make sure Crypt is post-loaded first
+            $META{Crypt}{'PostLoadCheck'}->( $self, $self->Get( 'Crypt' ) );
+
             my @plugins = $self->Get('MailPlugins');
             if ( grep $_ eq 'Auth::GnuPG' || $_ eq 'Auth::SMIME', @plugins ) {
                 $RT::Logger->warning(
@@ -636,6 +640,10 @@ our %META;
                     } @plugins;
                 $self->Set( MailPlugins => @plugins );
             }
+
+            if ( not @{$self->Get('Crypt')->{Incoming}} and grep $_ eq 'Auth::Crypt', @plugins ) {
+                $RT::Logger->warning("Auth::Crypt enabled in MailPlugins, but no available incoming encryption formats");
+            }
         },
     },
     Crypt        => {

commit a7dc270983483a9a273bec40e54364e670d6e4c0
Merge: 5859ccb caa19de
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Thu Feb 13 16:18:40 2014 -0500

    Merge branch '4.2/perl-5.19.4-compat' into 4.2-trunk


commit 7f6d2cdce4063d8dcb0132bbbad66f6704a2bdf5
Merge: a7dc270 c55a7ce
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Thu Feb 13 16:27:38 2014 -0500

    Merge branch '4.2/crypt-docs' into 4.2-trunk


commit 1e3ac5838a4fdc5864497ac5a1adb35f717c0cc7
Merge: 7f6d2cd 45b5972
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Thu Feb 13 16:55:55 2014 -0500

    Merge branch '4.2/admin-config-static-paths' into 4.2-trunk


commit 15026fd8e85100c20dc4c5847060f46870a40c6f
Merge: 1e3ac58 a5873d2
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Fri Feb 14 11:14:33 2014 -0500

    Merge branch '4.2/fewer-active-handles' into 4.2-trunk


commit 364df767a13143ee113fa30d1460bacb1a32193a
Author: Christian Loos <cloos at netcologne.de>
Date:   Thu Jan 23 15:23:53 2014 +0100

    introduce a new ticket-active class and add status
    
    This makes it possible to style active ticket links different and
    also style ticket links by status.

diff --git a/share/html/Elements/ShowLink b/share/html/Elements/ShowLink
index 51dd70c..18a8909 100644
--- a/share/html/Elements/ShowLink
+++ b/share/html/Elements/ShowLink
@@ -47,9 +47,12 @@
 %# END BPS TAGGED BLOCK }}}
 % my $member = $URI->Object;
 % if (blessed($member) && $member->isa("RT::Ticket")) {
-% my $inactive = $member->QueueObj->IsInactiveStatus($member->Status);
+% my $class = $member->QueueObj->IsInactiveStatus($member->Status)
+%     ? 'ticket-inactive'
+%     : 'ticket-active';
+% $class .= ' '.CSSClass($member->Status);
 
-<span class="<% $inactive ? 'ticket-inactive' : '' %>">
+<span class="<% $class %>">
 <a href="<% $href %>"><%$member->Id%>: <%$member->Subject || ''%> [<% loc($member->Status) %>]</a> (<& /Elements/ShowUser, User => $member->OwnerObj &>)
 </span>
 

commit 1f4a0bc774e4cc6c9a0d2228f193eaadead2403d
Merge: 15026fd 5161fe0
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Feb 14 12:49:46 2014 -0500

    Merge branch '4.0-trunk' into 4.2-trunk
    
    Conflicts:
    	lib/RT/Config.pm
    	lib/RT/Handle.pm
    	share/html/Elements/EditCustomFieldAutocomplete
    	share/html/Elements/EditCustomFieldDateTime
    	share/html/Elements/EditCustomFieldFreeform
    	share/html/Elements/EditCustomFieldSelect
    	share/html/Elements/EditCustomFieldText
    	share/html/Elements/EditCustomFieldWikitext
    	share/html/Elements/ShowLink
    	share/html/Elements/SimpleSearch
    	share/html/Search/Bulk.html
    	share/static/js/cascaded.js
    	t/validator/group_members.t

diff --cc lib/RT/Handle.pm
index daa748e,19cac0c..351478e
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@@ -827,8 -790,9 +827,9 @@@ sub InsertData 
          $RT::Logger->debug("Creating groups...");
          foreach my $item (@Groups) {
              my $new_entry = RT::Group->new( RT->SystemUser );
 -            $item->{Domain} ||= 'UserDefined';
 +            $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 );
diff --cc lib/RT/Interface/Web.pm
index b30036b,b1f8429..8f690fe
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@@ -3598,85 -3120,6 +3598,95 @@@ sub ProcessTransactionSquelching 
      return %squelched;
  }
  
 +sub ProcessRecordBulkCustomFields {
 +    my %args = (RecordObj => undef, ARGSRef => {}, @_);
 +
 +    my $ARGSRef = $args{'ARGSRef'};
 +
 +    my %data;
 +
 +    my @results;
 +    foreach my $key ( keys %$ARGSRef ) {
 +        next unless $key =~ /^Bulk-(Add|Delete)-CustomField-(\d+)-(.*)$/;
 +        my ($op, $cfid, $rest) = ($1, $2, $3);
 +        next if $rest eq "Category";
 +
 +        my $res = $data{$cfid} ||= {};
 +        unless (keys %$res) {
 +            my $cf = RT::CustomField->new( $session{'CurrentUser'} );
 +            $cf->Load( $cfid );
 +            next unless $cf->Id;
 +
 +            $res->{'cf'} = $cf;
 +        }
 +
 +        if ( $op eq 'Delete' && $rest eq 'AllValues' ) {
 +            $res->{'DeleteAll'} = $ARGSRef->{$key};
 +            next;
 +        }
 +
 +        my @values = _NormalizeObjectCustomFieldValue(
 +            CustomField => $res->{'cf'},
 +            Value => $ARGSRef->{$key},
 +            Param => $key,
 +        );
 +        next unless @values;
 +        $res->{$op} = \@values;
 +    }
 +
 +    while ( my ($cfid, $data) = each %data ) {
 +        # just add one value for fields with single value
 +        if ( $data->{'Add'} && $data->{'cf'}->MaxValues == 1 ) {
 +            my ( $id, $msg ) = $args{'RecordObj'}->AddCustomFieldValue(
 +                Field => $cfid,
 +                Value => $data->{'Add'}[-1],
 +            );
 +            push @results, $msg;
 +            next;
 +        }
 +
 +        my $current_values = $args{'RecordObj'}->CustomFieldValues( $cfid );
 +        if ( $data->{'DeleteAll'} ) {
 +            while ( my $value = $current_values->Next ) {
 +                my ( $id, $msg ) = $args{'RecordObj'}->DeleteCustomFieldValue(
 +                    Field   => $cfid,
 +                    ValueId => $value->id,
 +                );
 +                push @results, $msg;
 +            }
 +        }
 +        foreach my $value ( @{ $data->{'Delete'} || [] } ) {
++            # Convert for timezone. Without converstion,
++            # HasEntry and DeleteCustomFieldValue fail because
++            # the value in the DB is converted.
++            if ($data->{'cf'}->Type eq 'DateTime' or $data->{'cf'}->Type eq 'Date') {
++                my $DateObj = RT::Date->new( $session{'CurrentUser'} );
++                $DateObj->Set( Format => 'unknown',
++                               Value  => $value );
++                $value = $data->{'cf'}->Type eq 'DateTime' ? $DateObj->ISO
++                    : $DateObj->ISO(Time => 0, Seconds => 0);
++            }
 +            next unless $current_values->HasEntry($value);
 +
 +            my ( $id, $msg ) = $args{'RecordObj'}->DeleteCustomFieldValue(
 +                Field => $cfid,
 +                Value => $value
 +            );
 +            push @results, $msg;
 +        }
 +        foreach my $value ( @{ $data->{'Add'} || [] } ) {
 +            next if $current_values->HasEntry($value);
 +
 +            my ( $id, $msg ) = $args{'RecordObj'}->AddCustomFieldValue(
 +                Field => $cfid,
 +                Value => $value
 +            );
 +            push @results, $msg;
 +        }
 +    }
 +    return @results;
 +}
 +
  =head2 _UploadedFile ( $arg );
  
  Takes a CGI parameter name; if a file is uploaded under that name,
diff --cc share/html/Elements/BulkCustomFields
index c07c451,0000000..1eb3d59
mode 100644,000000..100644
--- a/share/html/Elements/BulkCustomFields
+++ b/share/html/Elements/BulkCustomFields
@@@ -1,94 -1,0 +1,101 @@@
 +%# BEGIN BPS TAGGED BLOCK {{{
 +%#
 +%# COPYRIGHT:
 +%#
 +%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
 +%#                                          <sales at bestpractical.com>
 +%#
 +%# (Except where explicitly superseded by other copyright notices)
 +%#
 +%#
 +%# LICENSE:
 +%#
 +%# This work is made available to you under the terms of Version 2 of
 +%# the GNU General Public License. A copy of that license should have
 +%# been provided with this software, but in any event can be snarfed
 +%# from www.gnu.org.
 +%#
 +%# This work is distributed in the hope that it will be useful, but
 +%# WITHOUT ANY WARRANTY; without even the implied warranty of
 +%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +%# General Public License for more details.
 +%#
 +%# You should have received a copy of the GNU General Public License
 +%# along with this program; if not, write to the Free Software
 +%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 +%# 02110-1301 or visit their web page on the internet at
 +%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
 +%#
 +%#
 +%# CONTRIBUTION SUBMISSION POLICY:
 +%#
 +%# (The following paragraph is not intended to limit the rights granted
 +%# to you to modify and distribute this software under the terms of
 +%# the GNU General Public License and is only of importance to you if
 +%# you choose to contribute your changes and enhancements to the
 +%# community by submitting them to Best Practical Solutions, LLC.)
 +%#
 +%# By intentionally submitting any modifications, corrections or
 +%# derivatives to this work, or any other work intended for use with
 +%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
 +%# you are the copyright holder for those contributions and you grant
 +%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
 +%# royalty-free, perpetual, license to use, copy, create derivative
 +%# works based on those contributions, and sublicense and distribute
 +%# those contributions and any derivatives thereof.
 +%#
 +%# END BPS TAGGED BLOCK }}}
 +<table class="bulk-edit-custom-fields">
 +
 +<tr>
 +<th><&|/l&>Name</&></th>
 +<th><&|/l&>Add values</&></th>
 +<th><&|/l&>Delete values</&></th>
 +</tr>
 +% my $i = 0;
 +% while (my $cf = $CustomFields->Next) {
 +<tr class="<% ++$i%2 ? 'oddline': 'evenline' %>">
 +<td class="label"><% $cf->Name %><br />
 +<em>(<% $cf->FriendlyType %>)</em></td>
 +% my $rows = 5;
 +% my $cf_id = $cf->id;
 +% my @add = (NamePrefix => 'Bulk-Add-CustomField-', CustomField => $cf, Rows => $rows,
 +%   Multiple => ($cf->MaxValues ==1 ? 0 : 1) , Cols => 25,
 +%   Default => $ARGS{"Bulk-Add-CustomField-$cf_id-Values"} || $ARGS{"Bulk-Add-CustomField-$cf_id-Value"}, );
 +% my @del = (NamePrefix => 'Bulk-Delete-CustomField-', CustomField => $cf,
 +%   Rows => $rows, Multiple => 1, Cols => 25,
 +%   Default => $ARGS{"Bulk-Delete-CustomField-$cf_id-Values"} || $ARGS{"Bulk-Delete-CustomField-$cf_id-Value"}, );
 +% if ($cf->Type eq 'Select') {
 +<td><& /Elements/EditCustomFieldSelect, @add &></td>
 +<td><& /Elements/EditCustomFieldSelect, @del &><br />
 +% } elsif ($cf->Type eq 'Combobox') {
 +<td><& /Elements/EditCustomFieldCombobox, @add &></td>
 +<td><& /Elements/EditCustomFieldCombobox, @del &><br />
 +% } elsif ($cf->Type eq 'Freeform') {
- <td><& /Elements/EditCustomFieldFreeform, @add &>
++<td><& /Elements/EditCustomFieldFreeform, @add &></td>
 +<td><& /Elements/EditCustomFieldFreeform, @del &><br />
 +% } elsif ($cf->Type eq 'Text') {
- <td><& /Elements/EditCustomFieldText, @add &>
++<td><& /Elements/EditCustomFieldText, @add &></td>
 +<td>
++% } elsif ($cf->Type eq 'Date') {
++<td><& /Elements/EditCustomFieldDate, @add, Default => undef &></td>
++<td><& /Elements/EditCustomFieldDate, @del, Default => undef &><br />
++% } 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' &><br />
 +% } else {
 +%   $RT::Logger->crit("Unknown CustomField type: " . $cf->Type);
 +%   next
 +% }
 +  <label><input type="checkbox" name="Bulk-Delete-CustomField-<% $cf_id %>-AllValues" value="1"><em>check to delete all values</em></label>
 +</td>
 +</tr>
 +% }
 +</table>
 +<%ARGS>
 +$CustomFields
 +</%ARGS>
 +<%INIT>
 +return unless $CustomFields->Count;
 +</%INIT>
diff --cc share/html/Elements/EditCustomFieldAutocomplete
index a45afef,8eb7b42..6453eca
--- a/share/html/Elements/EditCustomFieldAutocomplete
+++ b/share/html/Elements/EditCustomFieldAutocomplete
@@@ -46,13 -46,20 +46,20 @@@
  %#
  %# END BPS TAGGED BLOCK }}}
  % if ( $Multiple ) {
- <textarea cols="<% $Cols %>" rows="<% $Rows %>" name="<% $name %>" id="<% $name %>" 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>
++name="<% $name %>" id="<% $name %>" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
  
  <script type="text/javascript">
 -var id = <% "$name-Values" |n,j%>;
 +var id = <% "$name" |n,j%>;
  id = id.replace(/:/g,'\\:');
  jQuery('#'+id).autocomplete( {
 -    source: <%RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% "$name-Values" |n,u,j%>,
 +    source: RT.Config.WebHomePath + "/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% $name |n,u,j%>,
      focus: function () {
          // prevent value inserted on focus
          return false;
diff --cc share/html/Elements/EditCustomFieldDateTime
index 0380687,edf125e..4693e8e
--- a/share/html/Elements/EditCustomFieldDateTime
+++ b/share/html/Elements/EditCustomFieldDateTime
@@@ -59,5 -59,5 +59,6 @@@ $NamePrefix => unde
  $Default => undef
  $Values => undef
  $MaxValues => 1
 +$Name => undef
+ $Format => 'ISO'
  </%ARGS>
diff --cc share/html/Elements/EditCustomFieldFreeform
index a23766d,f0f8ee6..4280172
--- a/share/html/Elements/EditCustomFieldFreeform
+++ b/share/html/Elements/EditCustomFieldFreeform
@@@ -45,11 -45,22 +45,22 @@@
  %# those contributions and any derivatives thereof.
  %#
  %# END BPS TAGGED BLOCK }}}
 -% my $name = $NamePrefix . $CustomField->Id . '-Value';
 +% my $name = $Name || $NamePrefix . $CustomField->Id . ( $Multiple ? '-Values' : '-Value' );
  % if ($Multiple) {
- <textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>" id="<%$name%>" 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>
++name="<%$name%>" id="<%$name%>" 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 --cc share/html/Elements/EditCustomFieldSelect
index c8b1a92,b343d82..222fcd9
--- a/share/html/Elements/EditCustomFieldSelect
+++ b/share/html/Elements/EditCustomFieldSelect
@@@ -102,7 -104,7 +102,7 @@@ jQuery(  function () 
  
  % if ( $RenderType eq 'List' ) {
  <fieldset class="cfedit">
- <div name="<%$name%>" id="<%$name%>">
 -<div data-name="<%$id%>-Values" id="<%$id%>-Values">
++<div data-name="<%$name%>" id="<%$name%>">
  %   if ( $checktype eq 'radio' ) {
    <div class="none">
    <input class="none" type="<% $checktype %>" name="<% $name %>" id="<% $name %>-none" value="" <% keys %default ? '' : ' checked="checked"' |n%> />
diff --cc share/html/Elements/EditCustomFieldText
index 9b14a5c,ca7a266..d310b8f
--- a/share/html/Elements/EditCustomFieldText
+++ b/share/html/Elements/EditCustomFieldText
@@@ -46,10 -46,24 +46,24 @@@
  %#
  %# END BPS TAGGED BLOCK }}}
  % while ($Values and my $value = $Values->Next ) {
- <textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>" 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 />
++name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
  % }
  % if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
- <textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>" 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>
++name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
  % }
  <%INIT>
  # XXX - MultiValue textarea is for now outlawed.
diff --cc share/html/Elements/EditCustomFieldWikitext
index f7c2a68,d4b79cd..cbb317e
--- a/share/html/Elements/EditCustomFieldWikitext
+++ b/share/html/Elements/EditCustomFieldWikitext
@@@ -46,10 -46,24 +46,24 @@@
  %#
  %# END BPS TAGGED BLOCK }}}
  % while ($Values and my $value = $Values->Next ) {
- <textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>" 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 />
++name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
  % }
  % if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
- <textarea cols="<%$Cols%>" rows="<%$Rows%>" name="<%$name%>" 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>
++name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% $Default %></textarea>
  % }
  <%INIT>
  # XXX - MultiValue textarea is for now outlawed.
diff --cc share/html/Elements/ShowLink
index 51dd70c,35b2638..8f18a4c
--- a/share/html/Elements/ShowLink
+++ b/share/html/Elements/ShowLink
@@@ -45,8 -45,11 +45,8 @@@
  %# those contributions and any derivatives thereof.
  %#
  %# END BPS TAGGED BLOCK }}}
 -<a href="<% $href %>">
 -% if ($URI->IsLocal) {
  % my $member = $URI->Object;
- % if (blessed($member) && $member->isa("RT::Ticket")) {
 -% my $has_name = UNIVERSAL::can($member, 'Name') || (UNIVERSAL::can($member, '_Accessible') && $member->_Accessible('Name', 'read'));
 -% if (UNIVERSAL::isa($member, "RT::Ticket") and $member->CurrentUserHasRight('ShowTicket')) {
++% if (blessed($member) and $member->isa("RT::Ticket") and $member->CurrentUserHasRight('ShowTicket')) {
  % my $inactive = $member->QueueObj->IsInactiveStatus($member->Status);
  
  <span class="<% $inactive ? 'ticket-inactive' : '' %>">
diff --cc share/html/Search/Bulk.html
index 3d27a6f,593309a..69adea1
--- a/share/html/Search/Bulk.html
+++ b/share/html/Search/Bulk.html
@@@ -285,7 -416,10 +285,9 @@@ unless ( $ARGS{'AddMoreAttach'} ) 
          @results = ( @results, @tempresults );
      }
  
 -    # Cleanup WebUI
 -    delete $session{'Attachments'};
 +    delete $session{'Attachments'}{ $ARGS{'Token'} };
+ 
+     $Tickets->RedoSearch();
  }
  
  my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );
diff --cc share/static/js/cascaded.js
index 015e821,0000000..c5ea64a
mode 100644,000000..100644
--- a/share/static/js/cascaded.js
+++ b/share/static/js/cascaded.js
@@@ -1,104 -1,0 +1,104 @@@
 +function filter_cascade_by_id (id, vals, is_hierarchical) {
 +    var element = document.getElementById(id);
 +    if (!element) { return };
 +
 +    if ( element.tagName == 'SELECT' ) {
 +        var complete_select = document.getElementById(id + "-Complete" );
 +        return filter_cascade_select(element, complete_select, vals, is_hierarchical);
 +    }
 +    else {
 +        if ( !( vals instanceof Array ) ) {
 +            vals = [vals];
 +        }
 +
 +        if ( is_hierarchical && (vals.length == 0 || (vals.length == 1 && vals[0] == '')) ) {
 +            // no category, and the category is from a hierchical cf;
 +            // leave it empty
 +            jQuery(element).find('div').hide();
 +        }
 +        else {
 +            jQuery(element).find('div').hide().find('input').prop('disabled', true);
-             jQuery(element).find('div[name=]').show().find('input').prop('disabled', false);
++            jQuery(element).find('div[data-name=]').show().find('input').prop('disabled', false);
 +            jQuery(element).find('div.none').show().find('input').prop('disabled',false);
 +            for ( var j = 0; j < vals.length; j++ ) {
-                 jQuery(element).find('div[name^=' + vals[j] + ']').show().find('input').prop('disabled', false);
++                jQuery(element).find('div[data-name^=' + vals[j] + ']').show().find('input').prop('disabled', false);
 +            }
 +        }
 +    }
 +}
 +
 +function filter_cascade_select (select, complete_select, vals, is_hierarchical) {
 +    if ( !( vals instanceof Array ) ) {
 +        vals = [vals];
 +    }
 +
 +    if (!select) { return };
 +    var i;
 +    var children = select.childNodes;
 +
 +    if ( complete_select ) {
 +        jQuery(select).children().remove();
 +
 +        var complete_children = complete_select.childNodes;
 +
 +        var cloned_labels = {};
 +        var cloned_empty_label;
 +        for ( var j = 0; j < vals.length; j++ ) {
 +            var val = vals[j];
 +            if ( val == '' && is_hierarchical ) {
 +                // no category, and the category is from a hierchical cf;
 +                // leave this set of options empty
 +            } else if ( val == '' ) {
 +                // no category, let's clone all node
 +                jQuery(select).append(jQuery(complete_children).clone());
 +                break;
 +            }
 +            else {
 +                var labels_to_clone = {};
 +                for (i = 0; i < complete_children.length; i++) {
 +                    if (!complete_children[i].label ||
 +                          (complete_children[i].hasAttribute &&
 +                                !complete_children[i].hasAttribute('label') ) ) {
 +                        if ( cloned_empty_label ) {
 +                            continue;
 +                        }
 +                    }
 +                    else if ( complete_children[i].label == val ) {
 +                        if ( cloned_labels[complete_children[i].label] ) {
 +                            continue;
 +                        }
 +                        labels_to_clone[complete_children[i].label] = true;
 +                    }
 +                    else {
 +                        continue;
 +                    }
 +
 +                    jQuery(select).append(jQuery(complete_children[i]).clone());
 +                }
 +
 +                if ( !cloned_empty_label )
 +                    cloned_empty_label = true;
 +
 +                for ( label in labels_to_clone ) {
 +                    if ( !cloned_labels[label] )
 +                        cloned_labels[label] = true;
 +                }
 +            }
 +        }
 +    }
 +    else {
 +// for back compatibility
 +        for (i = 0; i < children.length; i++) {
 +            if (!children[i].label) { continue };
 +            if ( val == '' && is_hierarchical ) {
 +                hide(children[i]);
 +                continue;
 +            }
 +            if ( val == '' || children[i].label.substr(0, val.length) == val) {
 +                show(children[i]);
 +                continue;
 +            }
 +            hide(children[i]);
 +        }
 +    }
 +}
diff --cc t/99-policy.t
index 468e87b,39aa448..7e6d4a3
--- a/t/99-policy.t
+++ b/t/99-policy.t
@@@ -1,14 -1,17 +1,18 @@@
  use strict;
  use warnings;
  
 -use RT::Test;
 +use RT::Test tests => undef;
  use File::Find;
 +use IPC::Run3;
  
  my @files;
- find( { wanted   => sub { push @files, $File::Find::name if -f },
+ find( { wanted   => sub {
+             push @files, $File::Find::name if -f;
+             $File::Find::prune = 1 if $_ eq "t/tmp" or m{/\.git$};
+         },
          no_chdir => 1 },
        qw{etc lib share t bin sbin devel/tools} );
+ 
  if ( my $dir = `git rev-parse --git-dir 2>/dev/null` ) {
      # We're in a git repo, use the ignore list
      chomp $dir;
diff --cc t/validator/group_members.t
index 7a37ed5,af93c51..0fd1a74
--- a/t/validator/group_members.t
+++ b/t/validator/group_members.t
@@@ -2,46 -2,19 +2,15 @@@
  use strict;
  use warnings;
  
- use RT::Test tests => 62;
- 
- 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;
- }
 -use RT::Test tests => 63;
++use RT::Test tests => undef;
  
 -{
 -    my ($ecode, $res) = RT::Test->run_validator();
 -    is $res, '', 'empty result';
 -}
 +RT::Test->db_is_valid;
  
  {
-     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) = RT::Test->run_validator();
 -    is $res, '', 'empty result';
 +    RT::Test->db_is_valid;
  }
  
  # G1 -> G2
@@@ -115,10 -90,11 +84,10 @@@
          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) = RT::Test->run_validator();
 -    is $res, '', 'empty result';
 +    RT::Test->db_is_valid;
  }
  
  # G1 <- (G2, G3, G4) <- G5
@@@ -133,9 -109,27 +102,27 @@@
          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) = RT::Test->run_validator();
 -    is $res, '', 'empty result';
 +    RT::Test->db_is_valid;
  }
  
+ # 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;
++    isnt($ecode, 0, 'non-zero exit code');
+ 
 -    ($ecode, $res) = RT::Test->run_validator();
 -    is $res, '', 'empty result';
++    RT::Test->db_is_valid;
+ }
++
++done_testing;

commit f6e4a2f6205f442e965b8b7c1270f890112eb2b6
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Feb 14 01:21:50 2014 -0500

    Pass the role type to the ACL method, as AddRoleMember does
    
    This caused failures in removing watchers from tickets and queues, when
    the remover only had Watch / WatchAsAcminCc; see [rt3 #29063]
    
    This is now also consistent with how all existing _HasModifyWatcherRight
    methods (the only functions currently passed for ACL) expect to be
    called.

diff --git a/lib/RT/Record/Role/Roles.pm b/lib/RT/Record/Role/Roles.pm
index a6172b4..ac7e0c2 100644
--- a/lib/RT/Record/Role/Roles.pm
+++ b/lib/RT/Record/Role/Roles.pm
@@ -483,7 +483,7 @@ sub DeleteRoleMember {
 
     my $acl = delete $args{ACL};
     return (0, $self->loc("Permission denied"))
-        if $acl and not $acl->($principal);
+        if $acl and not $acl->($args{Type} => $principal);
 
     my $group = $self->RoleGroup( $args{Type} );
     return (0, $self->loc("Role group '[_1]' not found", $args{Type}))

commit fb67b690896c63c5c737ae467e8969f2dc5f332f
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri Feb 14 01:24:23 2014 -0500

    Document the ACL argument, as AddRoleMember does

diff --git a/lib/RT/Record/Role/Roles.pm b/lib/RT/Record/Role/Roles.pm
index ac7e0c2..c1e03fd 100644
--- a/lib/RT/Record/Role/Roles.pm
+++ b/lib/RT/Record/Role/Roles.pm
@@ -451,6 +451,12 @@ principal
 
 Required.  One of the valid roles for this record, as returned by L</Roles>.
 
+=item ACL
+
+Optional.  A subroutine reference which will be passed the role type and
+principal being removed.  If it returns false, the method will fail with a
+status of "Permission denied".
+
 =back
 
 One, and only one, of I<PrincipalId> or I<User> is required.

commit 235702851c3e20114629329ddab552669f5503ea
Merge: 1f4a0bc 364df76
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Fri Feb 14 13:59:00 2014 -0500

    Merge branch '4.2/ticket-status-in-link-class' into 4.2-trunk
    
    Conflicts:
    	share/html/Elements/ShowLink

diff --cc share/html/Elements/ShowLink
index 8f18a4c,18a8909..fec4b20
--- a/share/html/Elements/ShowLink
+++ b/share/html/Elements/ShowLink
@@@ -46,10 -46,13 +46,13 @@@
  %#
  %# END BPS TAGGED BLOCK }}}
  % my $member = $URI->Object;
 -% if (blessed($member) && $member->isa("RT::Ticket")) {
 +% if (blessed($member) and $member->isa("RT::Ticket") and $member->CurrentUserHasRight('ShowTicket')) {
- % my $inactive = $member->QueueObj->IsInactiveStatus($member->Status);
+ % my $class = $member->QueueObj->IsInactiveStatus($member->Status)
+ %     ? 'ticket-inactive'
+ %     : 'ticket-active';
+ % $class .= ' '.CSSClass($member->Status);
  
- <span class="<% $inactive ? 'ticket-inactive' : '' %>">
+ <span class="<% $class %>">
  <a href="<% $href %>"><%$member->Id%>: <%$member->Subject || ''%> [<% loc($member->Status) %>]</a> (<& /Elements/ShowUser, User => $member->OwnerObj &>)
  </span>
  

commit cce632b20ca0e16a67945bab3a1e81f1ffdb9216
Merge: 2357028 fb67b69
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Fri Feb 14 13:59:33 2014 -0500

    Merge branch '4.2/remove-role-acl' into 4.2-trunk


-----------------------------------------------------------------------


More information about the rt-commit mailing list