--- worklog.module.orig	2007-03-15 21:52:15.000000000 -0400
+++ worklog.module	2007-03-15 21:55:26.000000000 -0400
@@ -16,12 +16,7 @@
  */
 function worklog_help($section) {
   switch ($section) {
-  case 'admin/modules#description':
-	return t('Keep track of time spent working.');
-  case 'node/add#worklog':
-	return t('Log time spent working on something.');
-  case 'node/add#worklog-invoice':
-	return t('A collection of worklog entries.');
+  
   }
   
 }
@@ -30,9 +25,12 @@
  * Implementation of hook_node_info().
  */
 function worklog_node_info() {
-  return array('worklog' => array('name' => t('worklog'), 'base' => 'worklog'),
-               'worklog-invoice' => array('name' => t('worklog invoice'),
-                                          'base' => 'worklog'));
+  return array('worklog' => array('name' => t('worklog'),
+                              'module' => 'worklog',
+                              'description' => 'Log time spent working on something.'),
+               'worklog_invoice' => array('name' => t('worklog invoice'),
+                              'module' => 'worklog_invoice',
+                              'description' => 'A collection of worklog entries.'));
 }
 
 /**
@@ -41,9 +39,9 @@
 define ('WORKLOG_PERM_CREATE', 'create worklog');
 define ('WORKLOG_PERM_EDIT', 'edit own worklog');
 define ('WORKLOG_PERM_VIEW_OTHERS', 'view others worklog');
+define ('WORKLOG_PERM_ADMIN', 'admin worklog settings');
 function worklog_perm() {
-  return array(WORKLOG_PERM_EDIT, WORKLOG_PERM_CREATE, 
-			   WORKLOG_PERM_VIEW_OTHERS);
+  return array(WORKLOG_PERM_EDIT, WORKLOG_PERM_CREATE, WORKLOG_PERM_VIEW_OTHERS, WORKLOG_PERM_ADMIN);
 }
 
 /**
@@ -74,14 +72,16 @@
   $items = array();
 
   if ($may_cache) {
-    $items[] = array('path' => 'node/add/worklog', 'title' => t('worklog'),
-      'access' => user_access(WORKLOG_PERM_CREATE));
-    $items[] = array('path' => 'node/add/worklog-invoice', 
-                     'title' => t('worklog invoice'),
-                     'access' => user_access(WORKLOG_PERM_CREATE)
-    );    
+    $items[] = array('path' => 'admin/settings/worklog',
+      'title' => t('Worklog settings'),
+      'description' => t('Configure worklog settings'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('worklog_settings'),
+      'access' => user_access(WORKLOG_PERM_ADMIN),
+      'type' => MENU_NORMAL_ITEM
+    );
   }
-  
+
   return $items;
 }
 
@@ -94,7 +94,7 @@
   if (!user_access(WORKLOG_PERM_VIEW_OTHERS)) {
 	global $user;
 	if ($primary_table == 'n' || $primary_table == 'node') {
-	  $where = "$primary_table.type != 'worklog' OR $primary_table.type != 'worklog-invoice' OR $primary_table.uid=$user->uid";
+	  $where = "$primary_table.type != 'worklog' OR $primary_table.type != 'worklog_invoice' OR $primary_table.uid=$user->uid";
 	  return array('where' => $where);
 	}
   }
@@ -112,13 +112,6 @@
  * Implementation of hook_insert().
  */
 function worklog_insert($node) {
-  if ($node->type == 'worklog')
-    return _worklog_insert($node);
-  else if ($node->type == 'worklog-invoice')
-    return _worklog_invoice_insert($node);
-}
-
-function _worklog_insert($node) {
   $form_values = $node->worklog;
   if ($form_values['wlid'])
 	// if worklog row already exists, simply update
@@ -130,40 +123,17 @@
 
   db_query('INSERT INTO {worklog} (nid, started, elapsed, name, rate) VALUES (%d, %d, %d, \'%s\', %d)', 
 		   $node->nid, $started, $elapsed, $node->title, $form_values['rate']);
-
-  if (count($node->worklog_invoice)) {
-    if ($invoice_nid = $node->worklog_invoice['invoice_nid']) {
-      db_query('INSERT INTO {worklog_invoice_map} (invoice_nid, worklog_nid) VALUES (%d, %d)', $invoice_nid, $node->nid);      
-    }
-  }
-  
 }
 
