Added editable comment fields on week view.
authoranuko <support@anuko.com>
Fri, 12 Jan 2018 16:55:03 +0000 (16:55 +0000)
committeranuko <support@anuko.com>
Fri, 12 Jan 2018 16:55:03 +0000 (16:55 +0000)
WEB-INF/lib/ttWeekViewHelper.class.php
WEB-INF/templates/footer.tpl
WEB-INF/templates/week.tpl
week.php

index 5c03a37..4a8ce4e 100644 (file)
@@ -85,7 +85,7 @@ class ttWeekViewHelper {
     return $result;
   }
 
-  // getDataForWeekView - builds an array to render a table of durations for week view.
+  // getDataForWeekView - builds an array to render a table of durations and comments for week view.
   // In a week view we want one row representing the same attributes to have 7 values for each day of week.
   // We identify simlar records by a combination of client, billable, project, task, and custom field values.
   // This will allow us to extend the feature when more custom fields are added.
@@ -96,10 +96,14 @@ class ttWeekViewHelper {
   // "cl:546,bl:0,pr:23456,ts:27464,cf_1:7623"
   // The above means client 546, not billable, project 23456, task 27464, custom field option id 7623.
   //
+  // Daily comments are implemented as alternate rows following week durations.
+  // For example: row_0 - new entry durations, row_1 - new entry daily comments,
+  //              row_2 - existing entry durations, row_3 - existing entry comments, etc.
+  //
   // Description of $dataArray format that the function returns.
   // $dataArray = array(
   //   array( // Row 0. This is a special, one-off row for a new week entry with empty values.
-  //     'row_id' => null', // Row identifier. Null for a new entry.
+  //     'row_id' => null, // Row identifier. Null for a new entry.
   //     'label' => 'New entry', // Human readable label for the row describing what this time entry is for.
   //     'day_0' => array('control_id' => '0_day_0', 'tt_log_id' => null, 'duration' => null), // control_id is row_id plus day header for column.
   //     'day_1' => array('control_id' => '0_day_1', 'tt_log_id' => null, 'duration' => null),
@@ -109,6 +113,21 @@ class ttWeekViewHelper {
   //     'day_5' => array('control_id' => '0_day_5', 'tt_log_id' => null, 'duration' => null),
   //     'day_6' => array('control_id' => '0_day_6', 'tt_log_id' => null, 'duration' => null)
   //   ),
+  //
+  //   TODO: work in progress re-documenting the array for improved week view. Trying to implement this now.
+  //   array( // Row 1. This row represents daily comments for a new entry in row above (row 0).
+  //     'row_id' => null, // Row identifier. See ttWeekViewHelper::makeRowIdentifier().
+  //     'label' => 'Notes', // Human readable label.
+  //     'day_0' => array('control_id' => '1_day_0', 'tt_log_id' => null, 'note' => null),
+  //     'day_1' => array('control_id' => '1_day_1', 'tt_log_id' => null, 'note' => null),
+  //     'day_2' => array('control_id' => '1_day_2', 'tt_log_id' => null, 'note' => null),
+  //     'day_3' => array('control_id' => '1_day_3', 'tt_log_id' => null, 'note' => null),
+  //     'day_4' => array('control_id' => '1_day_4', 'tt_log_id' => null, 'note' => null),
+  //     'day_5' => array('control_id' => '1_day_5', 'tt_log_id' => null, 'note' => null),
+  //     'day_6' => array('control_id' => '1_day_6', 'tt_log_id' => null, 'note' => null)
+  //   ),
+  //   TODO: work in progress...
+  //
   //   array( // Row 1.
   //     'row_id' => 'cl:546,bl:1,pr:23456,ts:27464,cf_1:7623_0', // Row identifier. See ttWeekViewHelper::makeRowIdentifier().
   //     'label' => 'Anuko - Time Tracker - Coding',              // Human readable label for the row describing what this time entry is for.
@@ -138,16 +157,23 @@ class ttWeekViewHelper {
     $dataArray = array();
 
     // Construct the first row for a brand new entry.
-    $dataArray[] = array('row_id' => null,'label' => $i18n->getKey('form.week.new_entry')); // Insert row.
+    $dataArray[] = array('row_id' => null,'label' => $i18n->getKey('form.week.new_entry').':'); // Insert row.
     // Insert empty cells with proper control ids.
     for ($i = 0; $i < 7; $i++) {
       $control_id = '0_'. $dayHeaders[$i];
       $dataArray[0][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null);
     }
+    // Construct the second row for daily comments for a brand new entry.
+    $dataArray[] = array('row_id' => null,'label' => $i18n->getKey('label.notes').':'); // Insert row.
+    // Insert empty cells with proper control ids.
+    for ($i = 0; $i < 7; $i++) {
+      $control_id = '1_'. $dayHeaders[$i];
+      $dataArray[1][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null);
+    }
 
     // Iterate through records and build $dataArray cell by cell.
     foreach ($records as $record) {
-      // Create record id without suffix.
+      // Create row id without suffix.
       $row_id_no_suffix = ttWeekViewHelper::makeRowIdentifier($record);
       // Handle potential multiple records with the same attributes by using a numerical suffix.
       $suffix = 0;
@@ -160,16 +186,29 @@ class ttWeekViewHelper {
       // Find row.
       $pos = ttWeekViewHelper::findRow($row_id, $dataArray);
       if ($pos < 0) {
-        $dataArray[] = array('row_id' => $row_id,'label' => ttWeekViewHelper::makeRowLabel($record)); // Insert row.
+        // Insert row for durations.
+        $dataArray[] = array('row_id' => $row_id,'label' => ttWeekViewHelper::makeRowLabel($record));
         $pos = ttWeekViewHelper::findRow($row_id, $dataArray);
         // Insert empty cells with proper control ids.
         for ($i = 0; $i < 7; $i++) {
           $control_id = $pos.'_'. $dayHeaders[$i];
           $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'duration' => null);
         }
+        // Insert row for comments.
+        $dataArray[] = array('row_id' => $row_id.'_notes','label' => $i18n->getKey('label.notes').':');
+        $pos++;
+        // Insert empty cells with proper control ids.
+        for ($i = 0; $i < 7; $i++) {
+          $control_id = $pos.'_'. $dayHeaders[$i];
+          $dataArray[$pos][$dayHeaders[$i]] = array('control_id' => $control_id, 'tt_log_id' => null,'note' => null);
+        }
+        $pos--;
       }
       // Insert actual cell data from $record (one cell only).
       $dataArray[$pos][$day_header] = array('control_id' => $pos.'_'. $day_header, 'tt_log_id' => $record['id'],'duration' => $record['duration']);
+      // Insert existing comment from $record into the duration cell.
+      $pos++;
+      $dataArray[$pos][$day_header] = array('control_id' => $pos.'_'. $day_header, 'tt_log_id' => $record['id'],'note' => $record['comment']);
     }
     return $dataArray;
   }
@@ -201,7 +240,7 @@ class ttWeekViewHelper {
 
     // Insert label.
     global $i18n;
-    $dayTotals['label'] = $i18n->getKey('label.day_total');
+    $dayTotals['label'] = $i18n->getKey('label.day_total').':';
 
     foreach ($dataArray as $row) {
       foreach($dayHeaders as $dayHeader) {
@@ -400,12 +439,8 @@ class ttWeekViewHelper {
     global $user;
 
     // Possible errors: 1) Overlap if the existing record has start time. 2) Going beyond 24 hour boundary.
-    // TODO: rename this function.
-    // Handle different errors with specific error messages.
-    if (!ttWeekViewHelper::canModify($fields['tt_log_id'], $fields['duration'], $err)) {
-      // $err->add($i18n->getKey('error.overlap'));
+    if (!ttWeekViewHelper::canModify($fields['tt_log_id'], $fields['duration'], $err))
       return false;
-    }
 
     $mdb2 = getConnection();
     $duration = $fields['duration'];
@@ -468,4 +503,20 @@ class ttWeekViewHelper {
 
     return true; // There are no conflicts, safe to modify.
   }
+
+  // modifyCommentFromWeekView - modifies a comment in an existing record from a week view post.
+  static function modifyCommentFromWeekView($fields) {
+    global $user;
+
+    $mdb2 = getConnection();
+    $tt_log_id = $fields['tt_log_id'];
+    $comment = $fields['comment'];
+    $user_id = $user->getActiveUser();
+    $sql = "update tt_log set comment = ".$mdb2->quote($fields['comment'])." where id = $tt_log_id and user_id = $user_id";
+    $affected = $mdb2->exec($sql);
+    if (is_a($affected, 'PEAR_Error'))
+      return false;
+
+    return true;
+  }
 }
index 5c5d50c..734c158 100644 (file)
@@ -12,7 +12,7 @@
       <br>
       <table cellspacing="0" cellpadding="4" width="100%" border="0">
         <tr>
-          <td align="center">&nbsp;Anuko Time Tracker 1.13.12.3740 | Copyright &copy; <a href="https://www.anuko.com/lp/tt_3.htm" target="_blank">Anuko</a> |
+          <td align="center">&nbsp;Anuko Time Tracker 1.13.13.3741 | Copyright &copy; <a href="https://www.anuko.com/lp/tt_3.htm" target="_blank">Anuko</a> |
             <a href="https://www.anuko.com/lp/tt_4.htm" target="_blank">{$i18n.footer.credits}</a> |
             <a href="https://www.anuko.com/lp/tt_5.htm" target="_blank">{$i18n.footer.license}</a> |
             <a href="https://www.anuko.com/lp/tt_7.htm" target="_blank">{$i18n.footer.improve}</a>
index 42f24f1..f514475 100644 (file)
           <td>{$forms.weekTimeForm.task.control}</td>
         </tr>
 {/if}
-        <tr>
-          <td align="right">{$i18n.label.note}:</td>
-          <td align="left">{$forms.weekTimeForm.note.control}</td>
-        </tr>
       </table>
     </td>
     <td valign="top">
index fb2dcaa..1fb904e 100644 (file)
--- a/week.php
+++ b/week.php
@@ -107,7 +107,6 @@ $cl_project = $request->getParameter('project', ($request->getMethod()=='POST'?
 $_SESSION['project'] = $cl_project;
 $cl_task = $request->getParameter('task', ($request->getMethod()=='POST'? null : @$_SESSION['task']));
 $_SESSION['task'] = $cl_task;
-$cl_note = trim($request->getParameter('note'));
 
 // Get the data we need to display week view.
 // Get column headers, which are day numbers in month.
@@ -127,9 +126,11 @@ class LabelCellRenderer extends DefaultCellRenderer {
     // Special handling for row 0, which represents a new week entry.
     if (0 == $row) {
       $this->setOptions(array('style'=>'text-align: center; font-weight: bold;'));
+    } else if (0 != $row % 2) {
+      $this->setOptions(array('style'=>'text-align: right;'));
     }
     // Special handling for not billable entries.
-    if ($row > 0) {
+    if ($row > 1 && 0 == $row % 2) {
       $row_id = $table->getValueAtName($row,'row_id');
       $billable = ttWeekViewHelper::parseFromWeekViewRow($row_id, 'bl');
       if (!$billable) {
@@ -142,6 +143,7 @@ class LabelCellRenderer extends DefaultCellRenderer {
 }
 
 // Define rendering class for a single cell for time entry in week view table.
+// TODO: Refactor the class name, as we now handle both durations and comments in these cells.
 class TimeCellRenderer extends DefaultCellRenderer {
   function render(&$table, $value, $row, $column, $selected = false) {
     $field_name = $table->getValueAt($row,$column)['control_id']; // Our text field names (and ids) are like x_y (row_column).
@@ -152,7 +154,10 @@ class TimeCellRenderer extends DefaultCellRenderer {
       $field->setEnabled(false);
     $field->setFormName($table->getFormName());
     $field->setStyle('width: 60px;'); // TODO: need to style everything properly, eventually.
-    $field->setValue($table->getValueAt($row,$column)['duration']);
+    if (0 == $row % 2)
+      $field->setValue($table->getValueAt($row,$column)['duration']); // Duration for even rows.
+    else
+      $field->setValue($table->getValueAt($row,$column)['note']);     // Comment for odd rows.
     // Disable control when time entry mode is TYPE_START_FINISH and there is no value in control
     // because we can't supply start and finish times in week view - there are no fields for them.
     global $user;
@@ -259,7 +264,6 @@ if (MODE_PROJECTS_AND_TASKS == $user->tracking_mode) {
     'datakeys'=>array('id','name'),
     'empty'=>array(''=>$i18n->getKey('dropdown.select'))));
 }
-$form->addInput(array('type'=>'textarea','name'=>'note','style'=>'width: 250px; height:'.NOTE_INPUT_HEIGHT.'px;','value'=>$cl_note));
 
 // Add other controls.
 $form->addInput(array('type'=>'calendar','name'=>'date','value'=>$cl_date)); // calendar
@@ -324,58 +328,93 @@ if ($request->isPost()) {
           if ($lockedDays[$key]) continue;
           // Make control id for the cell.
           $control_id = $rowNumber.'_'.$dayHeader;
-          // Optain existing and posted durations.
-          $postedDuration = $request->getParameter($control_id);
-          $existingDuration = $dataArray[$rowNumber][$dayHeader]['duration'];
-          // If posted value is not null, check and normalize it.
-          if ($postedDuration) {
-            if (ttTimeHelper::isValidDuration($postedDuration)) {
-              $postedDuration = ttTimeHelper::normalizeDuration($postedDuration, false); // No leading zero.
-            } else {
-              $err->add($i18n->getKey('error.field'), $i18n->getKey('label.duration'));
-              $result = false; break; // Break out. Stop any further processing.
+
+          // Handle durations and comments in separate blocks of code.
+          if (0 == $rowNumber % 2) {
+            // Handle durations row here.
+
+            // Obtain existing and posted durations.
+            $postedDuration = $request->getParameter($control_id);
+            $existingDuration = $dataArray[$rowNumber][$dayHeader]['duration'];
+            // If posted value is not null, check and normalize it.
+            if ($postedDuration) {
+              if (ttTimeHelper::isValidDuration($postedDuration)) {
+                $postedDuration = ttTimeHelper::normalizeDuration($postedDuration, false); // No leading zero.
+              } else {
+                $err->add($i18n->getKey('error.field'), $i18n->getKey('label.duration'));
+                $result = false; break; // Break out. Stop any further processing.
+              }
             }
-          }
-          // Do not process if value has not changed.
-          if ($postedDuration == $existingDuration)
-            continue;
-          // Posted value is different.
-          if ($existingDuration == null) {
-            // Skip inserting 0 duration values.
-            if (0 == ttTimeHelper::toMinutes($postedDuration))
+            // Do not process if value has not changed.
+            if ($postedDuration == $existingDuration)
               continue;
-            // Insert a new record.
-            $fields = array();
-            $fields['row_id'] = $dataArray[$rowNumber]['row_id'];
-            if (!$fields['row_id']) {
-              // Special handling for row 0, a new entry. Need to construct new row_id.
-              $record = array();
-              $record['client_id'] = $cl_client;
-              $record['billable'] = $cl_billable ? '1' : '0';
-              $record['project_id'] = $cl_project;
-              $record['task_id'] = $cl_task;
-              $record['cf_1_value'] = $cl_cf_1;
-              $fields['row_id'] = ttWeekViewHelper::makeRowIdentifier($record).'_0';
-              // Note: no need to check for a possible conflict with an already existing row
-              // because we are doing an insert that does not affect already existing data.
-
-              $fields['note'] = $cl_note;
+            // Posted value is different.
+            if ($existingDuration == null) {
+              // Skip inserting 0 duration values.
+              if (0 == ttTimeHelper::toMinutes($postedDuration))
+                continue;
+              // Insert a new record.
+              $fields = array();
+              $fields['row_id'] = $dataArray[$rowNumber]['row_id'];
+              if (!$fields['row_id']) {
+                // Special handling for row 0, a new entry. Need to construct new row_id.
+                $record = array();
+                $record['client_id'] = $cl_client;
+                $record['billable'] = $cl_billable ? '1' : '0';
+                $record['project_id'] = $cl_project;
+                $record['task_id'] = $cl_task;
+                $record['cf_1_value'] = $cl_cf_1;
+                $fields['row_id'] = ttWeekViewHelper::makeRowIdentifier($record).'_0';
+                // Note: no need to check for a possible conflict with an already existing row
+                // because we are doing an insert that does not affect already existing data.
+              }
+              $fields['day_header'] = $dayHeader;
+              $fields['start_date'] = $startDate->toString(DB_DATEFORMAT); // To be able to determine date for the entry using $dayHeader.
+              $fields['duration'] = $postedDuration;
+              $fields['browser_today'] = $request->getParameter('browser_today', null);
+              // Take note value from the control below duration.
+              $noteRowNumber = $rowNumber + 1;
+              $note_control_id =  $noteRowNumber.'_'.$dayHeader;
+              $fields['note'] = $request->getParameter($note_control_id);
+              $result = ttWeekViewHelper::insertDurationFromWeekView($fields, $custom_fields, $err);
+            } elseif ($postedDuration == null || 0 == ttTimeHelper::toMinutes($postedDuration)) {
+              // Delete an already existing record here.
+              $result = ttTimeHelper::delete($dataArray[$rowNumber][$dayHeader]['tt_log_id'], $user->getActiveUser());
+            } else {
+              $fields = array();
+              $fields['tt_log_id'] = $dataArray[$rowNumber][$dayHeader]['tt_log_id'];
+              $fields['duration'] = $postedDuration;
+              $result = ttWeekViewHelper::modifyDurationFromWeekView($fields, $err);
             }
-            $fields['day_header'] = $dayHeader;
-            $fields['start_date'] = $startDate->toString(DB_DATEFORMAT); // To be able to determine date for the entry using $dayHeader.
-            $fields['duration'] = $postedDuration;
-            $fields['browser_today'] = $request->getParameter('browser_today', null);
-            $result = ttWeekViewHelper::insertDurationFromWeekView($fields, $custom_fields, $err);
-          } elseif ($postedDuration == null || 0 == ttTimeHelper::toMinutes($postedDuration)) {
-            // Delete an already existing record here.
-            $result = ttTimeHelper::delete($dataArray[$rowNumber][$dayHeader]['tt_log_id'], $user->getActiveUser());
+            if (!$result) break; // Break out of the loop in case of first error.
+
           } else {
+            // Handle commments row here.
+
+            // Obtain existing and posted comments.
+            $postedComment = $request->getParameter($control_id);
+            $existingComment = $dataArray[$rowNumber][$dayHeader]['note'];
+            // If posted value is not null, check it.
+            if ($postedComment && !ttValidString($postedComment, true)) {
+              $err->add($i18n->getKey('error.field'), $i18n->getKey('label.note'));
+              $result = false; break; // Break out. Stop any further processing.
+            }
+            // Do not process if value has not changed.
+            if ($postedComment == $existingComment)
+              continue;
+
+            // Posted value is different.
+            // TODO: handle new entries separately in the durations block above.
+
+            // Here, only update the comment on an already existing record.
             $fields = array();
             $fields['tt_log_id'] = $dataArray[$rowNumber][$dayHeader]['tt_log_id'];
-            $fields['duration'] = $postedDuration;
-            $result = ttWeekViewHelper::modifyDurationFromWeekView($fields, $err);
+            if ($fields['tt_log_id']) {
+              $fields['comment'] = $postedComment;
+              $result = ttWeekViewHelper::modifyCommentFromWeekView($fields);
+            }
+            if (!$result) break; // Break out of the loop in case of first error.
           }
-          if (!$result) break; // Break out of the loop in case of first error.
         }
         if (!$result) break; // Break out of the loop in case of first error.
         $rowNumber++;