Merge branch 'FREESIDE_3_BRANCH' of git.freeside.biz:/home/git/freeside into FREESIDE...
authorIvan Kohler <ivan@freeside.biz>
Sun, 18 Aug 2013 05:36:45 +0000 (22:36 -0700)
committerIvan Kohler <ivan@freeside.biz>
Sun, 18 Aug 2013 05:36:45 +0000 (22:36 -0700)
bin/part_pkg-bulk_change
httemplate/edit/bulk-part_pkg.html
httemplate/edit/process/bulk-part_pkg.html
httemplate/elements/checkbox-tristate.html
rt/lib/RT/Interface/Web_Vendor.pm
rt/lib/RT/Ticket.pm

index 64670de..cf65c39 100755 (executable)
@@ -1,19 +1,22 @@
 #!/usr/bin/perl
 
 use strict;
-use vars qw( $opt_r $opt_o $opt_v );
+use vars qw( $opt_r $opt_p $opt_o $opt_v );
 use Getopt::Std;
 use FS::UID qw(adminsuidsetup);
 use FS::Record qw(qsearch qsearchs);
 use FS::part_pkg;
 use FS::part_pkg_option;
 
-getopts('ro:v:');
+getopts('rp:o:v:');
 
 my $user = shift or &usage;
 adminsuidsetup $user;
 