-function _worklog_invoice_insert($node) {
+function worklog_invoice_insert($node) {
   db_query('INSERT INTO {worklog_invoice} (nid, closed) VALUES (%d, %d)',
            $node->nid, $node->worklog['closed']);
-  
-  if (count($node->worklog_invoice) &&
-      count($node->worklog_invoice['worklog_nids'])) {
-    foreach ($node->worklog_invoice['worklog_nids'] as $worklog_nid => $checked) {
-      if ($checked)
-        db_query('INSERT INTO {worklog_invoice_map} (invoice_nid, worklog_nid) VALUES (%d, %d)', $node->nid, $worklog_nid);
-    }
-  }
-
 }
 
 /**
  * Implementation of hook_update().
  */
 function worklog_update($node) {
-  if ($node->type == 'worklog')
-    return _worklog_update($node);
-  else if ($node->type == 'worklog-invoice')
-    return _worklog_invoice_update($node);
-}
-
-function _worklog_update($node) {
   $form_values = $node->worklog;
   $elapsed = $form_values['elapsed'];
   $started = $form_values['started'];
@@ -171,30 +141,12 @@
   db_query('REPLACE INTO {worklog} (wlid, nid, started, elapsed, name, rate) VALUES (%d, %d, %d, %d, \'%s\', %f)', 
 		   $form_values['wlid'], $node->nid, $started, $elapsed, $node->title, $form_values['rate']);
   
-  db_query('DELETE FROM {worklog_invoice_map} WHERE worklog_nid = %d', $node->nid);
-  if (count($node->worklog_invoice)) {
-    if ($invoice_nid = $node->worklog_invoice['invoice_nid']) {
-      db_query('INSERT INTO {worklog_invoice_map} (invoice_nid, worklog_nid) VALUES (%d, %d)', $invoice_nid, $node->nid);      
-    }
-  }
-
-
 }
 
-function _worklog_invoice_update($node) {
+function worklog_invoice_update($node) {
   // will this work if new revision created?
   db_query('REPLACE INTO {worklog_invoice} (nid, closed) VALUES (%d, %d)',
            $node->nid, $node->worklog['closed']);
-
-  db_query('DELETE FROM {worklog_invoice_map} WHERE invoice_nid = %d', $node->nid);
-  if (count($node->worklog_invoice) &&
-      count($node->worklog_invoice['worklog_nids'])) {
-    foreach ($node->worklog_invoice['worklog_nids'] as $worklog_nid => $checked) {
-      if ($checked)
-        db_query('INSERT INTO {worklog_invoice_map} (invoice_nid, worklog_nid) VALUES (%d, %d)', $node->nid, $worklog_nid);
-    }
-  }
-
 }
 
 
@@ -202,50 +154,38 @@
  * Implementation of hook_delete().
  */
 function worklog_delete($node) {
-  if ($node->type == 'worklog') {
-    db_query('DELETE FROM {worklog} WHERE wlid=%d', $node->worklog->wlid);
-    db_query('DELETE FROM {worklog_invoice_map} WHERE worklog_nid=%d', $node->nid);
-  }
-  else if ($node->type == 'worklog-invoice') {
-    // TODO: what should happen to orphaned worklog entries?
-    db_query('DELETE FROM {worklog_invoice_map} WHERE invoice_nid=%d', $node->nid);
-    db_query('DELETE FROM {worklog_invoice} WHERE nid=%d', $node->nid);
-  }
+  db_query('DELETE FROM {worklog} WHERE wlid=%d', $node->worklog->wlid);
+  db_query('DELETE FROM {worklog_invoice_map} WHERE worklog_nid=%d', $node->nid);
+}
+function worklog_invoice_delete($node) {
+  // TODO: what should happen to orphaned worklog entries?
+  db_query('DELETE FROM {worklog_invoice_map} WHERE invoice_nid=%d', $node->nid);
+  db_query('DELETE FROM {worklog_invoice} WHERE nid=%d', $node->nid);
 }
 
 /**
  * Implementation of hook_load().
  */
 function worklog_load($node) {
-  if ($node->type == 'worklog') {
-    $worklog = db_fetch_object(db_query('SELECT * FROM {worklog} WHERE nid=%d', $node->nid));
-    $invoice = db_result(db_query('SELECT invoice_nid FROM {worklog_invoice_map} WHERE worklog_nid = %d', $node->nid));
-    $worklog->invoice_nid = $invoice;
-
-    return array('worklog' => $worklog);
-  }
-
-  else if ($node->type == 'worklog-invoice') {
-    $invoice_map = db_query('SELECT * FROM {worklog_invoice_map} WHERE invoice_nid = %d', $node->nid);
-    $invoice = db_fetch_object(db_query('SELECT * FROM {worklog_invoice} WHERE nid=%d', $node->nid));
-    while ($row = db_fetch_object($invoice_map)) {
-      $invoice->worklog_nids[] = $row->worklog_nid;
-    }
-    return array('worklog' => $invoice);
+  $worklog = db_fetch_object(db_query('SELECT * FROM {worklog} WHERE nid=%d', $node->nid));
+  $invoice = db_result(db_query('SELECT invoice_nid FROM {worklog_invoice_map} WHERE worklog_nid = %d', $node->nid));
+  $worklog->invoice_nid = $invoice;
+
+  return array('worklog' => $worklog);
+}
+function worklog_invoice_load($node) {
+  $invoice_map = db_query('SELECT * FROM {worklog_invoice_map} WHERE invoice_nid = %d', $node->nid);
+  $invoice = db_fetch_object(db_query('SELECT * FROM {worklog_invoice} WHERE nid=%d', $node->nid));
+  while ($row = db_fetch_object($invoice_map)) {
+    $invoice->worklog_nids[] = $row->worklog_nid;
   }
+  return array('worklog' => $invoice);
 }
 
 /**
  * Implementation of hook_form().
  */
 function worklog_form(&$node) {
-  if ($node->type == 'worklog')
-    return _worklog_form($node);
-  else if ($node->type == 'worklog-invoice')
-    return _worklog_invoice_form($node);
-}
-
-function _worklog_form(&$node) {
   // if we are creating a new node, get default values from wlid passed in
   if (!isset($node->nid) && !isset($node->worklog)) {
 	$timer = _worklog_get_timer($_REQUEST['wlid']);
@@ -286,98 +226,21 @@
   $form['body'] = array('#type' => 'textarea', '#title' => t('Description'), '#default_value' => $node->body, '#rows' => 5, '#required' => false);
   $form['format'] = filter_form($node->format);
 
-  // mapping between worklog and invoice
-  $invoice_nid = $node->worklog->invoice_nid;
-  $invoice = new stdClass();
-  if ($invoice_nid) {
-    $invoice = node_load($invoice_nid);
-    if ($invoice->worklog->closed) {
-      drupal_set_message(t('This worklog entry may have already been invoiced.'));
-      $form['worklog_invoice'] = 
-        array('#type' => 'fieldset',
-              '#title' => t('Invoice'),
-              '#description' => t('This worklog entry is included in the invoice %invoice_link.  The invoice has been marked closed.  It is recommended not to make any changes to this entry.',
-                                  array('%invoice_link' => $invoice->title)),
-        );
-    }
-  }
-  if (!$invoice_nid || !$invoice->worklog->closed) {
-    // let user assign a worklog to an invoice
-    $form['worklog_invoice'] = 
-      array('#type' => 'fieldset',
-            '#title' => t('Invoice'),
-            '#tree' => TRUE,
-            '#collapsible' => TRUE,
-      );
-    
-    $result = db_query(db_rewrite_sql('SELECT n.title, n.nid FROM {node} n LEFT JOIN {worklog_invoice} wi ON n.nid = wi.nid WHERE n.type=\'worklog-invoice\' AND n.status = 1 AND (wi.closed = 0 OR n.nid=%d)'), $invoice_nid);
-    $options = array(0 => t('no invoice'));
-    while ($row = db_fetch_object($result)) {
-      $options[$row->nid] = t('%title',
-                              array('%title' =>$row->title,
-                              )
-      );
-    }
-    $form['worklog_invoice']['invoice_nid'] =
-      array('#type' => 'radios',
-            '#title' => t('Select invoice'),
-            '#options' => $options,
-            '#default_value' => $invoice_nid,
-            '#description' => t('This item will be included in the chosen invoice.'),
-      );
-    
-  }
-
   return $form;
 }
 
-function _worklog_invoice_form(&$node) {
+function worklog_invoice_form(&$node) {
   $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title);
 
-  $form['worklog'] = array('#tree' => true,
-						   '#weight' => -1,
-  );
+  $form['worklog'] = array('#tree' => true, '#weight' => -1,);
   $form['worklog']['closed'] = array('#type' => 'checkbox',
                                      '#title' => t('Closed to new entries'),
                                      '#default_value' => $node->worklog->closed,
                                      '#description' => t('Check this box when you will not add more worklog entries to this invoice.  For example, after you have sent the invoice to a client.'),
   );
-  
   $form['body'] = array('#type' => 'textarea', '#title' => t('Description'), '#default_value' => $node->body, '#rows' => 5, '#required' => false);
   $form['format'] = filter_form($node->format);
 
-  // on invoice form, allow choice of all non-invoiced worklogs
-  $result = db_query(db_rewrite_sql('SELECT n.title, w.* FROM {node} n LEFT JOIN {worklog} w ON n.nid = w.nid LEFT JOIN {worklog_invoice_map} wim ON wim.worklog_nid = n.nid WHERE n.type=\'worklog\' AND n.status = 1 AND (wim.invoice_nid IS NULL OR wim.invoice_nid = %d)'), $node->nid);
-  $options = array();
-  while ($row = db_fetch_object($result)) {
-    // TODO: show more than title, perhaps build a table.
-    $options[$row->nid] = t('%title (%hours @ %rate = %amount)',
-                            array('%title' =>$row->title,
-                                  '%hours' => _worklog_format_duration($row->elapsed),
-                                  '%rate' => $row->rate,
-                                  '%amount' => _worklog_calculate_bill($row->elapsed, $row->rate)
-                            ));
-  }
-  $form['worklog_invoice'] = 
-    array('#type' => 'fieldset',
-          '#title' => t('Invoice items'),
-          '#tree' => TRUE,
-          '#collapsible' => TRUE,
-    );
-  if (count($options)) {
-    //drupal_set_message(dprint_r($form['#node'], 1));
-    $form['worklog_invoice']['worklog_nids'] =
-      array('#type' => 'checkboxes',
-            '#title' => t('Select items'),
-            '#options' => $options,
-            '#default_value' => $node->worklog->worklog_nids,
-            '#description' => t('Choose the items to include in this invoice.'),
-      );
-  }
-  else {
-    $form['worklog_invoice']['#description'] = t('There are no worklog entries to include in this invoice.');
-  }
-  
   return $form;
 }
 
@@ -401,89 +264,103 @@
 define('WORKLOG_BUTTON_RESUME', t('Resume'));
 define('WORKLOG_BUTTON_SAVE', t('Save'));
 
+function worklog_forms() {
+  $forms['worklog_paused_form']= array(
+    'callback' => 'worklog_paused_form',
+    'callback arguments' => array('worklog_paused_form'),
+  );
+  $forms['worklog_stop_form']= array(
+    'callback' => 'worklog_stop',
+    'callback arguments' => array('worklog_stop_form'),
+  );
+  $forms['worklog_start_form']= array(
+    'callback' => 'worklog_start',
+    'callback arguments' => array('worklog_start_form'),
+  );
+  return $forms;
+}
+
+function worklog_stop_form($timer) {
+  $form['name'] = array('#type' => 'markup',
+    '#value' => "<p>$timer->name</p>"
+  );
+  $form['wlid'] = array('#type' => 'hidden',
+    '#value' => $timer->wlid);
+  $form['buttons'][] = array('#type' => 'submit',
+    '#name' => 'op',
+    '#value' => WORKLOG_BUTTON_PAUSE
+  );
+  $form['buttons'][] = array('#type' => 'submit',
+    '#name' => 'op',
+    '#value' => WORKLOG_BUTTON_STOP
+  );
+  return $form;
+}
+function worklog_start_form() {
+  // the form to start a new timer
+  $form['name'] = array('#type' => 'textfield',
+    '#title' => t('Task name'),
+    '#size' => 20,
+  );
+  $form['submit'] = array('#type' => 'submit',
+    '#value' => t('Start Timer')
+  );
+  // $form['#attributes'] = array('target' => '_blank'); // new window
+  return $form;
+}
+
+function worklog_paused_form($timer) {
+  $form[$timer->wlid] = array('#type' => 'fieldset');
+  $form[$timer->wlid][] = array('#type' => 'markup', '#value' => "<div class=\"worklog_pause_header\"><span class=\"name\">$timer->name</span> <span class=\"elapsed\">"._worklog_format_duration($timer->elapsed)."</span></div>");
+  $form[$timer->wlid][] = array('#type' => 'submit', '#name' => 'worklog_'.$timer->wlid, '#value' => WORKLOG_BUTTON_RESUME, '#attributes' => $resume_attrs, );
+  $form[$timer->wlid][] = array('#type' => 'submit', '#name' => 'worklog_'.$timer->wlid, '#value' => WORKLOG_BUTTON_SAVE);
+  return $form;
+}
+
 /**
  * Define the blocks.  Implementation of hook_block()
  */
-function worklog_block($op = 'list', $delta=0, $edit=array()) {
+function worklog_block($op = 'list', $delta=0) {
   switch ($op) {
   case 'list':
-	$items[WORKLOG_BLOCK_TIMER]['info'] = 
-	  t('Worklog: start a worklog timer');
-	$items[WORKLOG_BLOCK_PAUSED]['info'] =
-	  t('Worklog: paused timers');
-    $items[WORKLOG_BLOCK_OP]['info'] =
-      t('Worklog: operations');
-	return $items;
+    $blocks[WORKLOG_BLOCK_TIMER]['info'] = t('Worklog: start a worklog timer');
+    $blocks[WORKLOG_BLOCK_PAUSED]['info'] = t('Worklog: paused timers');
+    $blocks[WORKLOG_BLOCK_OP]['info'] = t('Worklog: operations');
+    return $blocks;
   case 'view':
 	switch ($delta) {
-	case WORKLOG_BLOCK_TIMER:
-	  if (!user_access(WORKLOG_PERM_CREATE))
-		return;
-
-	  if ($timer = _worklog_current_timer()) {
-		$form['name'] = array('#type' => 'markup',
-							  '#value' => "<p>$timer->name</p>");
-		$form['wlid'] = array('#type' => 'hidden',
-							  '#value' => $timer->wlid);
-		$form['buttons'][] = array('#type' => 'submit',
-							   '#name' => 'op',
-							   '#value' => WORKLOG_BUTTON_PAUSE,
-							   );
-		$form['buttons'][] = array('#type' => 'submit',
-							  '#name' => 'op',
-							  '#value' => WORKLOG_BUTTON_STOP,
-							  );
-		$block['content'] = drupal_get_form('worklog_stop_form', $form);
-		$block['subject'] = t('Worklog Timer');
-	  }
-	  else {
-		// the form to start a new timer
-		$form['name'] = array('#type' => 'textfield',
-							  '#title' => t('Task name'),
-							  '#size' => 20,
-							  );
-		$form['submit'] = array('#type' => 'submit',
-								'#value' => t('Start Timer')
-								);
-		// $form['#attributes'] = array('target' => '_blank'); // new window
-		$block['content'] = drupal_get_form('worklog_start_form', $form);
-		$block['subject'] = t('Worklog New Timer');
-	  }
-	  return $block;
-	case WORKLOG_BLOCK_PAUSED:
+        case WORKLOG_BLOCK_TIMER && user_access(WORKLOG_PERM_CREATE):
+          if($timer = _worklog_current_timer()) {
+            $block['content'] = drupal_get_form('worklog_stop_form',$timer);
+          }
+          else {
+          $block['content'] = drupal_get_form('worklog_start_form');
+          }
+          $block['subject'] = t('Worklog New Timer');
+          return $block;
+        case WORKLOG_BLOCK_PAUSED && user_access(WORKLOG_PERM_EDIT):
 	  global $user;
-	  if (!user_access(WORKLOG_PERM_EDIT))
-		return;
-
 	  // give user control over paused timers
 	  $result = db_query("SELECT * FROM {worklog} WHERE nid IS NULL AND timer_start = 0 AND uid=%d", $user->uid);
 	  if (!db_num_rows($result))
 		return;
-
-	  $form = array();
 	  $current_timer = _worklog_current_timer();
 	  if ($current_timer)
-		$resume_attrs = array('disabled' => 1);
+            $resume_attrs = array('disabled' => 1);
 	  while ($timer = db_fetch_object($result)) {
-		$form[$timer->wlid] = array('#type' => 'fieldset');
-		$form[$timer->wlid][] = array('#type' => 'markup',
-									  '#value' => "<div class=\"worklog_pause_header\"><span class=\"name\">$timer->name</span> <span class=\"elapsed\">"._worklog_format_duration($timer->elapsed)."</span></div>");
-		$form[$timer->wlid][] = array('#type' => 'submit',
-									  '#name' => 'worklog_'.$timer->wlid,
-									  '#value' => WORKLOG_BUTTON_RESUME,
-									  '#attributes' => $resume_attrs,
-									  );
-		$form[$timer->wlid][] = array('#type' => 'submit',
-									  '#name' => 'worklog_'.$timer->wlid,
-									  '#value' => WORKLOG_BUTTON_SAVE);
-	  }
-	  $block['content'] .= drupal_get_form('worklog_paused_form', $form);
+            $block['content'] .= drupal_get_form('worklog_paused_form',$timer);
+          }
 	  $block['subject'] = t('Worklog: Paused Timers');
 	  return $block;
 	}
   }
 }
-
+/**
+* Theme the block worklog form.
+*/
+function theme_worklog_block_form($form) {
+//return drupal_render($form);
+}
 /**
  * Our pause form presents buttons for each paused timer.
  * Here we figure out which timer to act on and perform the necessary action.
@@ -715,7 +592,7 @@
 function worklog_view(&$node, $teaser=false, $page=false) {
   if ($node->type == 'worklog')
     return _worklog_view($node, $teaser, $pager);
-  else if ($node->type == 'worklog-invoice') {
+  else if ($node->type == 'worklog_invoice') {
     return _worklog_invoice_view($node, $teaser, $pager);
   }
 }
@@ -737,8 +614,7 @@
 function _worklog_invoice_view(&$node, $teaser, $page) {
   $node = node_prepare($node, $teaser);
   
-  if (($vid = variable_get('worklog_invoice_view', 'worklog_invoice')) &&
-      function_exists('views_get_view')) {
+  if ($vid = variable_get('worklog_invoice_view', 0)) {
     // use a view to render invoice items
     $view = views_get_view($vid);
     $node->body .= views_build_view('embed', $view, array($node->nid),
@@ -773,10 +649,10 @@
     if ($invoice_nid = $node->worklog->invoice_nid) {
       $invoice = node_load($invoice_nid);
       if ($invoice->status)
-        $items[] = l($invoice->title, "node/$invoice_nid");
+        $links['worklog_invoice_$invoice_nid'] = array('title' => t($invoice->title), 'href' => "node/$invoice_nid");
     }
   }
-  return $items;
+  return $links;
 }
 
 function worklog_views_tables() {
@@ -831,7 +707,7 @@
   if ($op == 'summary') {
     $query->add_field('title');
     $query->add_field('nid');
-    $query->add_where("node.type = 'worklog-invoice'");
+    $query->add_where("node.type = 'worklog_invoice'");
     return array('field' => 'node.title');
   }
   else if ($op == 'title') {
@@ -882,6 +758,43 @@
   return theme('worklog_price', $billable);
 }
 
+/**
+ * theme function called by views module
+ */
+/* XXX
+function theme_views_view_worklog($view, $type, $data) {
+  // TODO: break data up into weekly chunks
+  
+  if ($type == 'page') {
+	drupal_set_title(views_get_title($view));
+  }
+  if ($view->header) {
+    $header = check_markup($view->header, $view->header_format, false);
+    $output = "<div class='view-header' id='view-header-$view->name'>$header</div>\n";
+  }
+  
+  $output .= theme('views_view_table', $view, $data);
+
+  // compute totals
+  $total_elapsed = 0;
+  $total_bill = 0;
+  foreach ($data as $datum) {
+	//drupal_set_message(theme('debug', $datum));
+	$elapsed = _worklog_round_duration($datum->worklog_elapsed);
+	$total_elapsed += $elapsed;
+	if ($datum->worklog_rate) {
+	  $total_bill += _worklog_round_bill(($datum->worklog_rate * $elapsed) / (60 * 60));
+	}
+  }
+  
+  $output .= "<dl>\n<dt>".t("Total Time")."</dt><dd>".theme('worklog_duration', $total_elapsed)."</dd>\n";
+  // TODO: format nicely
+  $output .= "<dt>".t("Total Bill")."</dt><dd>".$total_bill."</dd>\n";
+  $output .= "</dl>\n";
+
+  return $output;
+}
+*/
 
 /**
  * Display seconds as hours
@@ -895,15 +808,146 @@
   return worklog_format_price($amount);
 }
 
+///////////////////////////////////////////////////////////////////////////
+// functions for invoices
+
+/**
+ * Use hook_form_alter to manage the one-to-many relationship between invoices
+ * and worklog entries
+ */
+function worklog_form_alter($form_id, &$form) {
+  if (isset($form['type']) &&
+      isset($form['type']['#value'])) {
+    $type = $form['type']['#value'];
+    if ($type . '_node_form' == $form_id) {
+      // it's a node form
+      $nid = $form['nid']['#value'];
+      if ($type == 'worklog_invoice') {
+        // on invoice form, allow choice of all non-invoiced worklogs
+        $result = db_query(db_rewrite_sql('SELECT n.title, w.* FROM {node} n LEFT JOIN {worklog} w ON n.nid = w.nid LEFT JOIN {worklog_invoice_map} wim ON wim.worklog_nid = n.nid WHERE n.type=\'worklog\' AND n.status = 1 AND (wim.invoice_nid IS NULL OR wim.invoice_nid = %d)'), $nid);
+        $options = array();
+        while ($row = db_fetch_object($result)) {
+          // TODO: show more than title, perhaps build a table.
+          $options[$row->nid] = t('%title (%hours @ %rate = %amount)',
+                                  array('%title' =>$row->title,
+                                        '%hours' => _worklog_format_duration($row->elapsed),
+                                        '%rate' => $row->rate,
+                                        '%amount' => _worklog_calculate_bill($row->elapsed, $row->rate)
+                                  ));
+        }
+        $form['worklog_invoice'] = 
+          array('#type' => 'fieldset',
+                '#title' => t('Invoice items'),
+                '#tree' => TRUE,
+                '#collapsible' => TRUE,
+          );
+        //drupal_set_message(dprint_r($form['#node'], 1));
+        $form['worklog_invoice']['worklog_nids'] =
+          array('#type' => 'checkboxes',
+                '#title' => t('Select items'),
+                '#options' => $options,
+                '#default_value' => $form['#node']->worklog->worklog_nids,
+                '#description' => t('Choose the items to include in this invoice.'),
+          );
+
+        // callback to perform invoice to worklog mapping
+        $form['#submit'] = array('_worklog_invoice_map_form_submit' => array()) + (array)$form['#submit'];
+
+      }
+      else if ($type == 'worklog') {
+        $invoice_nid = $form['#node']->worklog->invoice_nid;
+        $invoice = new stdClass();
+        if ($invoice_nid) {
+          $invoice = node_load($invoice_nid);
+          if ($invoice->worklog->closed) {
+            drupal_set_message(t('This worklog entry may have already been invoiced.'));
+            $form['worklog_invoice'] = 
+              array('#type' => 'fieldset',
+                    '#title' => t('Invoice'),
+                    '#description' => t('This worklog entry is included in the invoice %invoice_link.  The invoice has been marked closed.  It is recommended not to make any changes to this entry.',
+                                        array('%invoice_link' => $invoice->title)),
+              );
+          }
+        }
+        if (!$invoice_nid || !$invoice->worklog->closed) {
+          // let user assign a worklog to an invoice
+          $form['worklog_invoice'] = 
+            array('#type' => 'fieldset',
+                  '#title' => t('Invoice'),
+                  '#tree' => TRUE,
+                  '#collapsible' => TRUE,
+            );
+            $result = db_query('SELECT n.title, n.nid FROM {node} n LEFT JOIN {worklog_invoice} wi ON n.nid = wi.nid WHERE n.type=\'%s\' AND n.status = 1 AND (wi.closed = 0)',array('%s'=>'worklog_invoice'));
+          $options = array(0 => t('no invoice'));
+          while ($row = db_fetch_object($result)) {
+            $options[$row->nid] = t('%title', 
+                                    array('%title' =>$row->title,
+                                  )
+            );
+          }
+          $form['worklog_invoice']['invoice_nid'] =
+            array('#type' => 'radios',
+                  '#title' => t('Select invoice'),
+                  '#options' => $options,
+                  '#default_value' => $invoice_nid,
+                  '#description' => t('This item will be included in the chosen invoice.'),
+          );
+          
+        }
+        // callback to perform invoice to worklog mapping
+        $form['#submit'] = array('_worklog_map_form_submit' => array()) + (array)$form['#submit'];
+        
+        
+      }
+    }
+  }
+}
+
+function _worklog_invoice_map_form_submit($form_id, $values) {
+  //drupal_set_message("_worklog_invoice_map_form_submit($form_id)" . dprint_r($values, 1));
+  $invoice_nid = $values['nid'];
+  db_query('DELETE FROM {worklog_invoice_map} WHERE invoice_nid = %d', $invoice_nid);
+  if (count($values['worklog_invoice']) &&
+      count($values['worklog_invoice']['worklog_nids'])) {
+    foreach ($values['worklog_invoice']['worklog_nids'] as $worklog_nid => $checked) {
+      if ($checked)
+        db_query('INSERT INTO {worklog_invoice_map} (invoice_nid, worklog_nid) VALUES (%d, %d)', $invoice_nid, $worklog_nid);
+    }
+  }
+}
 
+function _worklog_map_form_submit($form_id, $values) {
+  //drupal_set_message("_worklog_map_form_submit($form_id)" . dprint_r($values, 1));
+  $worklog_nid = $values['nid'];
+  db_query('DELETE FROM {worklog_invoice_map} WHERE worklog_nid = %d', $worklog_nid);
+  if (count($values['worklog_invoice'])) {
+    if ($invoice_nid = $values['worklog_invoice']['invoice_nid']) {
+      db_query('INSERT INTO {worklog_invoice_map} (invoice_nid, worklog_nid) VALUES (%d, %d)', $invoice_nid, $worklog_nid);      
+    }
+  }
+}
 
 function worklog_settings() {
+  $form['worklog_invoice'] = 
+    array('#type' => 'fieldset',
+          '#title' => t('Invoice settings'),
+    );
+  $options = array(0 => '<' . t('none') . '>') +
+    _worklog_get_views();
+  $form['worklog_invoice']['worklog_invoice_view'] =
+    array('#type' => 'select',
+          '#title' => t('Worklog view'),
+          '#description' => t('Use the Views module to define a view showing all the entries in an invoice.  The view\'s first argument should be a worklog invoice ID.  Select the view here, and it will be included when viewing an invoice.'),
+          '#options' => $options,
+          '#default_value' => variable_get('worklog_invoice_view', 0),
+    );
+
   // Begin price format section
   $form['price_format'] = array(
     '#type' => 'fieldset', 
     '#title' => t('Price Formatting'), 
     '#collapsible' => TRUE, 
-    '#collapsed' => FALSE, 
+    '#collapsed' => TRUE, 
   );
   $form['price_format']['worklog_symbol'] = array(
     '#type' => 'textfield', 
@@ -941,10 +985,30 @@
     '#size' => 3, '#maxlength' => 5, 
     '#desciption' => t('How many slots are needed after the decimal?')
    );
+  return system_settings_form($form);
+}
 
-
-
-  return $form;
+/**
+ * Fetches a list of all views from the database.
+ * 
+ * Unfortunately the views module does not provide a helper for us, so we have to use some knowledge of the views database schema.
+ *
+ * @return
+ *   An array of views, where each key is the view's ID, and each value is its
+ *   description (not its title).
+ */
+function _worklog_get_views() {
+  $views = array();
+  
+  if (function_exists('views_build_view')) {
+    // views module is installed
+    $result = db_query("SELECT vid, name FROM {view_view} ORDER BY description");
+    while ($view = db_fetch_object($result)) {
+      $views[$view->vid] = $view->name;
+    }
+  }
+  
+  return $views;
 }
 
 /**
@@ -958,83 +1022,4 @@
   return (variable_get('worklog_symbol_position', 1) == 1) ? variable_get('worklog_symbol', '$') . $price : $price . variable_get('worklog_symbol', '$');
 }
 
-////////////////////////////////////////////////////////////////
-// default views
-
-/**
- * worklog_invoice view is included in each invoice.  It displays a summary of
- * each worklog entry in the invoice.
- */
-function worklog_views_default_views() {
-  $view = new stdClass();
-  $view->name = 'worklog_invoice';
-  $view->description = 'Display worklog items in an invoice';
-  $view->access = array (
-);
-  $view->view_args_php = '';
-  $view->page = TRUE;
-  $view->page_title = 'Worklog invoice items %1';
-  $view->page_header = '';
-  $view->page_header_format = '1';
-  $view->page_footer = '';
-  $view->page_footer_format = '1';
-  $view->page_empty = 'No items';
-  $view->page_empty_format = '1';
-  $view->page_type = 'table';
-  $view->url = 'worklog_invoice';
-  $view->use_pager = TRUE;
-  $view->nodes_per_page = '99';
-  $view->sort = array (
-    array (
-      'tablename' => 'worklog',
-      'field' => 'started',
-      'sortorder' => 'ASC',
-      'options' => '',
-    ),
-  );
-  $view->argument = array (
-    array (
-      'type' => '0',
-      'argdefault' => '6',
-      'title' => '',
-      'options' => '',
-      'wildcard' => '',
-      'wildcard_substitution' => '',
-    ),
-  );
-  $view->field = array (
-    array (
-      'tablename' => 'node',
-      'field' => 'title',
-      'label' => 'item',
-      'handler' => 'views_handler_field_nodelink',
-      'sortable' => '1',
-    ),
-    array (
-      'tablename' => 'worklog',
-      'field' => 'elapsed',
-      'label' => 'time',
-      'sortable' => '1',
-    ),
-    array (
-      'tablename' => 'worklog',
-      'field' => 'rate',
-      'label' => 'rate',
-      'sortable' => '1',
-    ),
-    array (
-      'tablename' => 'worklog',
-      'field' => 'bill',
-      'label' => 'amount',
-    ),
-  );
-  $view->filter = array (
-  );
-  $view->exposed_filter = array (
-  );
-  $view->requires = array(worklog, node);
-  $views[$view->name] = $view;
-
-  return $views;
-}
-?>
\ Pas de fin de ligne à la fin du fichier.
+?>
