first commit
[step2_drupal.git] / casetracker / casetracker.module
blob4762687be25a60f0931295ec95eda7dc6db695c2
1 <?php
2 // $Id: casetracker.module,v 1.123.2.6 2009/03/15 23:27:39 jmiccolis Exp $
4 /**
5  * @file
6  * Enables the handling of projects and their cases.
7  */
9 /**
10  * Implementation of hook_views_api().
11  */
12 function casetracker_views_api() {
13   return array('api' => 2);
16 /**
17  * Implementation of hook_help().
18  */
19 function casetracker_help($path, $arg) {
20   switch ($path) {
21     case 'admin/settings/casetracker/states':
22       return '<p>'. t('Current Case Tracker case states are listed below.') .'</p>';
23     case 'admin/settings/casetracker/states/add':
24       return '<p>'. t('You may add a new case state below.') .'</p>';
25     case 'admin/settings/casetracker/states/edit/'. arg(4):
26       return '<p>'. t('You may edit an existing case state below.') .'</p>';
27     case 'admin/settings/casetracker':
28       return '<p>'. t('Configure the various Case Tracker options with these settings.') .'</p>';
29   }
32 /**
33  * Implementation of hook_perm().
34  */
35 function casetracker_perm() {
36   return array(
37     'access case tracker',
38     'administer case tracker',
39   );
42 /**
43  * Implementation of hook_menu().
44  */
45 function casetracker_menu() {
46   /* casetracker main settings */
47   $items['admin/settings/casetracker'] = array(
48     'file' => 'casetracker_admin.inc',
49     'access arguments' => array('administer case tracker'),
50     'page callback' => 'drupal_get_form',
51     'page arguments' => array('casetracker_settings'),
52     'description' => 'Configure the various Case Tracker options with these settings.',
53     'title' => 'Case Tracker',
54     'type' => MENU_NORMAL_ITEM,
55   );
56   $items['admin/settings/casetracker/settings'] = array(
57     'file' => 'casetracker_admin.inc',
58     'access arguments' => array('administer case tracker'),
59     'page callback' => 'drupal_get_form',
60     'page arguments' => array('casetracker_settings'),
61     'title' => 'Settings',
62     'weight' => -10,
63     'type' => MENU_DEFAULT_LOCAL_TASK,
64   );
65   /* casetracker state handling */
66   $items['admin/settings/casetracker/states'] = array(
67     'file' => 'casetracker_admin.inc',
68     'access arguments' => array('administer case tracker'),
69     'page callback' => 'casetracker_case_state_overview',
70     'type' => MENU_LOCAL_TASK,
71     'title' => 'Case states',
72     'description' => 'Add, edit and delete Case States, Types and Priorities',
73   );
74   $items['admin/settings/casetracker/states/list'] = array(
75     'file' => 'casetracker_admin.inc',
76     'access arguments' => array('administer case tracker'),
77     'page callback' => 'casetracker_case_state_overview',
78     'type' => MENU_DEFAULT_LOCAL_TASK,
79     'title' => 'Overview',
80     'weight' => -10,
81     'description' => 'Add, edit and delete Case States, Types and Priorities',
82   );
83   $items['admin/settings/casetracker/states/add'] = array(
84     'file' => 'casetracker_admin.inc',
85     'access arguments' => array('administer case tracker'),
86     'page callback' => 'drupal_get_form',
87     'page arguments' => array('casetracker_case_state_edit'),
88     'title' => 'Add case state',
89     'type' => MENU_LOCAL_TASK,
90   );
91   $items['admin/settings/casetracker/states/edit/%casetracker_case_state'] = array(
92     'file' => 'casetracker_admin.inc',
93     'access arguments' => array('administer case tracker'),
94     'page callback' => 'drupal_get_form',
95     'page arguments' => array('casetracker_case_state_edit', 5),
96     'title' => 'Edit case state',
97     'type' => MENU_CALLBACK,
98   );
99   $items['admin/settings/casetracker/states/delete/%casetracker_case_state'] = array(
100     'file' => 'casetracker_admin.inc',
101     'access arguments' => array('administer case tracker'),
102     'page callback' => 'drupal_get_form',
103     'page arguments' => array('casetracker_case_state_confirm_delete', 5),
104     'title' => 'Delete case state',
105     'type' => MENU_CALLBACK,
106   );
107   /* casetracker autocomplete */
108   $items['casetracker_autocomplete'] = array(
109     'title' => 'Case Tracker autocomplete',
110     'page callback' => 'casetracker_autocomplete',
111     'access callback' => 'user_access',
112     'access arguments' => array('access case tracker'),
113     'type' => MENU_CALLBACK,
114   );
115   return $items;
119  * Implementation of hook_nodeapi().
120  */
121 function casetracker_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
122   // CASES
123   if (casetracker_is_case($node->type)) {
124     switch ($op) {
126       case 'delete':
127         // delete case and its comments.
128         $comment_results = db_query("SELECT cid FROM {comments} WHERE nid = %d", $node->nid);
129         while ($comment_result = db_fetch_object($comment_results)) {
130           db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment_result->cid);
131         }
132         db_query('DELETE FROM {casetracker_case} WHERE nid = %d', $node->nid);
133         break;
135       case 'insert':
136         // $node->casetracker is an Array and we wanna it to be an object. I
137         // guess it's a nasty workaround.
138         $node->casetracker = (object) $node->casetracker;
140         // cases: generate a case ID and send it along.
141         $record = $node->casetracker;
142         $record->assign_to = casetracker_get_uid($record->assign_to);
143         $record->nid = $node->nid;
144         $record->vid = $node->vid;
146         drupal_write_record('casetracker_case', $record);
147         break;
149       case 'load':
150         $casetracker = db_fetch_object(db_query('SELECT pid, case_priority_id, case_type_id, assign_to, case_status_id FROM {casetracker_case} WHERE nid = %d AND vid = %d', $node->nid, $node->vid));
151         if ($casetracker) {
152           return array('casetracker' => $casetracker);
153         }
154         break;
156       case 'update':
157         $record = (object) $node->casetracker;
158         $record->assign_to = casetracker_get_uid($record->assign_to);
159         $record->nid = $node->nid;
160         $record->vid = $node->vid;
162         $primary = $node->revision ? array('nid') : array('nid', 'vid');
163         drupal_write_record('casetracker_case', $record, $primary);
164         break;
166       case 'view':
167         // used in the breadcrumb and our theme function, mostly for nid and project number display.
168         $project = node_load($node->casetracker->pid);
170         if ($page) {
171           $trail = array(
172             l(t('Home'), NULL),
173             l(t('Case Tracker'), 'casetracker/projects'),
174             l($project->title, "node/{$node->casetracker->pid}"),
175             l(t('All cases'), "casetracker/cases/{$node->casetracker->pid}/all"),
176           );
177           drupal_set_breadcrumb($trail);
178         }
180         $node->content['casetracker_case_summary'] = array(
181           '#value' => theme('casetracker_case_summary', $node, $project),
182           '#weight' => -10
183         );
184         break;
186     }
187   }
188   // PROJECTS
189   else if (casetracker_is_project($node->type)) {
190     switch ($op) {
192       case 'delete':
193         // projects: delete all the cases under the project and all the comments under each case.
194         $case_results = db_query("SELECT nid from {casetracker_case} WHERE pid = %d", $node->nid);
195         while ($case_result = db_fetch_object($case_results)) {
196           db_query("DELETE FROM {casetracker_case} WHERE nid = %d", $case_result->nid);
197           $comment_results = db_query("SELECT cid FROM {comments} WHERE nid = %d", $case_result->nid);
198           while ($comment_result = db_fetch_object($comment_results)) {
199             db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment_result->cid);
200           }
201           node_delete($case_result->nid); // this'll handle comment deletion too.
202         }
203         break;
205       case 'view':
206         if ($page) {
207           $trail = array(
208             l(t('Home'), NULL),
209             l(t('Case Tracker'), 'casetracker/projects'),
210           );
211           drupal_set_breadcrumb($trail);
212         }
213       
214         $node->content['casetracker_project_summary'] = array('#value' => theme('casetracker_project_summary', $node), '#weight' => -10);
215         break;
216     }
217   }
221  * Implementation of hook_comment().
222  */
223 function casetracker_comment(&$comment, $op) {
224   // Load the node here anyway -- it is almost certainly static cached already.
225   $node = is_array($comment) ? node_load($comment['nid']) : node_load($comment->nid);
227   // Bail if this is not a casetracker node.
228   if (!casetracker_is_case($node->type)) {
229     return;
230   }
232   if ($op == 'insert' || $op == 'update') {
233     $new = (object) $comment['casetracker'];
234     $new->cid = $comment['cid'];
235     $new->nid = $comment['nid'];
236     $new->vid = $comment['revision_id'];
237     $new->state = 1;
238     $new->assign_to = casetracker_get_uid($new->assign_to);
240     // Populate old state values from node
241     $old = $node->casetracker;
242     $old->cid = $comment['cid'];
243     $old->state = 0;
245     drupal_write_record('casetracker_case', $new, array('nid', 'vid'));
246   }
248   switch ($op) {
249     case 'insert':
250       drupal_write_record('casetracker_comment_status', $old);
251       drupal_write_record('casetracker_comment_status', $new);
252       break;
253     case 'update':
254       drupal_write_record('casetracker_comment_status', $old, array('cid', 'state'));
255       drupal_write_record('casetracker_comment_status', $new, array('cid', 'state'));
256       break;
257     case 'delete':
258       // @todo theoretically, if you delete a comment, we should reset all the values
259       // to what they were before the comment was submitted. this doesn't happen yet.
260       db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
261       break;
262     case 'view':
263       $results = db_query("SELECT * FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
264       while ($result = db_fetch_object($results)) {
265         $state = $result->state ? 'new' : 'old';
266         $case_data[$state] = $result;
267       }
268       $comment->comment = theme('casetracker_comment_changes', $case_data['old'], $case_data['new']) . $comment->comment;
269       break;
270   }
274  * Implementation of hook_form_alter().
275  */
276 function casetracker_form_alter(&$form, &$form_state, $form_id) {
277   if (!empty($form['#node'])) {
278     $node = $form['#node'];
280     // Add case options to our basic case type.
281     if (casetracker_is_case($node->type)) {
282       $count = count(casetracker_project_options());
283       if ($count == 0) {
284         drupal_set_message(t('You must create a project before adding cases.'), 'error');
285         return;
286       }
287       else {
288         $default_project = null;
289         if (!isset($form['#node']->nid) && is_numeric(arg(3))) {
290           $default_project = arg(3);
291         }
292         casetracker_case_form_common($form, $default_project);
293       }
294     }
295   }
299  * Implementation of hook_form_comment_form_alter().
300  */
301 function casetracker_form_comment_form_alter(&$form, &$form_state) {
302   $node = isset($form['nid']['#value']) ? node_load($form['nid']['#value']) : NULL;
303   if (casetracker_is_case($node->type)) {
304     $form['#node'] = $node;
306     // add case options to the comment form.
307     casetracker_case_form_common($form);
309     // necessary for our casetracker_comment() callback.
310     $form['revision_id'] = array('#type' => 'hidden', '#value' => $node->vid);
311   }
315  * Common form elements for cases, generic enough for use either in
316  * a full node display, or in comment displays and updating. Default
317  * values are calculated based on an existing $form['nid']['#value'].
319  * @param $form
320  *   A Forms API $form, as received from a hook_form_alter().
321  * @param $default_project
322  *   The project ID that should be pre-selected.
323  * @return $form
324  *   A modified Forms API $form.
325  */
326 function casetracker_case_form_common(&$form, $default_project = NULL) {
327   global $user;
329   $node = $form['#node'];
331   // project to set as the default is based on how the user got here.
332  if (empty($default_project) && !empty($node->casetracker->pid)) {
333     $default_project = $node->casetracker->pid;
334   }
336   $project_options = casetracker_project_options();
338   $form['casetracker'] = array(
339     '#type' => 'fieldset',
340     '#title' => t('Case information'),
341     '#weight' => -10,
342     '#collapsible' => TRUE,
343     '#collapsed' => FALSE,
344     '#tree' => TRUE,
345     '#theme' => 'casetracker_case_form_common',
346   );
348   // if there's no project ID from the URL, or more than one project,
349   // we'll create a select menu for the user; otherwise, we'll save
350   // the passed (or only) project ID into a hidden field.
351   if (count($project_options) > 1) {
352     $form['casetracker']['pid'] = array(
353       '#title' => t('Project'),
354       '#type' => 'select',
355       '#default_value' => $default_project,
356       '#options' => $project_options,
357     );
358   }
359   else {
360     $form['casetracker']['pid'] = array(
361       '#type' => 'value', // default value, or the only the project ID in the project_options array.
362       '#value' => !empty($default_project) ? $default_project : key($project_options),
363     );
364   }
366   $options = casetracker_user_options();
368   $form['casetracker']['assign_to'] = array(
369     '#type' => 'textfield',
370     '#title' => t('Assign to'),
371     '#autocomplete_path' => 'casetracker_autocomplete',
372     '#required' => TRUE,
373     '#size' => 12,
374   );
375   if (count($options) < 20) {
376     $normalized = array();
377     foreach ($options as $name) {
378       $normalized[$name] = $name;
379     }
380     $form['casetracker']['assign_to'] = array(
381     '#type' => 'radios',
382     '#title' => t('Assign to'),
383     '#required' => TRUE,
384     '#options' => $normalized,
385     );
386   }
388   if (isset($node->casetracker->assign_to)) {
389     $form['casetracker']['assign_to']['#default_value'] = is_numeric($node->casetracker->assign_to) ? casetracker_get_name($node->casetracker->assign_to) : $node->casetracker->assign_to;
390   }
391   else {
392     $form['casetracker']['assign_to']['#default_value'] = variable_get('casetracker_default_assign_to', variable_get('anonymous', t('Anonymous')));
393   }
395   $case_status_options = casetracker_realm_load('status');
396   $default_status = !empty($node->casetracker->case_status_id) ? $node->casetracker->case_status_id : variable_get('casetracker_default_case_status', key($case_status_options));
397   $form['casetracker']['case_status_id'] = array(
398     '#type' => 'select',
399     '#title' => t('Status'),
400     '#options' => $case_status_options,
401     '#default_value' => $default_status,
402   );
404   $case_priority_options = casetracker_realm_load('priority');
405   $default_priority = !empty($node->casetracker->case_priority_id) ? $node->casetracker->case_priority_id : variable_get('casetracker_default_case_priority', key($case_priority_options));
406     $form['casetracker']['case_priority_id'] = array(
407     '#type' => 'select',
408     '#title' => t('Priority'),
409     '#options' => $case_priority_options,
410     '#default_value' => $default_priority,
411   );
413   $case_type_options = casetracker_realm_load('type');
414   $default_type = !empty($node->casetracker->case_type_id) ? $node->casetracker->case_type_id : variable_get('casetracker_default_case_type', key($case_type_options));
415   $form['casetracker']['case_type_id'] = array(
416     '#type' => 'select',
417     '#title' => t('Type'),
418     '#options' => $case_type_options,
419     '#default_value' => $default_type,
420   );
422   return $form;
426  * CASE STATE CRUD ====================================================
427  */
430  * Returns information about the various case states and their options.
431  * The number of parameters passed will determine the return value.
433  * @param $csid
434  *   Optional; the state ID to return from the passed $realm.
435  * @param $realm
436  *   Optional; the name of the realm ('status', 'priority', or 'type').
437  * @param $reset
438  *   Optional; set to TRUE to reset the static cache.
440  * @return $values
441  *   If only $realm is passed, you'll receive an array with the keys
442  *   being the state ID and the values being their names. If a $csid
443  *   is also passed, you'll receive just a string of the state name.
444  *   If ONLY a $csid is passed, we'll return a list of 'name', 'realm'.
445  */
446 function casetracker_case_state_load($csid = NULL, $realm = NULL, $reset = FALSE) {
447   static $states_lookup;
449   if (!$states_lookup || $reset) {
450     $results = db_query("SELECT csid, case_state_name AS name, case_state_realm AS realm, weight 
451                          FROM {casetracker_case_states} ORDER BY weight");
452     $states_lookup = array();
453     while ($row = db_fetch_object($results)) {
454       $states_lookup[$row->realm][$row->csid] = $states_lookup['all'][$row->csid] = $row;
455     }
456   }
458   if ($csid && $realm) {
459     return $states_lookup['all'][$csid]->name;
460   }
461   elseif ($csid && !$realm) {
462     return $states_lookup['all'][$csid];
463   }
464   elseif (!$csid && $realm) {
465     $options = array(); // suitable for form api.
466     if (!empty($states_lookup[$realm])) {
467       foreach ($states_lookup[$realm] as $state) {
468         $options[$state->csid] = $state->name;
469       }
470     }
471     return $options;
472   }
476  * Load states for a particular realm. Wrapper around casetracker_case_state_load()
478  * @param $realm
479  *   Name of the realm ('status', 'priority', or 'type').
480  * @return
481  *   array with the keys being the state ID and the values being their names.
482  */
483 function casetracker_realm_load($realm) {
484   return casetracker_case_state_load(null, $realm);
488  * Saves a case state.
490  * @param $case_state
491  *   An array containing 'name' and 'realm' keys. If no 'csid'
492  *   is passed, a new state is created, otherwise, we'll update
493  *   the record that corresponds to that ID.
494  */
495 function casetracker_case_state_save($case_state = NULL) {
496   if (!$case_state['name'] || !$case_state['realm']) { 
497     return NULL; 
498   }
499   // Need to collect information into another array since the db columns have different names : (
500   $record = array(
501     'case_state_name' => $case_state['name'],
502     'case_state_realm' => $case_state['realm'],
503     'weight' => $case_state['weight'],
504   );
505   if (isset($case_state['csid'])) {
506     $record['csid'] = $case_state['csid']; 
507     drupal_write_record('casetracker_case_states', $record, array('csid'));
508   }
509   else {
510     drupal_write_record('casetracker_case_states', $record);
511   }
512   return $result;
516  * Deletes a case state.
518  * @todo There is currently no attempt to do anything with cases which
519  * have been assigned the $csid that is about to be deleted. We should
520  * reset them to the default per our settings (and warn the user on our
521  * confirmation page), or something else entirely.
522  * 
523  * @param $csid
524  *   The case state ID to delete.
525  */
526 function casetracker_case_state_delete($csid = NULL) {
527   if (!empty($csid)) {
528     db_query('DELETE FROM {casetracker_case_states} WHERE csid = %d', $csid);
529   }
533  * COMMENT DISPLAY ====================================================
534  */
537  * Retrieve a pipe delimited string of autocomplete suggestions for existing
538  * users. Stolen from user_autocomplete. Eventually this will be expanded to
539  * include OG specific users subscribed to a project.
540  */
541 function casetracker_autocomplete($string) {
542   $matches = array();
543   $group = module_exists('og') ? og_get_group_context() : NULL;
544   if ($group) {
545     $gid = $group->nid;
546     $result = db_query_range("SELECT u.name FROM {users} u LEFT JOIN {og_uid} ou ON u.uid = ou.uid WHERE LOWER(name) LIKE LOWER('%s%%') AND u.status > 0 AND ou.nid = %d", $string, $gid, 0, 10);
547   }
548   else {
549     $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string, 0, 10);
550   }
551   while ($user = db_fetch_object($result)) {
552     $matches[$user->name] = check_plain($user->name);
553   }
554   drupal_json($matches);
558  * Returns an query string needed in case of Organic Groups
559  * providing preselected audience checkboxes for projects as groups (og)
560  * 
561  * @param   object  CT project
562  * @return  string
563  */
564 function _casetracker_get_og_query_string(&$project) {
565   $querystring = array();
566   // checking if project is group
567   if ($project->type == 'group') {
568     $querystring[] = 'gids[]='. $project->nid;
569     //checking if group-project is part of another group
570     if (isset($project->og_groups) 
571       && is_array($project->og_groups) 
572     ) {
573       foreach ($project->og_groups as $group) {
574         $querystring[] = 'gids[]='. $group;
575       }
576     }
577   }
578   //checking if project is part of a group
579   elseif (isset($project->og_groups) 
580     && is_array($project->og_groups) 
581     && $project->type !== 'group'
582   ) {
583     foreach ($project->og_groups as $group) {
584       $querystring[] = 'gids[]='. $group;
585     }
586   }
588   return (0 < count($querystring))
589       ? implode('&', $querystring)
590       : NULL;
594  * THEME ==============================================================
595  */
598  * Implementation of hook_theme
599  */
600 function casetracker_theme() {
601   return array(
602     'casetracker_comment_changes' => array(),
603     'casetracker_case_form_common' => array(),
604     'casetracker_case_summary' => array(),
605     'casetracker_project_summary' => array(),
606   );
610  * Displays the changes a comment has made to the case fields.
612  * @param $case_data
613  *   An array of both 'old' and 'new' objects that contains
614  *   the before and after values this comment has changed.
615  */
616 function theme_casetracker_comment_changes($old, $new) {
617   $rows = array();
619   $fields = array(
620     'pid' => t('Project'),
621     'title' => t('Title'),
622     'case_status_id' => t('Status'),
623     'assign_to' => t('Assigned'),
624     'case_priority_id' => t('Priority'),
625     'case_type_id' => t('Type'),
626   );
627   foreach ($fields as $field => $label) {
628     if ($new->{$field} != $old->{$field}) {
629       switch ($field) {
630         case 'pid':
631           $old_title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $old->pid));
632           $new_title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $new->pid));
633           $old->{$field} = l($old_title, "node/{$old->pid}");
634           $new->{$field} = l($new_title, "node/{$new->pid}");
635           break;
636         case 'case_status_id':
637           $old->{$field} = casetracker_case_state_load($old->{$field}, 'status');
638           $new->{$field} = casetracker_case_state_load($new->{$field}, 'status');
639           break;
640         case 'assign_to':
641           $old->{$field} = casetracker_get_name($old->{$field});
642           $new->{$field} = casetracker_get_name($new->{$field});
643           break;
644         case 'case_priority_id':
645           $old->{$field} = casetracker_case_state_load($old->{$field}, 'priority');
646           $new->{$field} = casetracker_case_state_load($new->{$field}, 'priority');
647           break;
648         case 'case_type_id':
649           $old->{$field} = casetracker_case_state_load($old->{$field}, 'type');
650           $new->{$field} = casetracker_case_state_load($new->{$field}, 'type');
651           break;
652       }
653       $rows[] = array(t('!label: !old &raquo; !new', array('!label' => $label, '!old' => $old->{$field}, '!new' => $new->{$field})));
654     }
655   }
657   return theme('table', NULL, $rows, array('class' => 'case_changes'));
661  * Theme function for cleaning up the casetracker common form.
662  */
663 function theme_casetracker_case_form_common($form) {
664   drupal_add_css(drupal_get_path('module', 'casetracker') .'/casetracker.css');
665   $output = '';
666   $output .= drupal_render($form['pid']);
667   $output .= drupal_render($form['case_title']);
669   if ($form['assign_to']['#type'] == 'radios') {
670     $header = array_fill(0, 5, array());
671     $header[0] = $form['assign_to']['#title'];
672     $radios = array();
673     foreach (element_children($form['assign_to']) as $id) {
674       $radios[] = drupal_render($form['assign_to'][$id]);
675     }
676     $radios = array_chunk($radios, 5);
677     $output .= theme('table', $header, $radios, array('class' => 'casetracker-assign-to'));
678     drupal_render($form['assign_to']);
679   }
680   else {
681     $output .= drupal_render($form['assign_to']);
682   }
684   $row = array();
685   foreach (element_children($form) as $id) {
686     if (!in_array($id, array('pid', 'case_title', 'assign_to'))) {
687       $row[] = drupal_render($form[$id]);
688     }
689   }
690   $rows = array($row);
691   $output .= theme('table', array(), $rows);
692   $output .= drupal_render($form);
693   return $output;
697  * Theme the case summary shown at the beginning of a case's node.
699  * @param $case
700  *   The node object of the case being viewed.
701  * @param $project
702  *   The node object of the project this case belongs to.
703  */
704 function theme_casetracker_case_summary($case, $project) {
705   $last_comment = db_result(db_query('SELECT last_comment_timestamp FROM {node_comment_statistics} WHERE nid = %d', $case->nid));
706   $rows = array();
708   $rows[] = array(
709     t('Assigned to:'),
710     theme('username', user_load(array('uid' => $case->casetracker->assign_to))),
711   );
712   $rows[] = array(
713     t('Created:'),
714     theme_username($case) ." ". t('at') ." ". format_date($case->created, 'medium'),
715   );
716   $rows[] = array(
717     t('Status:'),
718     "<strong>". casetracker_case_state_load($case->casetracker->case_status_id, 'status') ."</strong> (" .
719     casetracker_case_state_load($case->casetracker->case_type_id, 'type') .' / '.
720     t("Priority")  ." ".
721     casetracker_case_state_load($case->casetracker->case_priority_id, 'priority') .
722     ")",
723   );
724   $rows[] = array(
725     t('Case ID:'),
726     l($project->title, 'node/'. $case->casetracker->pid) .': '. $project->nid .'-'. $case->nid,
727   );
728   if ($last_comment != $case->created) {
729     $rows[] = array(
730       t('Last modified:'),
731       format_date($last_comment, 'medium')
732     );
733   }
735   $output  = '<div class="case">';
736   $output .= theme('table', NULL, $rows, array('class' => 'summary'));
737   $output .= '</div>';
738   return $output;
742  * Theme the project summary shown at the beginning of a project's node.
744  * @param $project
745  *   The node object of the project being viewed.
746  */
747 function theme_casetracker_project_summary($project) {
748   $rows = array();
749   $rows[] = array(t('Project number:'), $project->nid);
750   $rows[] = array(t('Opened by:'), theme_username($project));
751   $rows[] = array(t('Opened on:'), format_date($project->created, 'large'));
752   $rows[] = array(t('Last modified:'), format_date($project->changed, 'large'));
754   $querystring = _casetracker_get_og_query_string($project);
755   $operations = array(); $node_types = node_get_types('names');
756   foreach (array_filter(variable_get('casetracker_case_node_types', array('casetracker_basic_case'))) as $type) {
757     $operations[] = l(
758       t('add !name', array('!name' => $node_types[$type])), 
759       'node/add/'. str_replace('_', '-', $type) .'/'. $project->nid,
760       array('query' => $querystring)
761     );
762   } 
763   $operations = implode(' | ', $operations); // ready for printing in our Operations table cell - delimited by a pipe. nonstandard.
764   $rows[] = array(t('Operations:'), $operations .' | '. l(t('view all project cases'), 'casetracker', array('query' => 'keys=&pid='. $project->nid)));
766   $output  = '<div class="project">';
767   $output .= theme('table', NULL, $rows, array('class' => 'summary'));
768   $output .= '</div>';
769   return $output;
773  * API FUNCTIONS ======================================================
774  */
777  * API function that returns valid project options.
778  */
779 function casetracker_project_options() {
780   $projects = array();
781   // Fetch the views list of projects, which is space-aware.
782   if ($view = views_get_view('casetracker_project_options')) {
783     $view->set_display();
784     $view->set_items_per_page(0);
785     $view->execute();
786     foreach ($view->result as $row) {
787       $projects[$row->nid] = $row->node_title;
788     }
789   }
790   return $projects;
794  * API function that returns valid user options.
795  */
796 function casetracker_user_options() {
797   $group = module_exists('og') ? og_get_group_context() : NULL;
798   $options = array(0 => variable_get('casetracker_default_assign_to', variable_get('anonymous', t('Anonymous'))));
799   if ($group) {
800     $gid = $group->nid;
801     $results = db_query('SELECT u.uid, u.name FROM {users} u INNER JOIN {og_uid} ou ON u.uid = ou.uid WHERE u.status > 0 AND u.uid > 0 AND ou.nid = %d ORDER BY u.name ASC', $gid);
802   }
803   else {
804     $results = db_query('SELECT u.uid, u.name FROM {users} u WHERE u.status > 0 AND u.uid > 0 ORDER BY u.name ASC');
805   }
806   while ($result = db_fetch_object($results)) {
807     $options[$result->uid] = check_plain($result->name);
808   }
809   return $options;
813  * API function for checking whether a node type is a casetracker case.
814  */
815 function casetracker_is_case($node) {
816   if (is_object($node) && !empty($node->type)) {
817     $type = $node->type;
818   }
819   else if (is_string($node)) {
820     $type = $node;
821   }
822   if ($type) {
823     return in_array($type, variable_get('casetracker_case_node_types', array('casetracker_basic_case')), TRUE);
824   }
825   return FALSE;
829  * API function for checking whether a node type is a casetracker project.
830  */
831 function casetracker_is_project($node) {
832   if (is_object($node) && !empty($node->type)) {
833     $type = $node->type;
834   }
835   else if (is_string($node)) {
836     $type = $node;
837   }
838   if ($type) {
839     return in_array($type, variable_get('casetracker_project_node_types', array('casetracker_basic_project')), TRUE);
840   }
841   return FALSE;
845  * Given a user name, returns the uid of that account.
846  * If the passed name is not found, returns 0.
847  * See also casetracker_get_name().
848  */
849 function casetracker_get_uid($name = NULL, $reset = FALSE) {
850   static $users = array();
851   if (!isset($users[$name]) || $reset) {
852     $result = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $name));
853     $users[$name] = $result ? $result : 0;
854   }
855   return $users[$name];
859  * Given a uid, returns the name of that account. If the passed uid is
860  * not found, returns the default "assign to" name as specified in the
861  * settings. @todo This may not always be desired, but is how we use it.
862  * See also casetracker_get_uid().
863  */
864 function casetracker_get_name($uid = NULL, $reset = FALSE) {
865   static $users = array();
866   if (!isset($users[$uid]) || $reset) {
867     $result = db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $uid));
868     $users[$uid] = $result ? $result : '';
869   }
870   return !empty($users[$uid]) ? $users[$uid] : variable_get('casetracker_default_assign_to', variable_get('anonymous', t('Anonymous')));