[Rt-commit] rt branch, 4.2-trunk, updated. rt-4.2.2-124-g1f4a0bc

Alex Vandiver alexmv at bestpractical.com
Fri Feb 14 13:31:30 EST 2014


The branch, 4.2-trunk has been updated
       via  1f4a0bc774e4cc6c9a0d2228f193eaadead2403d (commit)
       via  5161fe003ef41485df10eaf96ba022c65e23ca7d (commit)
       via  5445a1ea0b9cdd6dee6e33891aa9aab67b0b91fc (commit)
       via  cff2773674018de554ae69f410893038ef9ed750 (commit)
       via  b6caf1a0c65adeb901de90dcfc4b321d0b651c28 (commit)
       via  125a5b74f9390876cbf53fa5772f9b2c3db28e30 (commit)
       via  cfafcbd51cb0b446bd6c26587f4fa13f71ee7e9e (commit)
       via  bc7df0872eaf2e3a5ad3133b7750fb063047cd85 (commit)
       via  71e322d893df7d383d7fe0d30fcb46da9ec52026 (commit)
       via  728b3022635b9dcc007a784b5aab3f2e0ef7ab3a (commit)
       via  794fe4cfb01f13023b32e0a90a3b8661d93aa782 (commit)
       via  7719f84cc3fe0dbbb670b2568fa722177b172c4b (commit)
       via  ed759dbeafae5f11d3f998b487935b3eddc7a569 (commit)
       via  58465629fd55bb79db03b3f777cbe0f4e62cb95a (commit)
       via  3b1dfdcb1f9adcd1fbc86e8748b85cc94511f98f (commit)
       via  00cee2b9db60f47e76751cb4f4924f1158ab9fda (commit)
       via  38a2ad31df62c3fe1944477194352f3bc8d2195f (commit)
       via  ae2434b9a3137fe039619c14d7bffa16fd30d828 (commit)
       via  f4bbf9de2f3379e72c662d86ed871e4b3a9de7f4 (commit)
       via  aed50ba745992303993aaa529122c1723044dea1 (commit)
       via  b9136eca8fd37549dc79923f99427264ec9d3472 (commit)
       via  4335ba6812ffab7c8dcc6f8ff91485de3d228e27 (commit)
       via  43f534acdb227a8f56c31434e763bf307440faf7 (commit)
       via  d73b3f520ae929435ebc5048f52b5a5c3fd79aee (commit)
       via  2136966eed4a9805bd6f53c352f3fe6d2025692c (commit)
       via  4aaa0bbec6faabb75bf0a3c0b2467784d7777954 (commit)
       via  edfdb3dca452ca678c950fc8c3fe09856b933273 (commit)
       via  e4a2bb753a6401687c68cd241643e78687de9631 (commit)
       via  b5e4b43c8cf68dc51662a09860f2fe8afdba144a (commit)
       via  01e4bdc26f3b195e2c55f6a36a3286cec22a921d (commit)
       via  a5da1630751326fcad4d9bc06c63a5aa9404ef6a (commit)
       via  a849f253858d5c8156b6c515dae63c456e8f135a (commit)
       via  44bda4ae3eb4854c65a9858e162df0d85312bfec (commit)
       via  8b4566d17e99d83d956657d39124672f4be31c03 (commit)
       via  e4a1a4cb28b42238b9e395492a9cdaebfb3f1d27 (commit)
       via  ad8d7a5e1d7741b21dd38268adeedcfbe5ed59fc (commit)
       via  3156d1b9680384fb97908dc93f63932d54d8b079 (commit)
       via  547af3b40d401d9d4dc56cd76717b3bfdedb7cb7 (commit)
       via  a39f5fac6465f3e892d2e02723f030cbf36646f4 (commit)
       via  1d0f69fde472a296863661f6489aa184523d2067 (commit)
       via  21c7ddf7189e35a2173636a91455165d506c9dc3 (commit)
      from  15026fd8e85100c20dc4c5847060f46870a40c6f (commit)

Summary of changes:
 .gitignore                                         |   1 +
 configure.ac                                       |   1 +
 docs/UPGRADING-4.0                                 |   3 +
 docs/extending/clickable_links.pod                 |   2 +-
 docs/initialdata.pod                               |  50 +--
 docs/writing_extensions.pod                        | 376 +++++++++++++++++++++
 .../upgrade/4.0-customfield-checkbox-extension.in  |  45 ++-
 lib/RT.pm                                          |   4 +-
 lib/RT/Handle.pm                                   |  38 ++-
 lib/RT/Interface/Web.pm                            |  12 +-
 lib/RT/Shredder/Plugin/SQLDump.pm                  |   4 +-
 lib/RT/Template.pm                                 |   6 +
 lib/RT/Test.pm                                     |  33 ++
 sbin/rt-validator.in                               |   4 +-
 share/html/Dashboards/Queries.html                 |   2 +-
 share/html/Elements/BulkCustomFields               |  11 +-
 share/html/Elements/EditCustomFieldAutocomplete    |   9 +-
 share/html/Elements/EditCustomFieldDateTime        |   3 +-
 share/html/Elements/EditCustomFieldFreeform        |  15 +-
 share/html/Elements/EditCustomFieldSelect          |   4 +-
 share/html/Elements/EditCustomFieldText            |  18 +-
 share/html/Elements/EditCustomFieldWikitext        |  18 +-
 share/html/Elements/MakeClicky                     |   2 +-
 share/html/Elements/ShowLink                       |   2 +-
 share/html/Search/Bulk.html                        |   2 +
 share/static/js/cascaded.js                        |   4 +-
 t/99-policy.t                                      |   6 +-
 t/api/template-parsing.t                           |  18 +-
 t/validator/group_members.t                        |  69 ++--
 29 files changed, 627 insertions(+), 135 deletions(-)
 create mode 100644 docs/writing_extensions.pod
 copy lib/RT/Condition/PriorityChange.pm => etc/upgrade/4.0-customfield-checkbox-extension.in (73%)

- Log -----------------------------------------------------------------
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;

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


More information about the rt-commit mailing list