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.
// "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),
// '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.
$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;
// 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;
}
// 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) {
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'];
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;
+ }
}
$_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.
// 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) {
}
// 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).
$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;
'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
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++;