/[drupal]/contributions/modules/casetracker_work/casetracker_work.module
ViewVC logotype

Contents of /contributions/modules/casetracker_work/casetracker_work.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.3 - (show annotations) (download) (as text)
Mon Mar 31 09:58:36 2008 UTC (19 months, 4 weeks ago) by sime
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5
Changes since 1.2: +411 -230 lines
File MIME type: text/x-php
* Gone through and revitalized code.
* added views support, more could be done.
1 <?php
2
3 define('CT_WORK_HOUR', 60 * 60);
4 define('CT_WORK_DAY', 60 * 60 * 24);
5 define('CT_WORK_WEEK', 60 * 60 * 24 * 7);
6
7 define('CT_WORK_STARTFIN', 0);
8 define('CT_WORK_DURATION', 1);
9 define('CT_WORK_STARTFIN_AND', 2);
10 define('CT_WORK_DURATION_AND', 3);
11
12
13 /**
14 * Implementation of hook_help
15 */
16 function casetracker_work_help($section) {
17 switch ($section) {
18 case 'admin/build/modules#description':
19 return t('Keep track of time spent working.');
20 }
21 }
22
23 /**
24 * Implementation of hook_user().
25 */
26 function casetracker_work_user($type, $edit, &$user, $category = NULL) {
27 $rates = casetracker_work_get_rates(true);
28 if ($type == 'form' && $category == 'account' && user_access('manage work and rates')) {
29 // Present a form for selecting a default rate per user.
30 $form['casetracker_work'] = array(
31 '#type'=>'fieldset',
32 '#title' => t('Case Tracker settings'),
33 '#weight' => 6,
34 '#collapsible' => true,
35 );
36 $form['casetracker_work']['casetracker_rate'] = array(
37 '#type' => 'select',
38 '#title' => t('Default rate type'),
39 '#default_value' => strlen($edit['casetracker_rate']) ? $edit['casetracker_rate'] : '',
40 '#options' => $rates,
41 '#description' => t('Select the default rate for this user.'),
42 );
43 }
44 return $form;
45 }
46
47 /**
48 * Implementation of hook_perm().
49 */
50 function casetracker_work_perm() {
51 return array('manage work and rates', 'log work',);
52 }
53
54 /**
55 * Implementation of hook_menu().
56 */
57 function casetracker_work_menu($may_cache) {
58 $items = array();
59 if ($may_cache) {
60 $items[] = array(
61 'path' => 'casetracker/work/add', 'title' => t('Log time'),
62 'access' => user_access('log work'),
63 'callback' => 'casetracker_work_entry',
64 'callback arguments' => array('add'),
65 'type' => MENU_CALLBACK,
66 );
67 $items[] = array(
68 'path' => 'casetracker/work/edit', 'title' => t('Edit time'),
69 'access' => user_access('manage work and rates'),
70 'callback' => 'casetracker_work_entry',
71 'callback arguments' => array('edit'),
72 'type' => MENU_CALLBACK,
73 );
74 $items[] = array(
75 'access' => user_access('administer case tracker'),
76 'callback' => 'drupal_get_form',
77 'callback arguments' => array('casetracker_work_admin_settings'),
78 'description' => t('Settings for casetracker work, timesheets.'),
79 'path' => 'admin/settings/casetracker_work',
80 'title' => t('Case Tracker work'),
81 );
82 }
83 return $items;
84 }
85
86 function casetracker_work_admin_settings() {
87 $form = array();
88 $form['casetracker_work'] = array(
89 '#type' => 'fieldset',
90 '#title' => t('General settings'),
91 '#collapsible' => TRUE,
92 '#collapsed' => FALSE,
93 );
94 $form['casetracker_work_locked'] = array(
95 '#type' => 'fieldset',
96 '#title' => t('Settings edited in settings.php'),
97 '#description' => t('You need to edit the $conf array in settings.php. (This will not be necessary in future versions.)'),
98 '#collapsible' => TRUE,
99 '#collapsed' => FALSE,
100 );
101
102 $vocabs = array(0 => '-- N/A --');
103 foreach (taxonomy_get_vocabularies() AS $v) {
104 $vocabs[$v->vid] = $v->name;
105 }
106
107 foreach (node_get_types() AS $type) {
108 $node_types[$type->type] = $type->name;
109 }
110
111 // $conf['casetracker_work_base_cost'] = 70;
112 // Currency
113 //$conf['casetracker_work_base_currency'] = 'AUD';
114 // Default rate if none for user
115 // $conf['casetracker_work_rate_default'] = 'L1';
116
117 $rates = casetracker_work_get_rates(true);
118
119 $form['casetracker_work']['casetracker_work_attach_types'] = array(
120 '#type' => 'select',
121 '#multiple' => TRUE,
122 '#title' => t('Relate work to'),
123 '#default_value' => variable_get('casetracker_work_attach_types', 'casetracker_basic_case'),
124 '#options' => $node_types,
125 '#description' => t('Work can be associated with these node types.'),
126 );
127 $form['casetracker_work']['casetracker_work_category'] = array(
128 '#type' => 'select',
129 '#title' => t('Categorize by taxonomy'),
130 '#autocomplete_path' => 'casetracker/autocomplete',
131 '#default_value' => variable_get('casetracker_work_category', 0),
132 '#options' => $vocabs,
133 '#description' => t('Allow categorization of work by this vocabulary. Note that casetracker_work will look for a default by looking at the parent node.'),
134 );
135 $options_time_types = array(
136 CT_WORK_STARTFIN => 'Start-Finish Only',
137 CT_WORK_DURATION => 'Duration Only',
138 CT_WORK_STARTFIN_AND => 'Start-Finish (Duration Optional)',
139 CT_WORK_DURATION_AND => 'Duration (Start-Finish Optional)',
140 );
141 $form['casetracker_work']['casetracker_work_time_type'] = array(
142 '#type' => 'select',
143 '#title' => t('Data entry type'),
144 '#default_value' => variable_get('casetracker_work_time_type', 0),
145 '#options' => $options_time_types,
146 '#description' => t('Options for layout of time data entry.'),
147 );
148
149 // These values are locked while they are set in settings.php. They will be
150 // unlocked once the rates logic is bedded down and the settings move to the database.
151 $form['casetracker_work_locked']['casetracker_work_rate_default'] = array(
152 '#type' => 'select',
153 '#title' => t('Default rate'),
154 '#disabled' => true,
155 '#options' => $rates,
156 '#default_value' => $rates[variable_get('casetracker_work_rate_default', '')],
157 '#description' => t('Rate to use by default (if rate not found for current user).'),
158 );
159 $form['casetracker_work_locked']['casetracker_work_base_cost'] = array(
160 '#type' => 'textfield',
161 '#size' => 10,
162 '#title' => 'Base cost/hr',
163 '#disabled' => true,
164 '#default_value' => variable_get('casetracker_work_base_cost', 0),
165 );
166 $form['casetracker_work_locked']['casetracker_work_base_currency'] = array(
167 '#type' => 'textfield',
168 '#size' => 10,
169 '#title' => 'Base currency',
170 '#disabled' => true,
171 '#default_value' => variable_get('casetracker_work_base_currency', ''),
172 );
173 return system_settings_form($form);
174 }
175
176 /*
177 * Get rates list.
178 * @param $options bool
179 * If for an form select list.
180 *
181 */
182 function casetracker_work_get_rates($options = false) {
183 $saved_rates = variable_get('casetracker_work_rates', array());
184 foreach ($saved_rates as $rate) {
185 $rates[$rate['code']] = ($options) ? $rate['title'] : $rate;
186 }
187 return $rates;
188 }
189
190 /*
191 * Catch the casetracker/work/edit path and direct to the main work entry routine.
192 *
193 */
194 function casetracker_work_edit() {
195 return casetracker_work_load(arg(3));
196 }
197
198 function casetracker_work_load($ctwid) {
199 return db_fetch_object(db_query('SELECT * FROM casetracker_work WHERE ctwid = %d', $ctwid));
200 }
201
202 function casetracker_work_entry($op, $arg1 = null) {
203 return drupal_get_form('casetracker_work_entry_form', $op, ($arg1) ? $arg1 : arg(3));
204 }
205
206
207 /*
208 * This is the main work entry add/edit (also called by casetracker_work_edit).
209 * @param $op string
210 * 'add' = a new entry, $values must be a nid
211 * 'edit' = edit an entry, $values must be a ctwid, or $values a work object by casetracker_work_load($ctwid)
212 * @param $arg1 mixed
213 * a work object or a nid or a ctwid depending on op.
214 *
215 * Form values: Database values:
216 * ------------ ----------------
217 * [ctwid] => 99 `ctwid` = 99
218 * [nid] => 9 `nid` = 9
219 * [activity] => Blah blah `activity` = Blah blah
220 * [date_start] => 2006-11-16 --
221 * [start] => 21:00 `start` = 1163088960
222 * [finish] => 22:00 `finish` = 1163092620 (`finish` = 0 if the entry is duration only)
223 * [date_finish] => 2006-11-16 --
224 * [duration] => 1:00 `duration` = 3600
225 * [uid] => Bon Jovi `uid` = 7
226 * [rate] => L1 `rate` = L1
227 *
228 */
229
230 function casetracker_work_entry_form($op, $arg1 = null) {
231
232 global $user;
233
234 // Prepare an array of the default values.
235 $defaults = array();
236
237 // Build some basic values
238 if ($op == 'edit') {
239 // Don't allow non admin to edit times
240 if (!user_access('manage work and rates')) {
241 // TODO: better control of editing old times. Need a lock field in the table.
242 return array();
243 }
244 $edit = true;
245 if (is_numeric($arg1)) {
246 $values = casetracker_work_load($arg1);
247 }
248 $defaults = (array)$values;
249 }
250 else {
251 if (!user_access('log work')) {
252 return array();
253 }
254 $edit = false;
255 $defaults['nid'] = $arg1;
256 $defaults['rate'] = (isset($user->casetracker_rate)) ? $user->casetracker_rate : variable_get('casetracker_work_rate_default', '');
257 $defaults['activity'] = '';
258 }
259
260 // Get the type and name of the default node
261 if (is_numeric($defaults['nid'])) {
262 $node = node_load($defaults['nid']);
263 }
264 if (!isset($node->type)) {
265 drupal_set_message('Error, unable to determine a parent node for this work entry.', 'error');
266 return array();
267 }
268
269 $defaults['title'] = $node->title;
270 $defaults['type'] = $node->type;
271 $defaults['type_name'] = node_get_types('name', $defaults['type']);
272
273 // Determine the project info if applicable
274 $defaults['project'] = 0;
275 if (casetracker_work_project_types($defaults['type'])) {
276 // Find the parent project for the taxonomy default and list of cases.
277 $defaults['project'] = db_result(db_query('SELECT pid FROM {casetracker_case} WHERE nid = %d', $defaults['nid']));
278 }
279
280 // Find a vocab to use if applicable
281 $vid = variable_get('casetracker_work_category', 0);
282
283 if ($vid) {
284 // We have a vocabulary associated with a work entry. Build the select list.
285 $term_options = array();
286 $result = db_query('SELECT td.tid, td.name FROM term_data AS td WHERE td.vid = %d ORDER BY weight, name', $vid);
287 while ($row = db_fetch_object($result)) {
288 $term_options[$row->tid] = $row->name;
289 }
290
291 if ($edit) {
292 $defaults['tid'] = $values->tid;
293 }
294 else {
295 // Look for a default term to apply to this work, by looking for an
296 // existing vocab/term on the node (usually the 'case' node).
297 // Define the query.
298 $get_term_qry =
299 'SELECT td.tid
300 FROM {node} AS n
301 INNER JOIN {term_node} AS tn ON n.nid = tn.nid
302 INNER JOIN {term_data} AS td ON tn.tid = td.tid
303 INNER JOIN {vocabulary} AS v ON td.vid = v.vid
304 WHERE n.nid = %d AND v.vid = %d';
305
306 // Look at the node to which we are attaching this work entry.
307 $defaults['tid'] = db_result( db_query($get_term_qry, $defaults['nid'], $vid));
308
309 // If the node type we are attaching to is a 'case' and we haven't found an
310 // appropriate term yet, look at the case's project node.
311 if (!$defaults['tid'] && $defaults['project']) {
312 $defaults['tid'] = db_result( db_query($get_term_qry, $defaults['project'], $vid));
313 }
314 }
315 }
316 /*
317 * Finish finding default term.
318 */
319
320 // Determine if this is a duration time entry.
321 if ($op == 'edit') {
322 $duration = ($values->finish == 0) ? TRUE : FALSE;
323 // Some extra logic to raise a watchdog warning if the entry being edited
324 // contravenes current settings. (Eg. settings allow only Duration,
325 // but we are editing a Start-finish entry from the database.
326 // After raising the warning, we make the data entry page look nice,
327 // however this will fail in the validation unless the user changes the
328 // values appropriately.
329 $time_type = variable_get('casetracker_work_time_type', CT_WORK_DURATION_AND);
330 if ($duration && ($time_type == CT_WORK_STARTFIN)) {
331 watchdog('casetracker', 'A work entry has been viewed/edited that is a Duration entry, however current casetracker_case settings stipulate that only Start-Finish entries are allowed', WATCHDOG_WARNING, l('view', 'casetracker/work/edit/'. $defaults['ctwid']));
332 $time_type = CT_WORK_STARTFIN_AND;
333 $duration = !$duration;
334 }
335 elseif (!$duration && ($time_type == CT_WORK_DURATION)) {
336 watchdog('casetracker', 'A work entry has been viewed/edited that is a Start-Finish entry, however current casetracker_case settings stipulate that only Start-Finish entries are allowed', WATCHDOG_WARNING, l('view', 'casetracker/work/edit/'. $defaults['ctwid']));
337 $time_type = CT_WORK_DURATION_AND;
338 $duration = !$duration;
339 }
340 }
341 else {
342 $time_type = variable_get('casetracker_work_time_type', CT_WORK_DURATION_AND);
343 $duration = ($time_type == CT_WORK_DURATION_AND || $time_type == CT_WORK_DURATION) ? TRUE : FALSE;
344 }
345
346 // Calculate date/time in timestamps/seconds.
347 // Use the $clock array to store the raw time data (database format).
348 // Then use the $input array to store the default visible data-entry values.
349
350 $clock = array();
351 if ($edit && $duration) {
352 // Editing a database duration entry.
353 $clock['start'] = $values->start;
354 $clock['duration'] = $values->duration;
355 }
356 elseif ($edit && !$duration) {
357 // Editing a database start-finish entry.
358 $clock['start'] = $values->start ;
359 $clock['finish'] = $values->finish;
360 }
361 elseif (!$edit && !$duration) {
362 // Editing a new start-finish entry.
363 $clock['start'] = _casetracker_work_time_round(time(), 'down', 'hour');
364 $clock['finish'] = $clock['start'] + (CT_WORK_HOUR);
365 }
366 else {
367 // Editing a new duration entry.
368 $clock['start'] = _casetracker_work_time_round(time(), 'down', 'hour');
369 $clock['duration'] = CT_WORK_HOUR;
370 }
371
372 // Calculate form values in human-readable format.
373 $input = array();
374 $input['date_start'] = format_date($clock['start'], 'custom', 'Y-m-d');
375 $input['start'] = ($duration) ? '' : format_date($clock['start'],'custom','H:i');
376 $input['finish'] = ($duration) ? '' : format_date($clock['finish'],'custom','H:i');
377 $input['date_finish'] = ($duration) ? '' : format_date($clock['finish'], 'custom', 'Y-m-d');
378 $input['duration'] = ($duration) ? _casetracker_work_seconds_display($clock['duration'], 'hh:mm') : '';
379
380 $form['work']['ctwid'] = array(
381 '#type' => 'hidden',
382 '#default_value' => $defaults['ctwid'],
383 );
384
385 $form['work']['nid'] = array(
386 '#type' => 'hidden',
387 '#default_value' => $defaults['nid'],
388 );
389 drupal_set_title(t('Add time to ') . t($defaults['type_name']));
390 $form['work']['activity'] = array(
391 '#type' => 'textfield',
392 '#title' => t('Activity summary'),
393 '#required' => TRUE,
394 '#default_value' => $values->activity,
395 '#rows' => 1,
396 '#maxlength' => 2048,
397 );
398
399 if ($vid) {
400 $form['work']['tid'] = array(
401 '#type' => 'select',
402 '#title' => t('Category'),
403 '#required' => TRUE,
404 '#options' => $term_options,
405 '#default_value' => $defaults['tid'],
406 );
407 }
408 else {
409 $form['work']['tid'] = array(
410 '#type' => 'hidden',
411 '#default_value' => 0,
412 );
413 }
414 $form['work']['rate'] = array(
415 '#type' => 'select',
416 '#title' => t('Rate'),
417 '#weight' => 11,
418 '#options' => casetracker_work_get_rates(true),
419 '#default_value' => ($edit) ? $defaults['rate'] : $user->casetracker_rate,
420 '#description' => t('Hourly rate based on agreed terms.'),
421 );
422 $form['work']['date_start'] = array(
423 '#type' => 'textfield',
424 '#title' => ($duration) ? t('Date') : t('Start date'),
425 '#required' => TRUE,
426 '#maxlength' => 12,
427 '#size' => 20,
428 '#default_value' => $input['date_start'],
429 '#description' => t('Enter yyyy-mm-dd'),
430 );
431 $form['work']['start'] = array(
432 '#type' => 'textfield',
433 '#title' => t('Start'),
434 '#maxlength' => 8,
435 '#size' => 20,
436 '#default_value' => $input['start'],
437 '#description' => t('Enter hh:mm'),
438 );
439 $form['work']['finish'] = array(
440 '#type' => 'textfield',
441 '#title' => t('Finish'),
442 '#maxlength' => 8,
443 '#size' => 20,
444 '#default_value' => $input['finish'],
445 '#description' => t('Enter hh:mm'),
446 );
447 $form['work']['date_finish'] = array(
448 '#type' => 'textfield',
449 '#title' => t('Finish date'),
450 '#maxlength' => 12,
451 '#size' => 20,
452 '#default_value' => $input['date_finish'],
453 '#description' => t('Enter yyyy-mm-dd'),
454 );
455 $form['work']['duration'] = array(
456 '#type' => 'textfield',
457 '#size' => 20,
458 '#title' => 'Duration',
459 '#default_value' => $input['duration'],
460 '#description' => t('Enter duration as <em>h:mm</em>, <em>mm</em>, <em>h,mm</em> etc.'),
461 );
462
463 $form['work']['extra'] = array(
464 '#type' => 'fieldset',
465 '#title' => t('Optional information'),
466 '#collapsible' => TRUE,
467 '#collapsed' => TRUE,
468 );
469
470 // Worker/rate are can be set
471 $form['work']['extra']['uid'] = array(
472 '#type' => 'textfield',
473 '#title' => t('Person'),
474 '#size' => 20,
475 '#weight' => 10,
476 '#disabled' => !user_access('manage work and rates'),
477 '#autocomplete_path' => 'casetracker/autocomplete',
478 '#default_value' => ($edit) ? casetracker_get_name($defaults->uid) : $user->name,
479 );
480
481 // Information for theming.
482 $form['theme']['#duration'] = $duration;
483 $form['theme']['#vid'] = $vid;
484 $form['theme']['#clock'] = $clock;
485 $form['theme']['#defaults'] = $defaults;
486
487 $form['work']['submit'] = array(
488 '#type' => 'submit',
489 '#value' => t('Save time'),
490 );
491
492 // Modify the form array based on the data-entry type specified
493 // in the casetracker_work settings.
494
495 // Modify form based on duration-entry only.
496 if ($time_type == CT_WORK_DURATION) {
497 $form['work']['duration']['#required'] = TRUE;
498 $form['work']['start']['#type'] = 'hidden';
499 $form['work']['finish']['#type'] = 'hidden';
500 $form['work']['date_finish']['#type'] = 'hidden';
501 }
502 // Modify form based on start-finish-entry only.
503 if ($time_type == CT_WORK_STARTFIN) {
504 $form['work']['start']['#required'] = TRUE;
505 $form['work']['finish']['#required'] = TRUE;
506 $form['work']['date_finish']['#required'] = TRUE;
507 $form['work']['duration']['#type'] = HIDDEN;
508 }
509 // Modify form based on duration-entry preferred.
510 if ($time_type == CT_WORK_DURATION_AND) {
511 // This won't be necessary when javascript is added to automatically
512 // remove the other values if duration is entered as an over-ride:
513 $form['work']['start']['#description'] = $form['work']['start']['#description'] .'<br /><strong>Note:</strong> To enter start-finish times instead of duration, you need to delete the value from the duration field';
514 $form['work']['extra']['start'] = $form['work']['start'];
515 $form['work']['extra']['finish'] = $form['work']['finish'];
516 $form['work']['extra']['date_finish'] = $form['work']['date_finish'];
517 unset($form['work']['start']);
518 unset($form['work']['finish']);
519 unset($form['work']['date_finish']);
520 }
521 // Modify form based on start-finish-entry preferred.
522 if ($time_type == CT_WORK_STARTFIN_AND) {
523 // This won't be necessary when javascript is added to automatically
524 // remove the other values if duration is entered as an over-ride:
525 $form['work']['duration']['#description'] = $form['work']['duration']['#description'] .'<br /><strong>Note:</strong> To enter a duration you need to delete the times from the Start & Finish Fields';
526 $form['work']['extra']['duration'] = $form['work']['duration'];
527 unset($form['work']['duration']);
528 }
529
530 return $form;
531 }
532
533 /*
534 */
535 function theme_casetracker_work_entry_form(&$form) {
536 /*
537 * Style here for development purposes
538 * move to appropriate style sheet later.(?)
539 */
540
541 // Themers: useful display data can found on the $form['theme'] element.
542
543 drupal_set_title('Record work on '. $form['theme']['#defaults']['type_name']);
544
545 $output .= '<div class="casetracker-work">';
546
547 $output .= '<h3>'. $form['theme']['#defaults']['title'] .'</h3>';
548
549 $rows = array();
550 if ($form['theme']['#duration']) {
551 $rows[1] = array();
552 if ($form['theme']['#vid']) {
553 $rows[2][] = drupal_render($form['work']['tid']);
554 }
555 $rows[2][] = drupal_render($form['work']['date_start']);
556 $rows[2][] = drupal_render($form['work']['duration']);
557 $rows[2][] = drupal_render($form['work']['rate']);
558 $rows[1][] = array('data' => drupal_render($form['work']['activity']), 'colspan' => count($rows[2]),);
559 }
560 else {
561 // First row.
562 $r = array();
563 if ($form['theme']['#vid']) {
564 $r[] = drupal_render($form['work']['tid']);
565 $r[] = array('data' => drupal_render($form['work']['activity']), 'colspan' => 3, );
566 }
567 else {
568 $r[] = array('data' => drupal_render($form['work']['activity']), 'colspan' => 4, );
569 }
570 $rows[] = $r;
571 // Second row.
572 $r = array();
573 $r[] = drupal_render($form['work']['date_start']);
574 $r[] = drupal_render($form['work']['start']);
575 $r[] = drupal_render($form['work']['finish']);
576 $r[] = drupal_render($form['work']['date_finish']);
577 $rows[] = $r;
578 }
579
580 $output .= theme('table', array(), $rows);
581
582 $output .= drupal_render($form);
583 $output .= '</div>';
584 return $output;
585 }
586
587
588 function casetracker_work_entry_form_validate($form_id, &$values) {
589
590 // TODO: make sure that worker and rate are default if user cannot administer resources
591
592 // Perform the conversions used by _submit which will trigger various errors.
593 _casetracker_work_string_to_timestamp($values['date_start'],'date');
594
595 // Check for either start-finish or duration scenario.
596 // Eg. don't test for an end-time if it's a duration entry only.
597 $enough_startfin = ($values['date_start'] && $values['start'] && $values['finish']) ? TRUE : FALSE;
598 $enough_duration = ($values['date_start'] && $values['duration']) ? TRUE : FALSE;
599
600 $date_start = _casetracker_work_string_to_timestamp($values['date_start'],'date');
601
602 switch (variable_get('casetracker_work_time_type', CT_WORK_DURATION_AND)) {
603 case CT_WORK_DURATION:
604 if (!$enough_duration) {
605 form_set_error('work', t('Date and duration required.'));
606 }
607 _casetracker_work_string_to_timestamp($values['duration'], 'duration');
608 break;
609
610 case CT_WORK_STARTFIN:
611 if (!$enough_startfin) {
612 form_set_error('work', t('Date and start/finish times are required.'));
613 }
614 $date_finish = _casetracker_work_string_to_timestamp($values['date_finish'],'date');
615 _casetracker_work_string_to_timestamp($values['start'], 'time', $date_start);
616 _casetracker_work_string_to_timestamp($values['finish'], 'time', $date_finish);
617 break;
618
619 case CT_WORK_DURATION_AND:
620 case CT_WORK_STARTFIN_AND:
621 if ($enough_duration) {
622 _casetracker_work_string_to_timestamp($values['duration'], 'duration');
623 }
624 elseif ($enough_startfin) {
625 $date_finish = _casetracker_work_string_to_timestamp($values['date_finish'],'date');
626 _casetracker_work_string_to_timestamp($values['start'], 'time', $date_start);
627 _casetracker_work_string_to_timestamp($values['finish'], 'time', $date_finish);
628 }
629 else {
630 form_set_error('work', (CT_WORK_DURATION_AND) ? t('CDate and duration required.') : t('DDate and start/finish times are required.'));
631 }
632 }
633 if (!casetracker_get_uid($values['uid'])) {
634 form_set_error('work', 'Invalid user name.');
635 }
636
637 }
638
639 function casetracker_work_entry_form_submit($form_id, &$values) {
640 // Determine which type of time entry we have.
641 // Assume values are already checked in _validate.
642
643 // Determine the type of entry we are making, "start-finish" or "duration".
644 $time_type = variable_get('casetracker_work_time_type', CT_WORK_DURATION_AND);
645
646 // Make a firm decision about the type of entry if we happen to be unsure.
647 switch ($time_type) {
648 case CT_WORK_DURATION_AND:
649 $enough_duration = ($values['date_start'] && $values['duration']) ? TRUE : FALSE;
650 $time_type = ($enough_duration) ? CT_WORK_DURATION : CT_WORK_STARTFIN;
651 break;
652 case CT_WORK_STARTFIN_AND:
653 $enough_startfin = ($values['date_start'] && $values['start'] && $values['finish']) ? TRUE : FALSE;
654 $time_type = ($enough_startfin) ? CT_WORK_STARTFIN : CT_WORK_DURATION;
655 break;
656 }
657
658 // We always need the start date.
659 $date_start = _casetracker_work_string_to_timestamp($values['date_start'],'date');
660
661 // Convert other strings to timestamps or seconds.
662 if ($time_type == CT_WORK_DURATION) {
663 $db['seconds'] = _casetracker_work_string_to_timestamp($values['duration'], 'duration');
664 $db['start'] = $date_start;
665 }
666 else {
667 $date_finish = _casetracker_work_string_to_timestamp($values['date_finish'],'date');
668 $db['start'] = _casetracker_work_string_to_timestamp($values['start'], 'time', $date_start);
669 $db['finish'] = _casetracker_work_string_to_timestamp($values['finish'], 'time', $date_finish);
670 $db['seconds'] = $db['finish'] - $db['start'];
671 }
672
673
674 $db['uid'] = casetracker_get_uid($values['uid']);
675
676 if ($values['ctwid'] == 0) {
677
678 db_query("INSERT INTO {casetracker_work}
679 (activity, nid, uid, tid, start, finish, duration, rate)
680 VALUES ('%s', %d, %d, %d, %d, %d, %d, '%s')",
681 $values['activity'], $values['nid'], $db['uid'], $values['tid'],
682 $db['start'], $db['finish'], $db['seconds'], $values['rate']);
683 }
684 else {
685 db_query("UPDATE {casetracker_work}
686 SET activity = '%s', nid = %d, uid = %d, tid = %d,
687 start = %d, finish = %d,
688 duration = %d, rate = '%s'
689 WHERE ctwid = %d",
690 $values['activity'], $values['nid'], $db['uid'], $values['tid'],
691 $db['start'], $db['finish'], $db['seconds'],
692 $values['rate'], $values['ctwid']);
693 }
694
695 drupal_goto('node/'. $values['nid']);
696 }
697
698 function casetracker_work_link($type, $node = NULL, $teaser = FALSE) {
699
700 if ($type != 'node' || !user_access('log work')) {
701 return;
702 }
703 $valid_types = variable_get('casetracker_work_attach_types', array('casetracker_basic_case' => 'casetracker_basic_case'));
704 if (isset($valid_types[$node->type])) {
705 $links = array();
706 $links['casetrack_work_add_work'] = array(
707 'href' => "casetracker/work/add/$node->nid",
708 'title' => t('Log time'),
709 );
710 }
711 return $links;
712 }
713
714 /**
715 * Implementation of hook_delete().
716 */
717 function casetracker_work_delete($node) {
718 db_query('DELETE FROM {casetracker_work} WHERE nid=%d', $node->casetracker_work->nid);
719 }
720
721 /*
722 * Time rounding for various uses, especially default form inputs
723 * for user convenience.
724 *
725 */
726
727 function _casetracker_work_time_round($timestamp, $op, $interval) {
728
729 $time_values = array('z','w','j','H','i','s');
730 $time_string = format_date($timestamp, 'custom', implode(',', $time_values));
731 $t = array_combine( $time_values, explode(',', $time_string) );
732
733 switch ($op) {
734 case 'down':
735 switch ($interval) {
736 case 'hour':
737 return $timestamp - ($t['i']*60) - $t['s'];
738 break;
739 default:
740 return 0;
741 }
742 default:
743 return 0;
744 }
745 }
746
747 /*
748 * Get hour minute second in an array for fine handling.
749 * If there is a $format argument then return the elements
750 * in the desired format.
751 *
752 * If there is a better function for this I'm interested.
753 * Core format_interval() seems too wordy.
754 * Avoid date() and format_date() to avoid timezone shifts.
755 *
756 */
757 function _casetracker_work_seconds_display($seconds, $format = 'array') {
758
759 $time['day'] = (int)($seconds / CT_WORK_DAY);
760 $seconds = ($seconds % CT_WORK_DAY);
761 $time['hour'] = (int)($seconds / CT_WORK_HOUR);
762 $seconds = ($seconds % CT_WORK_HOUR);
763 $time['minute'] = (int)($seconds / 60);
764 $time['second'] = ($seconds % 60);
765
766 switch ($format) {
767 case 'array':
768 return $time;
769 break;
770 case 'hh:mm':
771 // Nice and clear: format seconds in hh:mm without timezone effects.
772 return ($time['day'] * 24) + $time['hour'] .':'. str_pad($time['minute'], 2, "0", STR_PAD_LEFT);
773 break;
774 }
775 }
776
777 /*
778 * We want to give the user flexibility entering data,
779 * so we expect a narrow range of formats and a wide range of
780 * delimiters.
781 */
782 function _casetracker_work_string_to_timestamp($time_string, $op, $date_timestamp = NULL) {
783
784 if ($op == 'time' && (!isset($date_timestamp) || !is_numeric($date_timestamp))) {
785 // dpr(ddebug_backtrace()); die();
786 form_set_error ('work', t('API error. Missing the date parameter used for creating a timestamp from hh:mm.'));
787 return 0;
788 }
789
790 $chars = array(':', ',', '.', '-', '/', ' ');
791 $time_string = str_replace($chars, ',', $time_string);
792 $time_string = str_replace(',,', ',', $time_string);
793
794 if ($op == 'duration' && strpos($time_string, ',') === false) {
795 $time_string = ','. $time_string;
796 }
797
798 $t = explode(',', $time_string);
799
800 switch ($op) {
801 case 'date':
802 // Accept only yyyymmdd for now
803 // TODO: user setting for preferred format.
804 if (count($t) != 3 || $t[0] < 2000 || $t[1] > 12 || $t[2] > 31) {
805 form_set_error ('work', t('Please use yyyy/mm/dd format for dates.'));
806 return 0;
807 }
808 else {
809 // TODO: Make this local time, not GMT
810 global $user;
811 return gmmktime (0, 0, 0, $t[1], $t[2], $t[0]) - $user->timezone;
812 }
813
814 case 'duration':
815 // One value is minutes, two values is hour, minutes
816 if ($t[1] > 59 || count($t) > 2) {
817 form_set_error ('work', t('Please enter duration only in hh:mm format, even for multiple days.'));
818 return 0;
819 }
820 else {
821 return ($t[0] * CT_WORK_HOUR) + ($t[1] * 60);
822 }
823 case 'time':
824 // Only accept two values being hour, minutes.
825 if ($t[0] > 23 || $t[1] > 59 || count($t) != 2) {
826 form_set_error ('work', 'Time should be hh:mm in 24hr format only.');
827 return 0;
828 }
829 else {
830 return $date_timestamp + ($t[0] * CT_WORK_HOUR) + ($t[1] * 60);
831 }
832 }
833
834 return $time;
835 }
836
837 // Simple utility for checking a project type. Should be supplied by ct
838 function casetracker_work_project_types($check_type = null) {
839 $types = variable_get('casetracker_project_node_types', 'casetracker_basic_project');
840 foreach ($types as $t => $type) {
841 if (!$type) {
842 unset($types[$t]);
843 }
844 }
845 if ($check_type) {
846 return isset($types[$check_type]);
847 }
848 return $types;
849 }
850
851 function casetracker_work_allow_edit($ctwid = 0) {
852 // if a manager, then allow
853 return user_access('manage work and rates');
854 // if a owner then allow if not locked
855 }
856
857 // ----------------------------------------------------------------------------
858 // Views hooks
859
860 /**
861 * Implementation of hook_views_tables()
862 */
863 function casetracker_work_views_tables() {
864 $tables['casetracker_work'] = array(
865 'name' => 'casetracker_work',
866 'join' => array(
867 'left' => array(
868 'table' => 'node',
869 'field' => 'nid'
870 ),
871 'right' => array(
872 'field' => 'nid'
873 )
874 ),
875 'fields' => array(
876 'ctwid' => array(
877 'name' => t('Work: Work ID'),
878 ),
879 'uid' => array(
880 'name' => t('Work: User'),
881 'handler' => 'casetracker_work_handler_user_name',
882 'sortable' => true,
883 'help' => t('Display the user of this work entry.'),
884 ),
885 'start' => array(
886 'name' => t('Work: Start'),
887 'sortable' => true,
888 'handler' => views_handler_field_dates(),
889 'option' => 'string',
890 'help' => t('Display the work start time.'),
891 ),
892 'duration' => array(
893 'name' => t('Work: Duration'),
894 'handler' => 'casetracker_work_handler_duration',
895 'help' => t('Display the work duration.'),
896 ),
897 'rate' => array(
898 'name' => t('Work: Rate'),
899 'sortable' => true,
900 ),
901 'activity' => array(
902 'name' => t('Work: Activity'),
903 ),
904 'edit' => array(
905 'name' => t('Work: Edit link'),
906 'handler' => 'casetracker_work_handler_edit_link',
907 'notafield' => TRUE,
908 'addlfields' => array('ctwid'),
909 'help' => t('Display a link to edit work entry.'),
910 ),
911 ),
912 'sorts' => array(
913 'sku' => array(
914 'name' => t('Product: SKU'),
915 ),
916 'price' => array(
917 'name' => t('Product: Price'),
918 )
919 ),
920 'filters' => array(
921 'has_work' => array(
922 'name' => 'Work: Has work',
923 'operator' => array('=' => 'Equals'),
924 'list' => 'views_handler_operator_yesno',
925 'list-type' => 'select',
926 'handler' => 'casetracker_work_handler_filter_has_work',
927 ),
928 'uid' => array(
929 'help' => t('Filter by user.'),
930 'list' => 'casetracker_work_handler_user_options',
931 'name' => t('Work: User'),
932 'operator' => views_handler_operator_andor(),
933 'value-type' => 'array',
934 ),
935 'assign_to_currentuid' => array(
936 'list' => 'views_handler_filter_usercurrent',
937 'list-type' => 'select',
938 'name' => t('Work: Current user'),
939 'field' => 'uid',
940 'operator' => 'views_handler_operator_eqneq',
941 'help' => t('Filter work by the current user.'),
942 ),
943 ),
944 );
945 return $tables;
946 }
947
948 /**
949 * display a link to edit a work entry with a destination return
950 */
951 function casetracker_work_handler_edit_link($fieldinfo, $fielddata, $value, $data) {
952 // try to build a fake node object
953 if (casetracker_work_allow_edit()) {
954 $link_text = t('Edit');
955 return l($link_text, "casetracker/work/edit/{$data->casetracker_work_ctwid}", NULL, drupal_get_destination());
956 }
957 }
958
959 /**
960 * Show the work entry user
961 */
962 function casetracker_work_handler_user_name($fieldinfo, $fielddata, $value, $data) {
963 return l(casetracker_get_name($data->casetracker_work_uid), "user/{$data->casetracker_work_uid}");
964 }
965
966 /**
967 * Format duration.
968 */
969 function casetracker_work_handler_duration($fieldinfo, $fielddata, $value, $data) {
970 return _casetracker_work_seconds_display($data->casetracker_work_duration, 'hh:mm');
971 }
972
973 function casetracker_work_handler_user_options() {
974 $results = db_query('SELECT u.uid, u.name FROM {users} AS u INNER JOIN {casetracker_work} AS ctw ON u.uid = ctw.uid WHERE u.uid > 0 ORDER BY u.name');
975 while ($result = db_fetch_object($results)) {
976 $options[$result->uid] = check_plain($result->name);
977 }
978 return $options;
979 }
980
981
982 /**
983 * Filter Nodes based on being a product.
984 */
985 function casetracker_work_handler_filter_has_work($op, $filter, $filterinfo, &$query) {
986 switch ($op) {
987 case 'handler':
988 $query->ensure_table('casetracker_work');
989 switch ($filter['value'][0]) {
990 case '0':
991 $query->add_where('casetracker_work.ctwid IS NULL');
992 break;
993
994 case '1':
995 $query->add_where('casetracker_work.ctwid IS NOT NULL');
996 break;
997 }
998 break;
999 }
1000 }
1001
1002
1003 /*
1004 * Timesheet display, just a simple search results. This will suffice for a
1005 * short time.
1006 *
1007 * Maybe will be a good idea to separate function to return a range of data,
1008 * so that this can be called from anywhere (eg. case summary).
1009 *
1010 * I don't know whether views can be used to display this info.
1011 *
1012 * Not showing project data here, because casetracker_work lets you assign
1013 * work to multiple node types, so project data might not be appropriate.
1014 *
1015 function casetracker_work_report_temp() {
1016
1017 global $user;
1018
1019 if (!user_access('manage work and rates')) {
1020 $uid = $user->uid;
1021 } elseif (!($uid = arg(2))) {
1022 $uid = $user->uid;
1023 }
1024
1025 $header = array(t('ID'), t('Date'), t('Start'), t('Finish'), t('Time'), t('Rate'), t('Activity'), t('Node'));
1026
1027 $result = db_query(
1028 'SELECT cw.*, n.type, u.name
1029 FROM casetracker_work AS cw
1030 INNER JOIN node AS n
1031 ON cw.nid = n.nid
1032 INNER JOIN users AS u
1033 ON cw.uid = u.uid
1034 WHERE cw.uid = %d
1035 ORDER BY cw.start',
1036 $uid);
1037
1038 while ($work = db_fetch_object($result)) {
1039
1040 $time = _casetracker_work_seconds_display($work->duration, 'hh:mm');
1041 $type_name = node_get_name($work->type);
1042 $start = ($work->finish == 0) ? '' : format_date($work->start, 'custom', 'H:i');
1043 $finish = ($work->finish == 0) ? '' : format_date($work->finish, 'custom', 'H:i');
1044 $name = $work->name;
1045
1046 $rows[] =
1047 array(
1048 l($work->ctwid, 'casetracker/work/edit/'. $work->ctwid),
1049 format_date($work->start, 'custom', 'Y M d'),
1050 $start,
1051 $finish,
1052 $time,
1053 $work->rate,
1054 $work->activity,
1055 l($work->nid, 'node/'. $work->nid),
1056 );
1057
1058 $total_time += $work->duration;
1059 }
1060
1061 drupal_set_title('Timesheet for '. $name);
1062
1063 $output .= theme('table', $header, $rows, array('class' => 'casetracker_timesheet'));
1064 $time = _casetracker_work_seconds_display($total_time, 'hh:mm');
1065
1066 $output .= '<p><b>Total time</b>: '. $time .'</p>';
1067 // $output .= '<p>'. l('More work', 'node/add/casetracker_work') .'</p>';
1068
1069 return $output;
1070
1071 }
1072 */

  ViewVC Help
Powered by ViewVC 1.1.2