Index: tasks.module
===================================================================
--- tasks.module    (revision 1) 
+++ tasks.module    (working copy) 
@@ -20,7 +20,11 @@
 
 // Implementation of hook_perm().
 function tasks_perm() {
-  return array('create task', 'edit own task', 'edit all tasks', 'access tasks main page'); 
+  if(variable_get("tasks_use_timer", 0)>0) { 
+    return array('create task', 'edit own task', 'edit all tasks', 'access tasks main page', 'edit times', 'edit own times', 'add times'); 
+  } else { 
+    return array('create task', 'edit own task', 'edit all tasks', 'access tasks main page'); 
+  } 
 }
 
 // Implementation of hook_access().
@@ -155,6 +159,14 @@
     '#options' => $users
   );
 
+  if(variable_get('tasks_use_timer', 0)) { 
+    $form['time'] = array( 
+      '#type' => 'fieldset',  
+      '#title' => t('Timer') 
+    ); 
+    $form['time']['timer'] = _tasks_timer_form($node); 
+  } 
+ 
   $form['completed'] = array(
     '#type' => 'checkbox',
     '#title' => t('Completed?'),
@@ -211,7 +223,7 @@
 // Implementation of hooks_forms_alter so we don't see preview or delete buttons for tasks
 function tasks_form_alter($form_id, &$form) {
   global $user;
- 
+   
   if ($form_id == 'tasks_node_form') {
     unset($form['preview']);
     //unset($form['delete']);
@@ -240,8 +252,9 @@
     $node->completed_date['month'] = "00";
     $node->completed_date['day'] = "00";
   }
- 
-  db_query("INSERT INTO {tasks} (nid, parent, assigned_to, order_by, completed) VALUES (%d, %d, %d, '%s', '%s')", $node->nid, $node->parent, $node->assigned_to, $node->order_by, $node->completed_date['year'].'-'.$node->completed_date['month'].'-'.$node->completed_date['day']); 
+  $node->start = '0'; 
+  $node->stop = '0'; 
+  db_query("INSERT INTO {tasks} (nid, parent, assigned_to, order_by, completed, start, stop) VALUES (%d, %d, %d, '%s', '%s','%s','%s')", $node->nid, $node->parent, $node->assigned_to, $node->order_by, $node->completed_date['year'].'-'.$node->completed_date['month'].'-'.$node->completed_date['day'],$node->start,$node->stop); 
   _tasks_order_by($node);
 }
 
@@ -255,8 +268,33 @@
     $node->completed_date['month'] = "00";
     $node->completed_date['day'] = "00";
   }
- 
-  db_query("UPDATE {tasks} SET parent = %d, assigned_to = %d, order_by = '%s', completed = '%s' WHERE nid = %d", $node->parent, $node->assigned_to, $node->order_by, $node->completed_date['year'].'-'.$node->completed_date['month'].'-'.$node->completed_date['day'], $node->nid); 
+  if(variable_get("tasks_use_timer", 0) > 0) { 
+    $startNum = $node->timer['tasks_start_num']; 
+    $stopNum = $node->timer['tasks_stop_num']; 
+    $node->start = ''; 
+    $node->stop = ''; 
+    //print_r($node->timer); 
+    if($node->timer[0]['start_date'] != 'Task not started' && $node->timer[0]['stop_date'] != 'Task not started') { 
+      for($i = 0; $i < $startNum; $i++) { 
+        if($node->timer[$i]['delete'] == 0) { 
+          $node->start .= strtotime($node->timer[$i]['start_date']).','; 
+          if($node->timer[$i]['stop_date'] != 'Running...') { 
+            $node->stop .= strtotime($node->timer[$i]['stop_date']).','; 
+          } 
+        } 
+      } 
+      $node->start = rtrim($node->start,','); 
+      $node->stop = rtrim($node->stop,','); 
+      if($startNum - $stopNum == 0 && $node->completed != '0000-00-00') { 
+        $node->stop = $node->stop.','._tasks_hours($node->start,$node->stop,$node->completed); 
+      } 
+    } 
+    if($node->start == '' && $node->stop == '') { 
+      $node->start = '0'; 
+      $node->stop = '0'; 
+    } 
+  } 
+  db_query("UPDATE {tasks} SET parent = %d, assigned_to = %d, order_by = '%s', completed = '%s', start = '%s', stop = '%s' WHERE nid = %d", $node->parent, $node->assigned_to, $node->order_by, $node->completed_date['year'].'-'.$node->completed_date['month'].'-'.$node->completed_date['day'],$node->start,$node->stop,$node->nid); 
   _tasks_order_by($node);
 
   // Redirect to the tasks home page?
