[freeside-commits] branch FREESIDE_4_BRANCH updated. 343ae4008c577b1bab4e79b70895fe7c894a6d2f

Jonathan Prykop jonathan at 420.am
Tue Apr 5 01:03:23 PDT 2016


The branch, FREESIDE_4_BRANCH has been updated
       via  343ae4008c577b1bab4e79b70895fe7c894a6d2f (commit)
       via  df26343f51cce2957413c0a210308892db533e5b (commit)
       via  d07285899ebbe9b9578b90db3913eb8d5b63ebf4 (commit)
       via  eb985ed029f86ad4056c78ae95417f3402359d62 (commit)
      from  db02f61252803872f2acc3dd885b693d345cc5c3 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 343ae4008c577b1bab4e79b70895fe7c894a6d2f
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Thu Mar 31 21:35:14 2016 -0500

    RT#34237: installer scheduling [draggable divs]

diff --git a/rt/share/html/Search/Schedule.html b/rt/share/html/Search/Schedule.html
index cfa4558..6a62c27 100644
--- a/rt/share/html/Search/Schedule.html
+++ b/rt/share/html/Search/Schedule.html
@@ -2,11 +2,26 @@
 
 <SCRIPT TYPE="text/javascript">
 
+  // sets cell content and bgcolor in a div, for use as a draggable
+  //   (draggable tds have border problems on FF/IE)
+  function set_cell_div ($cell,content,bgcolor) {
+    var $div = $cell.data('div');
+    if (!$div) {
+      $div = $(document.createElement('div'));
+      $div.data('cell',$cell);
+      $cell.data('div',$div);
+      $cell.append($div);
+    }
+    $div.css('width','100%');
+    $div.css('background-color', bgcolor);
+    $div.html(content || ' ');
+  }
+
   // gives cell the appearance dictated by its data
   function set_data_cell ($cell) {
     $cell.css('border',  '1px solid #D7D7D7' );
     $cell.css('background-color', $cell.data('bgcolor'));
-    $cell.html($cell.data('content'));
+    set_cell_div($cell,$cell.data('content'),$cell.data('bgcolor'));
   }
 
   // sets cell data and appearance to schedulable