-foreach my $part_pkg ( qsearch('part_pkg', {}) ) {
+my %plan;
+%plan = ( 'plan' => $opt_p ) if $opt_p;
+
+foreach my $part_pkg ( qsearch('part_pkg',\%plan) ) {
   next if ! $part_pkg->freq && $opt_r;
 
   my %hash = (
index a1c6f0c..4665c9f 100644 (file)
@@ -6,6 +6,7 @@
 .row0 { background-color: #eeeeee; }
 .row1 { background-color: #ffffff; }
 </STYLE>
+<& /elements/error.html &>
 
 <FORM ACTION="process/bulk-part_pkg.html" METHOD="POST">
 <DIV>
@@ -22,15 +23,12 @@ The following packages will be changed:<BR>
 % foreach my $num (sort keys %report_class) {
   <TR CLASS="row<%$row % 2%>">
     <TD>
-%   if ( defined $initial_state{$num} ) {
-      <& /elements/checkbox.html,
-            field => 'report_option_'.$num,
-            value => 1,
-            curr_value => $initial_state{$num}
-      &>
-%   } else {
+%   if ( $initial_state{$num} == -1 ) {
 %     # needs to be a tristate so that you can say "don't change it"
       <& /elements/checkbox-tristate.html, field => 'report_option_'.$num &>
+%   } else {
+%# for visual consistency
+      <INPUT TYPE="checkbox" CLASS="partial" NAME="report_option_<%$num%>" VALUE="1" <% $initial_state{$num} ? 'CHECKED':'' %>><LABEL />
 %   }
     </TD>
     <TD><% $report_class{$num}->name %></TD>
@@ -64,11 +62,11 @@ foreach my $num (keys %report_class) {
     }
   }
   if ( $yes and $no ) {
-    $initial_state{$num} = undef;
+    $initial_state{$num} = -1;
   } elsif ( $yes ) {
     $initial_state{$num} = 1;
   } elsif ( $no ) {
-    $initial_state{$num} = 0;
+    $initial_state{$num} = '';
   } # else, uh, you didn't provide any pkgparts
 }
 </%init>
index 4775a93..59c914a 100644 (file)
@@ -1,6 +1,6 @@
 % if ( $error ) {
 %  $cgi->param('error', $error);
-<% $cgi->redirect(popurl(3).'/edit/bulk-part_pkg.cgi?', $cgi->query_string) %>
+<% $cgi->redirect(popurl(3).'/edit/bulk-part_pkg.html?'.$cgi->query_string) %>
 % } else {
 <% $cgi->redirect(popurl(3).'/browse/part_pkg.cgi') %>
 % }
@@ -10,21 +10,26 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk edi
 my @pkgparts = $cgi->param('pkgpart')
   or die "no package definitions selected";
 
-my %changes;
+my %delete = map { 'report_option_'.($_->num) => 1 }
+  qsearch('part_pkg_report_option', {});
+my %insert;
+
 foreach my $param (grep { /^report_option_\d+$/ } $cgi->param) {
-  if ( length($cgi->param($param)) ) {
-    if ( $cgi->param($param) == 1 ) {
-      $changes{$param} = 1;
-    } else {
-      $changes{$param} = '';
-    }
-  }
+  if ( $cgi->param($param) == 1 ) {
+    $insert{$param} = 1;
+    delete $delete{$param};
+  } elsif ( $cgi->param($param) == -1 ) {
+    # leave it alone
+    delete $delete{$param};
+  } # else it's empty, so leave it on the delete list
 }
 
+
 my $error;
 foreach my $pkgpart (@pkgparts) {
   my $part_pkg = FS::part_pkg->by_key($pkgpart);
-  my %options = ( $part_pkg->options, %changes );
+  my %options = ( $part_pkg->options, %insert );
+  delete $options{$_} foreach keys(%delete);
   $error ||= $part_pkg->replace( options => \%options );
 }
 </%init>
index 4c26ed7..90966a5 100644 (file)
@@ -2,29 +2,54 @@
 A tristate checkbox (with three values: true, false, and null).
 Internally, this creates a checkbox, coupled via javascript to a hidden
 field that actually contains the value.  For now, the only values these
-can have are 1, 0, and empty.  Clicking the checkbox cycles between them.
+can have are 1, -1, and empty.  Clicking the checkbox cycles between them.
+
+For compatibility with regular checkboxes, empty is the false state and 
+-1 is the indeterminate state.
+
+Displaying these is a problem.  "indeterminate" is a standard HTML5 attribute
+but some browsers display it in unhelpful ways (e.g. Firefox slightly grays
+the checkbox, approximately #dddddd), and checkboxes ignore nearly all CSS 
+styling.
 </%doc>
 <%shared>
 my $init = 0;
 </%shared>
 % if ( !$init ) {
 %   $init = 1;
+<STYLE>
+input.partial {
+  position: absolute;
+  opacity: 0;
+  z-index: 1;
+}
+input.partial + label::before {
+  position: relative;
+  content: "\2610";
+}
+input.partial:checked + label::before {
+  content: "\2611";
+}
+input.partial:indeterminate + label::before {
+  content: "\2612";
+}
+</STYLE>
 <SCRIPT TYPE="text/javascript">
 function tristate_onclick() {
   var checkbox = this;
   var input = checkbox.input;
-  if ( input.value == "" ) {
-    input.value = "0";
-    checkbox.checked = false;
-    checkbox.indeterminate = false;
-  } else if ( input.value == "0" ) {
+  if ( input.value == "" ) { // false -> true
     input.value = "1";
     checkbox.checked = true;
     checkbox.indeterminate = false;
-  } else if ( input.value == "1" ) {
+  } else if ( input.value == "1" ) { // true -> indeterminate
+    input.value = "-1";
+    checkbox.checked = false;
+    checkbox.indeterminate = true;
+  } else if ( input.value == "-1" ) { // indeterminate -> false
     input.value = "";
-    checkbox.checked = true;
-    checkbox.indeterminate = true
+    checkbox.checked = false;
+    checkbox.indeterminate = false;
   }
 }
 
@@ -47,10 +72,10 @@ window.onload = function() { // don't do this until all of the checkboxes exist
     // set event handler
     tristate_boxes[i].onclick = tristate_onclick;
     // set initial value
-    if ( tristates[i].value == "" ) {
+    if ( tristates[i].value == "-1" ) {
       tristate_boxes[i].indeterminate = true
     }
-    if ( tristates[i].value != "0" ) {
+    if ( tristates[i].value == "1" ) {
       tristate_boxes[i].checked = true;
     }
   }
@@ -62,6 +87,7 @@ window.onload = function() { // don't do this until all of the checkboxes exist
                      VALUE="<% $curr_value %>"
                      CLASS="tristate">
 <INPUT TYPE="checkbox" ID="checkbox_<%$opt{field}%>" CLASS="partial">
+<LABEL />
 <%init>
 
 my %opt = @_;
@@ -72,7 +98,10 @@ my %opt = @_;
 #                 : '';
 
 $opt{'id'} ||= 'hidden_'.$opt{'field'};
-my $curr_value = $opt{curr_value};
-$curr_value = undef
-  unless $curr_value eq '0' or $curr_value eq '1';
+my $curr_value = '-1';
+if (exists $opt{curr_value}) {
+  $curr_value = $opt{curr_value};
+  $curr_value = '' unless $curr_value eq '-1' or $curr_value eq '1';
+}
+
 </%init>
index 7a4804b..0c061e2 100644 (file)
@@ -257,17 +257,19 @@ sub ProcessTicketBasics {
         WillResolve
     );
 
-# causes endless redirect loops and "WillResolve changed from Not set to Not set" on ticket view?
-#    # the UI for editing WillResolve through Ticket Basics should allow 
-#    # setting it to null
-#    my $to_date = delete($ARGSRef->{'WillResolve_Date'});
-#    my $DateObj = RT::Date->new($session{'CurrentUser'});
-#    if ( $to_date ) {
-#        $DateObj->Set(Format => 'unknown', Value => $to_date);
-#    } else {
-#        $DateObj->Set(Value => 0);
-#    }
-#    $ARGSRef->{'WillResolve'} = $DateObj->ISO;
+    # the UI for editing WillResolve through Ticket Basics should allow 
+    # setting it to null
+    if ( exists $ARGSRef->{'WillResolve_Date'} ) {
+      my $to_date = delete($ARGSRef->{'WillResolve_Date'});
+      my $DateObj = RT::Date->new($session{'CurrentUser'});
+      if ( $to_date ) {
+          $DateObj->Set(Format => 'unknown', Value => $to_date);
+          $ARGSRef->{'WillResolve'} = $DateObj->ISO;
+      } elsif ( $TicketObj and $TicketObj->WillResolveObj->Unix > 0 ) {
+          $DateObj->Set(Value => 0);
+          $ARGSRef->{'WillResolve'} = $DateObj->ISO;
+      }
+    }
 
     if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) {
         my $tempqueue = RT::Queue->new($RT::SystemUser);
index 4da1d48..6165378 100755 (executable)
@@ -255,6 +255,7 @@ sub Create {
         Starts             => undef,
         Started            => undef,
         Resolved           => undef,
+        WillResolve        => undef,
         MIMEObj            => undef,
         _RecordTransaction => 1,
         DryRun             => 0,
@@ -357,6 +358,11 @@ sub Create {
         $Started->Set( Format => 'ISO', Value => $args{'Started'} );
     }
 
+    my $WillResolve = RT::Date->new($self->CurrentUser );
+    if ( defined $args{'WillResolve'} ) {
+        $WillResolve->Set( Format => 'ISO', Value => $args{'WillResolve'} );
+    }
+
     # If the status is not an initial status, set the started date
     elsif ( !$cycle->IsInitial($args{'Status'}) ) {
         $Started->SetToNow;
@@ -483,6 +489,7 @@ sub Create {
         Starts          => $Starts->ISO,
         Started         => $Started->ISO,
         Resolved        => $Resolved->ISO,
+        WillResolve     => $WillResolve->ISO,
         Due             => $Due->ISO
     );