@@ -313,7 +351,28 @@
     }
 
     $node = node_prepare($node, $teaser);
- 
+     
+    //Get Task hours 
+    if(variable_get("tasks_use_timer", 0)>0) { 
+      $task = db_fetch_object(db_query("SELECT t.start, t.stop, t.completed FROM {tasks} t WHERE t.nid = %s", $node->nid)); 
+      if($_POST['edit']['tasks_stop_watch'] == 'start') { 
+        if($task->start == 0) { 
+          $task->start = time(); 
+        } else { 
+          $task->start .= ','.time(); 
+        } 
+        db_query("UPDATE {tasks} SET start = '%s' WHERE nid = %d",$task->start,$node->nid); 
+      } else if($_POST['edit']['tasks_stop_watch'] == 'stop') { 
+        if($task->stop == 0) { 
+          $task->stop = time(); 
+        } else { 
+          $task->stop .= ','.time(); 
+        } 
+        db_query("UPDATE {tasks} SET stop = '%s' WHERE nid = %d",$task->stop,$node->nid); 
+      } 
+      $node->body .= _tasks_formated_time($task->start,$task->stop,$task->completed,1); 
+      $node->body .= drupal_get_form('tasks_timer_clocker',_tasks_timer_stopwatch($task->start,$task->stop,$task->completed)); 
+    } 
     $output = '';
 
 
@@ -361,37 +420,78 @@
         //$tasks_colour = drupal_unpack($task)->tasks_colour;
         $extra = drupal_unpack($task);
         $tasks_colour = $extra->tasks_colour;
+        $hours = 0; 
         $task = node_load(array('nid'=>$task->nid));
         $rows[] = array(
           'data' => array(
             array('data' => $assigned_name, 'style' => "width:25px;".($tasks_colour ? "background:$tasks_colour;":'')),
-            array('data' => l($task->title, "node/$task->nid", array('name'=>'task'.$task->nid)), 'style'=>($task->completed>0)?'text-decoration: line-through;':''), 
-            array('data' => ($task->completed>0)?$task->completed:'No'), 
-            array('data' => "<a href='javascript: void(true);' onClick=\"javascript:toggleVisibility('row-$task->nid');\">Expand</a> ".l("<img src=\"".drupal_get_path('module', 'tasks')."/icon_edit.gif\" border=\"0\" />", "node/$task->nid/edit/home",array(),null,null,false,true).' '.l('Up',"node/$node->nid",array(),"action=up&task=$task->nid").' '.l('Down',"node/$node->nid",array(),"action=down&task=$task->nid")), 
+            array('data' => l($task->title, "node/$task->nid", array('name'=>'task'.$task->nid)), 'style'=>($task->completed>0)?'text-decoration: line-through;':'') 
           )
         );
+        // Add new row for hours 
+        if(variable_get("tasks_use_timer", 0)>0) { 
+          $hours = _tasks_hours($task->start,$task->stop,$task->completed); 
+          if($hours > 0) { 
+            $hours = "<a href='javascript: void(true);' onClick=\"javascript:toggleVisibility('row-timer-$task->nid');\">".$hours."</a>"; 
+          } 
+          $rows[count($rows)-1]['data'][] = array('data' => $hours); 
+        } 
+        $rows[count($rows)-1]['data'][] = array('data' => ($task->completed>0)?$task->completed:'No'); 
+        $rows[count($rows)-1]['data'][] = array('data' => "<a href='javascript: void(true);' onClick=\"javascript:toggleVisibility('row-$task->nid');\">Expand</a> ".l("<img src=\"".drupal_get_path('module', 'tasks')."/icon_edit.gif\" border=\"0\" />", "node/$task->nid/edit/home",array(),null,null,false,true).' '.l('Up',"node/$node->nid",array(),"action=up&task=$task->nid").' '.l('Down',"node/$node->nid",array(),"action=down&task=$task->nid")); 
+         
         $task = node_prepare($task);