@@ -56,6 +71,7 @@
     for ( var c=0; c < <%$cells%>; c++) {
 
       $this.css('background-color', '#ffffdd');
+      set_cell_div($this,'','#ffffdd');
       if ( c == 0 ) {
         $this.css('border-top', '1px double black');
       }
@@ -75,6 +91,7 @@
     var $this = $(what);
     for ( var c=0; c < <%$cells%>; c++) {
       $this.css('background-color', '#ffffff');
+      set_cell_div($this,'','#ffffff');
       $this.css('border', '1px solid #D7D7D7'); //watch out in IE8 woes, empty string removes cell borders
       var rownum = $this.parent().prevAll('tr').length;
       var colnum = $this.prevAll('td').length;
@@ -90,14 +107,19 @@
   // ticket-dependant test if we can drop here
   // prevent overlap with other appointments, while allowing appointment to overlap itself
   function can_drop ($where, ui) {
-    var cells = ui.draggable.data('cells');
-    var ticketid = ui.draggable.data('ticketid');
+    var cells = ui.draggable.data('cell').data('cells');
+    var ticketid = ui.draggable.data('cell').data('ticketid');
     for (var c=0; c < cells; c++) {
       if (!$where.is('.ui-droppable')) {
         return false;
       }
-      if ($where.data('ticketid') && ($where.data('ticketid') != ticketid)) {
-        return false;
+      if ($where.data('ticketid')) {
+        if ($where.data('ticketid') != ticketid) {
+          return false;
+        }
+        if ($where.data('offset') == c) { // don't reschedule in the same slot
+          return false;
+        }
       }
       var rownum = $where.parent().prevAll('tr').length;
       var colnum = $where.prevAll('td').length;
@@ -117,12 +139,14 @@
 
   // makes cell draggable (able to be rescheduled)
   function set_draggable_cell ($cell) {
-    $cell.draggable({
+    var $div = $cell.data('div');
+    $div.draggable({
       containment: '.titlebox-content',
       revert: true,
       revertDuration: 0,
       start: appointment_drag_start,
       stop: appointment_drag_stop,
+      zIndex: 10,
     });
   }
 
@@ -130,7 +154,7 @@
   function set_white_cell ($cell) {
     $cell.css('border',  '1px solid #D7D7D7' );
     $cell.css('background-color', '#FFFFFF');
-    $cell.html('');
+    set_cell_div($cell,'','#FFFFFF');
   }
 
   // track drag highlighting
@@ -142,6 +166,7 @@
       for ( var c=0; c < cells; c++) {
         if (drag_hi.data('isdragging')) {
           drag_hi.css('border',  '1px solid #D7D7D7' );
+          drag_hi.css('background-color',  '#FFFFFF' );
         } else {
           set_white_cell(drag_hi);
         }
@@ -156,9 +181,9 @@
   // drag start event
   function appointment_drag_start(event, ui) {
     var $this = $(this);
-    // cell that's actually dragging
-    $this.html($this.data('label'));
-    $this.css('z-index',10);
+    // cell that's dragging
+    $this = $this.data('cell');
+    set_cell_div($this,$this.data('label'),$this.data('bgcolor'));
     $this.data('isdragging',true);
     var offset = $this.data('offset');
     var cells  = $this.data('cells');
@@ -168,7 +193,11 @@
     $this = $this.parent().parent().children('tr').eq(rownum-offset).children('td').eq(colnum);
     // loop through all cells in appointment
     for ( var c=0; c < cells; c++) {
-      if (c != offset) set_white_cell($this);
+      if ($this.data('isdragging')) {
+        $this.css('background-color', '#FFFFFF');
+      } else {
+        set_white_cell($this);
+      }
       var rownum = $this.parent().prevAll('tr').length;
       var colnum = $this.prevAll('td').length;
       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
@@ -178,10 +207,10 @@
   // drag stop event
   function appointment_drag_stop(event, ui) {
     var $this = $(this);
-    // the cell that was dragging
+    // cell that's dragging
+    $this = $this.data('cell');
     var cells = $this.data('cells');
     clear_drag_hi(cells);
-    $this.css('z-index','initial');
     $this.data('isdragging',false);
     var offset = $this.data('offset');
     // jump to first cell in appointment
@@ -199,17 +228,20 @@
 
   // drag over event
   function appointment_drag_over(event, ui) {
-    // the cell that is dragging
-    var cells = ui.draggable.data('cells');
-	// the droppable cell that you're over
+    // the cell that's dragging
+    var cells = ui.draggable.data('cell').data('cells');
+    // the droppable cell that you're over
     var $this = $(this);
     clear_drag_hi(cells);
     if (!can_drop($this, ui)) return;
     drag_hi = $this;
     // loop through potential appointment cells
     for ( var c=0; c < cells; c++) {
+      $this.css('background-color', '#ffffdd');
       if ( !$this.data('isdragging')) {
-        $this.css('background-color', '#ffffdd');
+        set_cell_div($this,'','#ffffdd');
+      } else {
+        $this.css('background-color','#ffffdd');
       }
       if ( c == 0 ) {
         $this.css('border-top', '1px double black');
@@ -228,16 +260,17 @@
   // drop event
   function reschedule_appointment( event, ui ) {
 
+	// the droppable cell that you're over
     var $this = $(this);
 
     if (!can_drop($this, ui)) return;
 
 %   #get the ticket number and appointment length (from the draggable object)
-    var draggable = ui.draggable;
-    var ticketid = draggable.data('ticketid');
-    var length   = draggable.data('length');
-    var bgcolor  = draggable.data('bgcolor');
-    var offset   = draggable.data('offset');
+    var dragcell = ui.draggable.data('cell');
+    var ticketid = dragcell.data('ticketid');
+    var length   = dragcell.data('length');
+    var bgcolor  = dragcell.data('bgcolor');
+    var offset   = dragcell.data('offset');
 
 %   #and.. the new date and time, and username (from the droppable object)
     var starts   = $this.data('starts');
@@ -247,7 +280,7 @@
     var n_st_tod_row   = $this.data('tod_row');
 
     var droppable = $this;
-    draggable.effect( "transfer", { to: droppable }, 420 );
+    ui.draggable.effect( "transfer", { to: droppable }, 420 );
 
 %   #tell the backend to reschedule it
     var url = "<% popurl(3) %>misc/xmlhttp-ticket-update.html?" +
@@ -264,23 +297,23 @@
         var label = data.sched_label;
 
         // jump to first cell in appointment
-        var rownum = draggable.parent().prevAll('tr').length;
-        var colnum = draggable.prevAll('td').length;
-        draggable = draggable.parent().parent().children('tr').eq(rownum-offset).children('td').eq(colnum);
+        var rownum = dragcell.parent().prevAll('tr').length;
+        var colnum = dragcell.prevAll('td').length;
+        dragcell = dragcell.parent().parent().children('tr').eq(rownum-offset).children('td').eq(colnum);
 
         // remove old appointment entirely
-        var epoch        = draggable.data('epoch');
-        var st_tod_row   = draggable.data('tod_row');
-        var old_username = draggable.data('username');
-        var cells        = draggable.data('cells');
+        var epoch        = dragcell.data('epoch');
+        var st_tod_row   = dragcell.data('tod_row');
+        var old_username = dragcell.data('username');
+        var cells        = dragcell.data('cells');
         for ( var c=0; c < cells; c++) {
           var tod_row = parseInt(st_tod_row) + (c * <%$timestep%>);
           var td_id = 'td_' + epoch +
                       '_' + String( tod_row ) +
                       '_' + old_username;
           var $cell = $('#'+td_id);
+          $cell.data('div').draggable('destroy');
           set_schedulable_cell($cell);
-          $cell.draggable('destroy');
           set_droppable_cell($cell);
         }
 

commit df26343f51cce2957413c0a210308892db533e5b
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Thu Mar 31 18:10:41 2016 -0500

    RT#34237: installer scheduling [SetOwner not necessarily a Steal]

diff --git a/httemplate/misc/xmlhttp-ticket-update.html b/httemplate/misc/xmlhttp-ticket-update.html
index bd58b95..e81e353 100644
--- a/httemplate/misc/xmlhttp-ticket-update.html
+++ b/httemplate/misc/xmlhttp-ticket-update.html
@@ -8,14 +8,12 @@ my $username = $cgi->param('username');
 
 my $ticket = FS::TicketSystem->get_ticket_object( \%session, ticket_id=>$id );
 
-#hmm, this should happen in a single transaction and either commit or rollback,
-# but in reality failures "Don't Happen" so its not like a ticket gets
-# half changed
+#hmm, this should happen in a single transaction and either commit or rollback
 
 my $return;
 if ( $ticket ) {
 
-  my($orv, $omsg) = $ticket->SetOwner( $username, 'Steal' );
+  my($orv, $omsg) = $ticket->SetOwner( $username );
   $orv = 1 if ! $orv && $omsg =~ /already own/i;
 
   if ( $orv ) {

commit d07285899ebbe9b9578b90db3913eb8d5b63ebf4
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Thu Mar 31 18:08:37 2016 -0500

    RT#34237: installer scheduling [bug fixes for refactor]

diff --git a/rt/share/html/Elements/CalendarSlotSchedule b/rt/share/html/Elements/CalendarSlotSchedule
index c8d3bfa..b5c08d6 100644
--- a/rt/share/html/Elements/CalendarSlotSchedule
+++ b/rt/share/html/Elements/CalendarSlotSchedule
@@ -21,7 +21,7 @@
 %
 %   my $bgcolor = '#666666';
 %   my $border = '1px solid #555555';
-%   my $content = '';
+%   my $label = '';
 %   my $selectable = 0; # can we schedule a new appointment
 %   my $ticketid = 0;
 %   my $draggable_length = 0;
@@ -42,13 +42,20 @@
 %   }
 %
 %   #block out / show / color code existing appointments
+%   my $maxstarts = 0;
 %   foreach my $id ( keys %{ $schedule{'scheduled'} } ) {
 %
 %     my( $starts, $due, $col, $t ) = @{ $schedule{'scheduled'}->{$id} };
 %
-%     # misleading loop--at most one id will pass this test
+%     # misleading loop--at most one id should pass this test
 %     next if $starts >= ($tod_row+$timestep) || $due <= $tod_row;
 %
+%     # but, if for any reason a scheduling conflict occurs, 
+%     # use the later starting one to minimize UI conflicts--
+%     #   not to imply that this scenario has been tested or should ever happen!!!
+%     next if $starts < $maxstarts;
+%     $maxstarts = $starts;
+%
 %     $ticketid = $id;
 %     $bgcolor = '#'.$col;
 %     $border = '1px solid #D7D7D7';
@@ -60,22 +67,18 @@
 %     $cells = int( ($due-$starts) / $timestep );
 %     $cells++ if ($due-$starts) % $timestep;
 %
-%     if ( $starts >= $tod_row ) { #first row
-%
-%       #false laziness w/misc/xmlhttp-ticket-update.html & CalendarDaySchedule
-%       my %hash = $m->comp('/Ticket/Elements/Customers', Ticket => $t);
-%       my @cust_main = values( %{$hash{cust_main}} );
+%     #false laziness w/misc/xmlhttp-ticket-update.html & CalendarDaySchedule
+%     my %hash = $m->comp('/Ticket/Elements/Customers', Ticket => $t);
+%     my @cust_main = values( %{$hash{cust_main}} );
 %
-%       #false laziness w/xmlhttp-ticket-update.html
-%       $content .= FS::sched_avail::pretty_time($starts). '-'.
-%                   FS::sched_avail::pretty_time($due).
-%                   ': '. $cust_main[0]->_FreesideURILabel;
-%                   #'install for custname XX miles away'; #XXX placeholder/more
+%     #false laziness w/xmlhttp-ticket-update.html
+%     $label .= FS::sched_avail::pretty_time($starts). '-'.
+%               FS::sched_avail::pretty_time($due).
+%               ': '. $cust_main[0]->_FreesideURILabel;
+%               #'install for custname XX miles away'; #XXX placeholder/more
 %
-%     } else {
-%       $offset = int( ($tod_row - $starts) / $timestep );
-%       $offset++ if ($tod_row - $starts) % $timestep;
-%     }
+%     $offset = int( ($tod_row - $starts) / $timestep );
+%     $offset++ if ($tod_row - $starts) % $timestep;
 %   }
 %
 %   my $td_id = 'td_'. $Date->epoch. '_'. $tod_row. '_'. $username;
@@ -137,7 +140,7 @@
 %       }
 %
 %     }
-    ><% $content |h %></td>
+    ></td>
     <SCRIPT TYPE="text/javascript">
 
       var $cell_<% $td_id %> = $('#<% $td_id %>');
@@ -155,7 +158,7 @@
         $cell_<% $td_id %>,
         <% $ticketid |js_string %>,
         <% $bgcolor |n,js_string %>,
-        <% $content |n,js_string %>,
+        <% $label |n,js_string %>,
         <% $draggable_length * 60 %>,
         <% $cells %>,
         <% $offset %>
diff --git a/rt/share/html/Search/Schedule.html b/rt/share/html/Search/Schedule.html
index b16f609..cfa4558 100644
--- a/rt/share/html/Search/Schedule.html
+++ b/rt/share/html/Search/Schedule.html
@@ -24,8 +24,6 @@
   // sets cell data and appearance as an appointment
   function set_appointment_cell ($cell,ticketid,bgcolor,label,length,cells,offset) {
     $cell.data('bgcolor',  bgcolor );
-    $cell.css('background-color', bgcolor);
-    $cell.css('border',  '1px solid #D7D7D7' );
     $cell.data('ticketid', ticketid );
     $cell.data('length',   length );
     $cell.data('cells',    cells );

commit eb985ed029f86ad4056c78ae95417f3402359d62
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Mar 30 07:41:51 2016 -0500

    RT#34237: installer scheduling [major refactor, many bugs fixed]

diff --git a/rt/share/html/Elements/CalendarSlotSchedule b/rt/share/html/Elements/CalendarSlotSchedule
index b82997b..c8d3bfa 100644
--- a/rt/share/html/Elements/CalendarSlotSchedule
+++ b/rt/share/html/Elements/CalendarSlotSchedule
@@ -11,6 +11,7 @@
   $pkgnum    => undef
   $RedirectToBasics => 0
 </%ARGS>
+% my $scheduling = ($custnum && $LengthMin) ? 1 : 0;
 % foreach my $username ( @username ) {
 %
 %   my %schedule = UserDaySchedule( username => $username,
@@ -18,20 +19,22 @@
 %                                   Tickets  => \@Tickets,
 %                                 );
 %
-%   my $bgcolor = '666666;border-color:#555555';
+%   my $bgcolor = '#666666';
+%   my $border = '1px solid #555555';
 %   my $content = '';
-%   my $link = '';
-%   my $selectable = 0;
-%   my $draggable_ticketid = 0;
+%   my $selectable = 0; # can we schedule a new appointment
+%   my $ticketid = 0;
 %   my $draggable_length = 0;
-%   my $droppable = 0;
-%   my $cells = 0;
+%   my $droppable = 0; # can we reschedule an appointment here
+%   my $cells = 0; # total cell count for appointment
+%   my $offset = 0; # position of cell in appointment
 %
 %   #white out available times
 %   foreach my $avail ( @{ $schedule{'avail'} } ) {
 %     my( $start, $end ) = @$avail;
 %     next if $start >= ($tod_row+$timestep) || $end <= $tod_row;
-%     $bgcolor = 'FFFFFF';
+%     $bgcolor = '#FFFFFF';
+%     $border = '1px solid #D7D7D7';
 %     $selectable = 1
 %       if $LengthMin <= $end - $tod_row  #the slot is long enough
 %       && ! grep { $_ > $tod_row && $LengthMin > $_ - $tod_row }
@@ -39,15 +42,23 @@
 %   }
 %
 %   #block out / show / color code existing appointments
-%   #my %line = ();
 %   foreach my $id ( keys %{ $schedule{'scheduled'} } ) {
 %
 %     my( $starts, $due, $col, $t ) = @{ $schedule{'scheduled'}->{$id} };
 %
+%     # misleading loop--at most one id will pass this test
 %     next if $starts >= ($tod_row+$timestep) || $due <= $tod_row;
 %
-%     $bgcolor = $col;
+%     $ticketid = $id;
+%     $bgcolor = '#'.$col;
+%     $border = '1px solid #D7D7D7';
+%     # can't schedule a new appointment
 %     $selectable = 0;
+%     # but can reschedule a ticket overlapping its old slot (filtered by can_drop)
+%     $droppable = 1 unless $scheduling;
+%     $draggable_length = $due - $starts;
+%     $cells = int( ($due-$starts) / $timestep );
+%     $cells++ if ($due-$starts) % $timestep;
 %
 %     if ( $starts >= $tod_row ) { #first row
 %
@@ -55,35 +66,23 @@
 %       my %hash = $m->comp('/Ticket/Elements/Customers', Ticket => $t);
 %       my @cust_main = values( %{$hash{cust_main}} );
 %
-%       $content .= ($content?', ':''). #$id. ': '.
-%                   #false laziness w/xmlhttp-ticket-update.html
-%                   FS::sched_avail::pretty_time($starts). '-'.
+%       #false laziness w/xmlhttp-ticket-update.html
+%       $content .= FS::sched_avail::pretty_time($starts). '-'.
 %                   FS::sched_avail::pretty_time($due).
 %                   ': '. $cust_main[0]->_FreesideURILabel;
 %                   #'install for custname XX miles away'; #XXX placeholder/more
-%       $link = qq( <A HREF="$RT::WebPath/Ticket/Display.html?id=$id" target="_blank">view</A> ).
-%               include('/elements/popup_link.html',
-%                         action=>$RT::WebPath.'/Ticket/ModifyCustomFieldsPopup.html?id='.$id,
-%                         label =>'edit',
-%                         actionlabel => 'Edit appointment',
-%                         height      => 436, # better: A + B * (num_custom_fields)
-%                      );
-%       $draggable_ticketid = $id;
-%       $draggable_length = $due - $starts;
-%
-%       $cells = int( ($due-$starts) / $timestep );
-%       $cells++ if ($due-$starts) % $timestep;
-%       
-%     #} else {
-%     #  $content .= ($content?', ':''). $id;
+%
+%     } else {
+%       $offset = int( ($tod_row - $starts) / $timestep );
+%       $offset++ if ($tod_row - $starts) % $timestep;
 %     }
 %   }
 %
 %   my $td_id = 'td_'. $Date->epoch. '_'. $tod_row. '_'. $username;
 
-    <td style = "background-color:#<%$bgcolor%>"
+    <td style = "background-color: <% $bgcolor %>; border: <% $border %>"
           ID="<% $td_id %>"
-        class = "<% ($selectable && $custnum && $LengthMin) ? 'weeklyselectable' : 'weekly' %>"
+        class = "<% ($selectable && $scheduling) ? 'weeklyselectable' : 'weekly' %>"
 %#                 <%   $is_today     ? 'today'
 %#                    : $is_yesterday ? 'yesterday'
 %#                    : $is_aweekago  ? 'aweekago'
@@ -91,7 +90,8 @@
 %#                 %>"
 %     if ( $selectable ) {
 %
-%       if ( $custnum && $LengthMin ) {
+%       # Scheduling a new appointment
+%       if ( $scheduling ) {
 %
 %         #XXX for now, construct a ticket creation URL
 %         # eventually, do much the same, but say "appointment made", show time
@@ -131,39 +131,41 @@
                         %>"
           onclick     = "window.location.href = '<% $url %>'"
 %
+%       # If not scheduling, allow drag-and-drop rescheduling
 %       } else {
 %         $droppable = 1;
 %       }
 %
 %     }
-    ><% $content |h %><% $link |n %></td>
+    ><% $content |h %></td>
     <SCRIPT TYPE="text/javascript">
 
-      $('#<% $td_id %>').data('username', "<% $username %>");
-      $('#<% $td_id %>').data('starts',   <% $Date->epoch + $tod_row*60 %>);
-      $('#<% $td_id %>').data('epoch',    <% $Date->epoch %>);
-      $('#<% $td_id %>').data('tod_row',  <% $tod_row %>);
+      var $cell_<% $td_id %> = $('#<% $td_id %>');
+      $cell_<% $td_id %>.data('username', "<% $username %>");
+      $cell_<% $td_id %>.data('starts',   <% $Date->epoch + $tod_row*60 %>);
+      $cell_<% $td_id %>.data('epoch',    <% $Date->epoch %>);
+      $cell_<% $td_id %>.data('tod_row',  <% $tod_row %>);
 
-%     if ( $droppable ) {
-        $('#<% $td_id %>').droppable({
-          over: boxon_drop,
-          drop: reschedule_appointment,
-          tolerance: 'pointer'
-        });
+%     if ($selectable) {
+      set_schedulable_cell($cell_<% $td_id %>);
 %     }
 
-%     if ( $draggable_ticketid ) {
-        $('#<% $td_id %>').draggable({
-          containment: '.titlebox-content',
-%#          revert:      'invalid',
-          revert: true,
-          revertDuration: 0,
-          stop: clear_drag_hi,
-        });
-        $('#<% $td_id %>').data('ticketid', <% $draggable_ticketid %>);
-        $('#<% $td_id %>').data('length',   <% $draggable_length * 60 %>);
-        $('#<% $td_id %>').data('cells',    <% $cells %>);
-        $('#<% $td_id %>').data('bgcolor',  "#<% $bgcolor %>");
+%     if ($ticketid) {
+      set_appointment_cell(
+        $cell_<% $td_id %>,
+        <% $ticketid |js_string %>,
+        <% $bgcolor |n,js_string %>,
+        <% $content |n,js_string %>,
+        <% $draggable_length * 60 %>,
+        <% $cells %>,
+        <% $offset %>
+      );
+%     }
+%     if ( $droppable ) {
+%       if ( $draggable_length ) {
+      set_draggable_cell($cell_<% $td_id %>);
+%       }
+      set_droppable_cell($cell_<% $td_id %>);
 %     }
 
     </SCRIPT>
diff --git a/rt/share/html/Search/Schedule.html b/rt/share/html/Search/Schedule.html
index 0dbe8c3..b16f609 100644
--- a/rt/share/html/Search/Schedule.html
+++ b/rt/share/html/Search/Schedule.html
@@ -2,8 +2,57 @@
 
 <SCRIPT TYPE="text/javascript">
 
+  // gives cell the appearance dictated by its data
+  function set_data_cell ($cell) {
+    $cell.css('border',  '1px solid #D7D7D7' );
+    $cell.css('background-color', $cell.data('bgcolor'));
+    $cell.html($cell.data('content'));
+  }
+
+  // sets cell data and appearance to schedulable
+  function set_schedulable_cell ($cell) {
+    $cell.data('bgcolor',  '#FFFFFF' );
+    $cell.data('ticketid', 0 );
+    $cell.data('length',   0 );
+    $cell.data('cells',    0 );
+    $cell.data('offset',   0 );
+    $cell.data('label',    '' );
+    $cell.data('content',  '' );
+    set_data_cell($cell);
+  }
+
+  // sets cell data and appearance as an appointment
+  function set_appointment_cell ($cell,ticketid,bgcolor,label,length,cells,offset) {
+    $cell.data('bgcolor',  bgcolor );
+    $cell.css('background-color', bgcolor);
+    $cell.css('border',  '1px solid #D7D7D7' );
+    $cell.data('ticketid', ticketid );
+    $cell.data('length',   length );
+    $cell.data('cells',    cells );
+    $cell.data('offset',   offset );
+    $cell.data('label',  label );
+    $cell.data('content', '');
+    if ( offset == 0 ) { // first row
+      var title = 
+        label +
+        ' <A HREF="<%$RT::WebPath%>/Ticket/Display.html?id=' + ticketid + '" target="_blank">view</A> ' +
+        <% include('/elements/popup_link.html',
+             action=>$RT::WebPath.'/Ticket/ModifyCustomFieldsPopup.html?id=__MAGIC_TICKET_ID__',
+             label =>'edit',
+             actionlabel => 'Edit appointment',
+             height      => 436, # better: A + B * (num_custom_fields)
+          ) |n,js_string
+        %>;
+      title = title.replace( /__MAGIC_TICKET_ID__/, ticketid );
+      $cell.data('content', title);
+    }
+    set_data_cell($cell);
+  }
+
 % if ( $cells ) {
 
+  // hover effects for scheduling new appointment
+
   function boxon(what) {
     var $this = $(what);
     for ( var c=0; c < <%$cells%>; c++) {
@@ -11,16 +60,12 @@
       $this.css('background-color', '#ffffdd');
       if ( c == 0 ) {
         $this.css('border-top', '1px double black');
-        $this.css('border-left', '1px double black');
-        $this.css('border-right', '1px solid black');
-      } else if ( c == <%$cells-1%> ) {
-        $this.css('border-left', '1px double black');
-        $this.css('border-right', '1px solid black');
+      }
+      if ( c == <%$cells-1%> ) {
         $this.css('border-bottom', '1px solid black');
-      } else {
-        $this.css('border-left', '1px double black');
-        $this.css('border-right', '1px solid black');
       }
+      $this.css('border-left', '1px double black');
+      $this.css('border-right', '1px solid black');
 
       var rownum = $this.parent().prevAll('tr').length;
       var colnum = $this.prevAll('td').length;
@@ -31,12 +76,8 @@
   function boxoff(what) {
     var $this = $(what);
     for ( var c=0; c < <%$cells%>; c++) {
-
-      //$this.css('background-color', '');
-      //$this.css('border', ''); //IE8 woes, removes cell borders
-      $this.removeAttr('style'); //slightly "flashy" on cell changes under IE8
-                                 //but at least it doesn't remove cell borders
-
+      $this.css('background-color', '#ffffff');
+      $this.css('border', '1px solid #D7D7D7'); //watch out in IE8 woes, empty string removes cell borders
       var rownum = $this.parent().prevAll('tr').length;
       var colnum = $this.prevAll('td').length;
       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
@@ -44,14 +85,22 @@
   }
 
 
-% }
+% } else {
+
+  // functions for drag-and-drop rescheduling
 
-% # it would be better if we had draggable-specific droppables, but this will prevent overlap for now...
-  function can_drop ($where, cells) {
+  // ticket-dependant test if we can drop here
+  // prevent overlap with other appointments, while allowing appointment to overlap itself
+  function can_drop ($where, ui) {
+    var cells = ui.draggable.data('cells');
+    var ticketid = ui.draggable.data('ticketid');
     for (var c=0; c < cells; c++) {
       if (!$where.is('.ui-droppable')) {
         return false;
       }
+      if ($where.data('ticketid') && ($where.data('ticketid') != ticketid)) {
+        return false;
+      }
       var rownum = $where.parent().prevAll('tr').length;
       var colnum = $where.prevAll('td').length;
       $where = $where.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
@@ -59,71 +108,119 @@
     return true;
   }
 
-  var drag_cells = 0;
+  // makes cell droppable (can reschedule here, subject to can_drop)
+  function set_droppable_cell ($cell) {
+    $cell.droppable({
+      over: appointment_drag_over,
+      drop: reschedule_appointment,
+      tolerance: 'pointer'
+    });
+  }
+
+  // makes cell draggable (able to be rescheduled)
+  function set_draggable_cell ($cell) {
+    $cell.draggable({
+      containment: '.titlebox-content',
+      revert: true,
+      revertDuration: 0,
+      start: appointment_drag_start,
+      stop: appointment_drag_stop,
+    });
+  }
+
+  // gives cell a white (schedulable) appearance, without changing cell data
+  function set_white_cell ($cell) {
+    $cell.css('border',  '1px solid #D7D7D7' );
+    $cell.css('background-color', '#FFFFFF');
+    $cell.html('');
+  }
+
+  // track drag highlighting
   var drag_hi;
 
-  // on drag stop (regardless of if it was dropped)
-  function clear_drag_hi () {
+  // clear drag highlighting
+  function clear_drag_hi (cells) {
     if ( drag_hi ) {
-      boxoff_do(drag_hi);
+      for ( var c=0; c < cells; c++) {
+        if (drag_hi.data('isdragging')) {
+          drag_hi.css('border',  '1px solid #D7D7D7' );
+        } else {
+          set_white_cell(drag_hi);
+        }
+        var rownum = drag_hi.parent().prevAll('tr').length;
+        var colnum = drag_hi.prevAll('td').length;
+        drag_hi = drag_hi.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
+      }
       drag_hi = undefined;
     }
   }
 
-  // on drag over
-  function boxon_drop(event, ui) {
-    //var $this = $(what);
+  // drag start event
+  function appointment_drag_start(event, ui) {
     var $this = $(this);
-
-    drag_cells = ui.draggable.data('cells');
-
-    clear_drag_hi();
-
-    if (!can_drop($this, drag_cells)) return;
-
-    drag_hi = $this;
-
-    for ( var c=0; c < drag_cells; c++) {
-
-      /* well, its not exactly what i want, would prefer if it could properly
-         mouse in-out, but this sorta helps for now?
-         revisit when everthing else is working */
-/*      $this.effect("highlight", {}, 1500); */
-
-      $this.css('background-color', '#ffffdd');
-      if ( c == 0 ) {
-        $this.css('border-top', '1px double black');
-        $this.css('border-left', '1px double black');
-        $this.css('border-right', '1px solid black');
-      } else if ( c == (drag_cells-1) ) {
-        $this.css('border-left', '1px double black');
-        $this.css('border-right', '1px solid black');
-        $this.css('border-bottom', '1px solid black');
-      } else {
-        $this.css('border-left', '1px double black');
-        $this.css('border-right', '1px solid black');
-      }
-
+    // cell that's actually dragging
+    $this.html($this.data('label'));
+    $this.css('z-index',10);
+    $this.data('isdragging',true);
+    var offset = $this.data('offset');
+    var cells  = $this.data('cells');
+    // jump to first cell in appointment
+    var rownum = $this.parent().prevAll('tr').length;
+    var colnum = $this.prevAll('td').length;
+    $this = $this.parent().parent().children('tr').eq(rownum-offset).children('td').eq(colnum);
+    // loop through all cells in appointment
+    for ( var c=0; c < cells; c++) {
+      if (c != offset) set_white_cell($this);
       var rownum = $this.parent().prevAll('tr').length;
       var colnum = $this.prevAll('td').length;
       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
     }
-
-
   }
 
-  // clears highlighted box, used by clear_hi_drag (drag stop event)
-  function boxoff_do(what) {
-
-    var $this = what;
-
-    for ( var c=0; c < drag_cells; c++) {
-
-      //$this.css('background-color', '');
-      //$this.css('border', ''); //IE8 woes, removes cell borders
-      $this.removeAttr('style'); //slightly "flashy" on cell changes under IE8
-                                 //but at least it doesn't remove cell borders
+  // drag stop event
+  function appointment_drag_stop(event, ui) {
+    var $this = $(this);
+    // the cell that was dragging
+    var cells = $this.data('cells');
+    clear_drag_hi(cells);
+    $this.css('z-index','initial');
+    $this.data('isdragging',false);
+    var offset = $this.data('offset');
+    // jump to first cell in appointment
+    var rownum = $this.parent().prevAll('tr').length;
+    var colnum = $this.prevAll('td').length;
+    $this = $this.parent().parent().children('tr').eq(rownum-offset).children('td').eq(colnum);
+    // loop through all cells in appointment
+    for ( var c=0; c < cells; c++) {
+      set_data_cell($this);
+      var rownum = $this.parent().prevAll('tr').length;
+      var colnum = $this.prevAll('td').length;
+      $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
+    }
+  }
 
+  // drag over event
+  function appointment_drag_over(event, ui) {
+    // the cell that is dragging
+    var cells = ui.draggable.data('cells');
+	// the droppable cell that you're over
+    var $this = $(this);
+    clear_drag_hi(cells);
+    if (!can_drop($this, ui)) return;
+    drag_hi = $this;
+    // loop through potential appointment cells
+    for ( var c=0; c < cells; c++) {
+      if ( !$this.data('isdragging')) {
+        $this.css('background-color', '#ffffdd');
+      }
+      if ( c == 0 ) {
+        $this.css('border-top', '1px double black');
+      }
+      if ( c == (cells-1) ) {
+        $this.css('border-bottom', '1px solid black');
+      }
+      $this.css('border-left', '1px double black');
+      $this.css('border-right', '1px solid black');
       var rownum = $this.parent().prevAll('tr').length;
       var colnum = $this.prevAll('td').length;
       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
@@ -135,23 +232,22 @@
 
     var $this = $(this);
 
-    if (!can_drop($this, ui.draggable.data('cells'))) return;
+    if (!can_drop($this, ui)) return;
 
 %   #get the ticket number and appointment length (from the draggable object)
-    var ticketid = ui.draggable.data('ticketid');
-    var length   = ui.draggable.data('length');
-    var bgcolor  = ui.draggable.data('bgcolor');
+    var draggable = ui.draggable;
+    var ticketid = draggable.data('ticketid');
+    var length   = draggable.data('length');
+    var bgcolor  = draggable.data('bgcolor');
+    var offset   = draggable.data('offset');
 
 %   #and.. the new date and time, and username (from the droppable object)
     var starts   = $this.data('starts');
     var username = $this.data('username');
-
     var due = parseInt(starts) + parseInt(length);
-
     var n_epoch        = $this.data('epoch');
     var n_st_tod_row   = $this.data('tod_row');
 
-    var draggable = ui.draggable;
     var droppable = $this;
     draggable.effect( "transfer", { to: droppable }, 420 );
 
@@ -164,78 +260,50 @@
       if ( data.error && data.error.length ) {
 %       #error?  "that shouldn't happen" but should display 
         alert(data.error);
-%       #XX and should revert the dragable...
-      } else {
 
-        //draggable.effect( "transfer", { to: droppable }, 1000 );
+      } else {
 
         var label = data.sched_label;
 
-%       #remove the old appointment entirely
-        var epoch        = ui.draggable.data('epoch');
-        var st_tod_row   = ui.draggable.data('tod_row');
-        var old_username = ui.draggable.data('username');
-        var cells        = ui.draggable.data('cells');
+        // jump to first cell in appointment
+        var rownum = draggable.parent().prevAll('tr').length;
+        var colnum = draggable.prevAll('td').length;
+        draggable = draggable.parent().parent().children('tr').eq(rownum-offset).children('td').eq(colnum);
+
+        // remove old appointment entirely
+        var epoch        = draggable.data('epoch');
+        var st_tod_row   = draggable.data('tod_row');
+        var old_username = draggable.data('username');
+        var cells        = draggable.data('cells');
         for ( var c=0; c < cells; c++) {
           var tod_row = parseInt(st_tod_row) + (c * <%$timestep%>);
           var td_id = 'td_' + epoch +
                       '_' + String( tod_row ) +
                       '_' + old_username;
-          $('#'+td_id).css('background-color', '#FFFFFF');
-          $('#'+td_id).text('');
-%         #(and make those boxes droppable)
-          $('#'+td_id).droppable({
-            over: boxon_drop,
-            drop: reschedule_appointment,
-            tolerance: 'pointer'
-          });
+          var $cell = $('#'+td_id);
+          set_schedulable_cell($cell);
+          $cell.draggable('destroy');
+          set_droppable_cell($cell);
         }
 
-%       #maybe use that animation which shows the box from point A to B
-
-        clear_drag_hi();
+        // set appointment in new position
+        clear_drag_hi(cells);
         for ( var d=0; d < cells; d++) {
           var n_tod_row = parseInt(n_st_tod_row) + (d * <%$timestep%>);
           var n_td_id = 'td_' + n_epoch +
                         '_' + String( n_tod_row ) +
                         '_' + username;
-          $('#'+n_td_id).css('background-color', bgcolor);
-%         #remove their droppable
-          $('#'+n_td_id).droppable('destroy');
-          if ( d == 0 ) {
-            var title = 
-              label +
-              ' <A HREF="<%$RT::WebPath%>/Ticket/Display.html?id=' + ticketid + '" target="_blank">view</A> ' +
-              <% include('/elements/popup_link.html',
-                   action=>$RT::WebPath.'/Ticket/ModifyCustomFieldsPopup.html?id=__MAGIC_TICKET_ID__',
-                   label =>'edit',
-                   actionlabel => 'Edit appointment',
-                   height      => 436, # better: A + B * (num_custom_fields)
-                 ) |n,js_string
-              %>;
-            title = title.replace( /__MAGIC_TICKET_ID__/, ticketid );
-            $('#'+n_td_id).html( title );
-%           #(and make the top draggable, so we could do it all over again)
-            $('#'+n_td_id).draggable({
-              containment: '.titlebox-content',
-%#              revert:      'invalid',
-              revert: true,
-              revertDuration: 0,
-              stop: clear_drag_hi,
-            });
-            $('#'+n_td_id).data('ticketid', ticketid );
-            $('#'+n_td_id).data('length',   length );
-            $('#'+n_td_id).data('cells',    cells );
-            $('#'+n_td_id).data('bgcolor',  bgcolor );
-          }
+          var $cell = $('#'+n_td_id);
+          set_appointment_cell($cell,ticketid,bgcolor,label,length,cells,d);
+          set_draggable_cell($cell);
+          set_droppable_cell($cell);
         }
-
       }
-
     });
-
   }
 
+% } # end of rescheduling functions
+
 </SCRIPT>
 
 <& /Search/Calendar.html,

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

Summary of changes:
 httemplate/misc/xmlhttp-ticket-update.html  |    6 +-
 rt/share/html/Elements/CalendarSlotSchedule |  135 ++++++-----
 rt/share/html/Search/Schedule.html          |  347 +++++++++++++++++----------
 3 files changed, 295 insertions(+), 193 deletions(-)




More information about the freeside-commits mailing list