-        $rows[] = array( 
-          'data' => array( 
-            array('data' => $task->body, 'colspan' => '4'), 
-          ), 
-          'class' => 'task-expand', 'id' => "row-$task->nid", 'style' => 'display:none;' 
-        ); 
+        if(variable_get("tasks_use_timer", 0)>0) { 
+          // change last column span to 5 
+          $rows[] = array( 
+            'data' => array( 
+              array('data' => $task->body, 'colspan' => '5'), 
+            ), 
+            'class' => 'task-expand', 'id' => "row-$task->nid", 'style' => 'display:none;' 
+          ); 
+          // add new row for timer details if necessary 
+          if($hours > 0) { 
+            $rows[] = array( 
+              'data' => array( 
+                array('data' => _tasks_formated_time($task->start,$task->stop,$task->completed), 'colspan' => '5'), 
+              ), 
+              'class' => 'task-expand', 'id' => "row-timer-$task->nid", 'style' => 'display:none;' 
+            ); 
+          } 
+          $header = array(t('Assigned'),t('Task'),t('Hours'),t('Complete?'), t('Edit')); 
+        } else { 
+          $rows[] = array( 
+            'data' => array( 
+              array('data' => $task->body, 'colspan' => '4'), 
+            ), 
+            'class' => 'task-expand', 'id' => "row-$task->nid", 'style' => 'display:none;' 
+          ); 
+          $header = array(t('Assigned'),t('Task'),t('Complete?'), t('Edit')); 
+        } 
       }
-      $header = array(t('Assigned'),t('Task'), t('Complete?'), t('Edit')); 
-      $output .= theme('table', $header, $rows, array("id"=>"task-list")); 
     }
- 
+    $output .= theme('table', $header, $rows, array("id"=>"task-list")); 
     $node->body .= $output;
   }
 }
 
+function tasks_settings() { 
+  //record time spent on a task 
+  $options = array('1' => t('Enabled'), '0' => t('Disabled')); 
+  $form['tasks_use_timer'] = array( 
+    '#type' => 'radios',  
+    '#title' => t('Enable time spent'),  
+    '#default_value' =>  variable_get('tasks_use_timer', 0), 
+    '#options' => $options,  
+    '#description' => t('Allows for time logging of a task') 
+    ); 
+   
+  return $form; 
+} 
 
 
 
 
 
- 
 // PRIVATE FUNCTIONS
 
 
@@ -449,4 +549,299 @@
 
     db_query("UPDATE {tasks} SET order_by = %d WHERE nid = %d", $task->order_by, $task->nid);
   }  
-} 
\ No newline at end of file
+} 
+ 
+ 
+/* 
+  FUNCTION _tasks_hours() 
+ 
+  This function returns total hours worked on task 
+ 
+ 
+*/ 
+ 
+function _tasks_hours($start, $stop,$complete = '0000-00-00') { 
+  $startTimes = explode(',',$start); 
+  $stopTimes = explode(',',$stop); 
+  if($start == 0) { 
+    $startNum = 0; 
+  } else { 
+    $startNum = count($startTimes); 
+  } 
+  if($stop == 0) { 
+    $stopNum = 0; 
+  } else { 
+    $stopNum = count($stopTimes); 
+  } 
+  $totalTime = 0; 
+  // For a complete task final stop time is total in unix time stamp 
+  if($complete != '0000-00-00' && $stopNum - $startNum == 1) { 
+    return $stopTimes[$stopNum-1]; 
+  // if there is at least on start time set the current hours 
+  } else { 
+    if($stopNum > 0) { 
+      for($i=0;$i<$startNum-1;$i++) { 
+        $totalTime = $totalTime+$stopTimes[$i]-$startTimes[$i]; 
+      } 
+    } 
+    if($startNum - $stopNum == 1) { 
+      // If the last stop sign is not set, then use the current time for hours. 
+      $totalTime = $totalTime+time()-$startTimes[$startNum-1]; 
+    } else if($startNum == $stopNum) { 
+      $totalTime = $totalTime+$stopTimes[$stopNum-1]-$startTimes[$startNum-1]; 
+    } 
+    // return hours 
+    if($totalTime > 0) { 
+      $totalTime = round((($totalTime/60)/60),2); 
+    } 
+  }  
+  return $totalTime; 
+} 
+ 
+ 
+/* 
+  FUNCTION _tasks_formated_time() 
+ 
+  This function returns a table of Start and Stop times 
+ 
+ 
+*/ 
+ 
+function _tasks_formated_time($start, $stop,$complete = '0000-00-00',$showTotal = 0) { 
+  $startTimes = explode(',',$start); 
+  $stopTimes = explode(',',$stop); 
+  if($start == 0) { 
+    $startNum = 0; 
+  } else { 
+    $startNum = count($startTimes); 
+  } 
+  if($stop == 0) { 
+    $stopNum = 0; 
+  } else { 
+    $stopNum = count($stopTimes); 
+  } 
+   
+  if($startNum > 0 && $startTimes[$startNum-1] != 0) { 
+    if($stopNum > 0) { 
+      for($i=0;$i<$startNum-1;$i++) { 
+        $rows[] = array( 
+        'data' => array( 
+          array('data' => _tasks_date('g:ia n/j/y',$startTimes[$i]), 'style' => "width:25px"), 
+          array('data' => _tasks_date('g:ia n/j/y',$stopTimes[$i]), 'style' => "width:25px"),       
+        ) 
+        ); 
+      } 
+    } 
+    if($startNum > $stopNum) { 
+      $rows[] = array( 
+      'data' => array( 
+        array('data' => _tasks_date('g:ia n/j/y',$startTimes[$startNum-1]), 'style' => "width:25px"), 
+        array('data' => 'Still Running', 'style' => "width:25px"),       
+      ) 
+      ); 
+    } else if($startNum < $stopNum && $complete) { 
+      $rows[] = array( 
+      'data' => array( 
+        array('data' => _tasks_date('g:ia n/j/y',$startTimes[$startNum-1]), 'style' => "width:25px"), 
+        array('data' => _tasks_date('g:ia n/j/y',$stopTimes[$stopNum-2]), 'style' => "width:25px"),       
+      ) 
+      ); 
+    } else if ($startNum == $stopNum) { 
+      $rows[] = array( 
+      'data' => array( 
+        array('data' => _tasks_date('g:ia n/j/y',$startTimes[$startNum-1]), 'style' => "width:25px"), 
+        array('data' => _tasks_date('g:ia n/j/y',$stopTimes[$stopNum-1]), 'style' => "width:25px"),       
+      ) 
+      ); 
+    } 
+    if($showTotal > 0) { 
+      $rows[] = array( 
+      'data' => array( 
+        array('data' => 'Total Time (hrs)', 'style' => "width:25px"), 
+        array('data' => _tasks_hours($start,$stop,$complete), 'style' => "width:25px"),       
+      ) 
+      ); 
+    } 
+    $header = array(t('Start'),t('Stop')); 
+    return theme('table', $header, $rows, array("id"=>"time-list")); 
+  } 
+} 
+ 
+function _tasks_timer_stopwatch($start, $stop,$complete = '0000-00-00') { 
+  if (user_access('add times')) { 
+    $startTimes = explode(',',$start); 
+    $stopTimes = explode(',',$stop); 
+    if($start == 0) { 
+      $startNum = 0; 
+    } else { 
+      $startNum = count($startTimes); 
+    } 
+    if($stop == 0) { 
+      $stopNum = 0; 
+    } else { 
+      $stopNum = count($stopTimes); 
+    } 
+     
+    if($complete == '0000-00-00') { 
+      if($startNum > $stopNum) { 
+        $form['tasks_stop_watch'] = array( 
+          '#type' => 'hidden', 
+          '#value' => 'stop' 
+          ); 
+        $form['timer'] = array( 
+          '#type' => 'submit', 
+          '#value' => t('Stop Timer') 
+        ); 
+      } else { 
+        $form['tasks_stop_watch'] = array( 
+          '#type' => 'hidden', 
+          '#value' => 'start' 
+          ); 
+        $form['timer'] = array( 
+          '#type' => 'submit', 
+          '#value' => t('Start Timer'.$node->start) 
+          ); 
+      } 
+      //$form['#action'] = "node/$node->nid"; 
+      return $form; 
+    } 
+  } 
+} 
+ 
+function _tasks_timer_form($node) { 
+  global $user; 
+  $form['#type'] = 'item'; 
+  $form['#tree'] = TRUE; 
+  $form['#theme'] = 'tasks_timer_form'; 
+  $editable = FALSE; 
+   
+  if ((user_access('edit own times') && ($user->uid == $node->uid)) || user_access('edit times')) { 
+    $editable = TRUE; 
+  } 
+   
+  if($editable && $node->nid) {  
+    $form['#description'] = t('Edit Start and Stop Times (HH:MM MM/DD/YYYY) or remove entries'); 
+  } else {  
+    $form['#description'] = t('Start and Stop Times');  
+  } 
+  if($node->nid) { 
+    $task = db_fetch_object(db_query("SELECT t.start, t.stop, t.completed FROM {tasks} t WHERE t.nid = %s", $node->nid)); 
+  } else { 
+    $task = array('start' => 0,'stop' => 0,'completed' => 0); 
+  } 
+  $startTimes = explode(',',$task->start); 
+  $stopTimes = explode(',',$task->stop); 
+  if($task->start == 0) { 
+    $startNum = 0; 
+  } else { 
+    $startNum = count($startTimes); 
+  } 
+  if($task->stop == 0) { 
+    $stopNum = 0; 
+  } else { 
+    $stopNum = count($stopTimes); 
+  } 
+   
+  if($startNum > 0) { 
+    for($i=0;$i<$startNum;$i++) { 
+      $form[$i]['start_date'] = array( 
+        '#type' => 'textfield', 
+        '#default_value' => _tasks_date('H:i n/j/y',$startTimes[$i]), 
+        '#size' => 19, 
+        '#maxlength' => 19, 
+        ); 
+      $form[$i]['stop_date'] = array( 
+        '#type' => 'textfield', 
+        '#size' => 19, 
+        '#maxlength' => 19 
+        ); 
+      $form[$i]['delete'] = array( 
+        '#type' => 'checkbox', 
+        '#default_value' => 0 
+        ); 
+      if(!$editable) { 
+        $form[$i]['start_date']['#attributes'] = array('disabled' => 'disabled'); 
+        $form[$i]['stop_date']['#attributes'] = array('disabled' => 'disabled'); 
+        $form[$i]['delete']['#attributes'] = array('disabled' => 'disabled'); 
+      } 
+       
+      if($i == $startNum-1 && $startNum > $stopNum) { 
+        $form[$i]['stop_date']['#default_value'] = 'Running...'; 
+        $form[$i]['stop_date']['#attributes'] = array('disabled' => 'disabled'); 
+      } else { 
+        $form[$i]['stop_date']['#default_value'] = _tasks_date('H:i n/j/y',$stopTimes[$i]); 
+      } 
+    } 
+  } else { 
+    $form[0]['start_date'] = array( 
+      '#type' => 'textfield', 
+      '#default_value' => 'Task not started', 
+      '#attributes' => array('disabled' => 'disabled'), 
+      '#size' => 19, 
+      '#maxlength' => 19, 
+      ); 
+    $form[0]['stop_date'] = array( 
+      '#type' => 'textfield', 
+      '#default_value' => 'Task not started', 
+      '#attributes' => array('disabled' => 'disabled'), 
+      '#size' => 19, 
+      '#maxlength' => 19, 
+      ); 
+  } 
+  $form['tasks_start_num'] = array( 
+          '#type' => 'hidden', 
+          '#value' => $startNum 
+          ); 
+  $form['tasks_stop_num'] = array( 
+          '#type' => 'hidden', 
+          '#value' => $stopNum 
+          ); 
+  return $form; 
+} 
+ 
+function theme_tasks_timer_form(&$form) { 
+  $header = array(t('Start'), t('Stop'), t('Delete')); 
+  foreach (element_children($form) as $key) { 
+    $row = array(); 
+    $row[] = form_render($form[$key]['start_date']); 
+    $row[] = form_render($form[$key]['stop_date']); 
+    $row[] = form_render($form[$key]['delete']); 
+    $rows[]['data'] = $row; 
+  } 
+  $output = theme('table', $header, $rows); 
+  $output .= form_render($form); 
+ 
+  return $output; 
+} 
+ 
+/* 
+  FUNCTION _tasks_date() 
+ 
+  This function returns a formated time with timezone 
+ 
+ 
+*/ 
+ 
+function _tasks_date($format, $timestamp, $offset = null) { 
+  global $user; 
+ 
+  if (isset($offset)) { 
+    $timestamp += $offset; 
+  } 
+  elseif (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) { 
+    $timestamp += $user->timezone; 
+  } 
+  else { 
+    $timestamp += variable_get('date_default_timezone', 0); 
+  } 
+ 
+  // make sure we apply the site first day of the week setting for dow requests 
+  if ($format == 'w') { 
+    $result = _event_day_of_week($timestamp); 
+  } 
+  else { 
+    $result = gmdate($format, $timestamp); 
+  } 
+  return  $result; 
+} 
+ 
