2);
}
/**
* Implementation of hook_help().
*/
function casetracker_help($path, $arg) {
switch ($path) {
case 'admin/settings/casetracker/states':
return '
'. t('Current Case Tracker case states are listed below.') .'
';
case 'admin/settings/casetracker/states/add':
return ''. t('You may add a new case state below.') .'
';
case 'admin/settings/casetracker/states/edit/'. arg(4):
return ''. t('You may edit an existing case state below.') .'
';
case 'admin/settings/casetracker':
return ''. t('Configure the various Case Tracker options with these settings.') .'
';
}
}
/**
* Implementation of hook_perm().
*/
function casetracker_perm() {
return array(
'administer case tracker',
'assign cases',
);
}
/**
* Implementation of hook_menu().
*/
function casetracker_menu() {
/* casetracker main settings */
$items['admin/settings/casetracker'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'drupal_get_form',
'page arguments' => array('casetracker_settings'),
'description' => 'Configure the various Case Tracker options with these settings.',
'title' => 'Case Tracker',
'type' => MENU_NORMAL_ITEM,
);
$items['admin/settings/casetracker/settings'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'drupal_get_form',
'page arguments' => array('casetracker_settings'),
'title' => 'Settings',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
/* casetracker state handling */
$items['admin/settings/casetracker/states'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'casetracker_case_state_overview',
'type' => MENU_LOCAL_TASK,
'title' => 'Case states',
'description' => 'Add, edit and delete Case States, Types and Priorities',
);
$items['admin/settings/casetracker/states/list'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'casetracker_case_state_overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'title' => 'Overview',
'weight' => -10,
'description' => 'Add, edit and delete Case States, Types and Priorities',
);
$items['admin/settings/casetracker/states/add'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'drupal_get_form',
'page arguments' => array('casetracker_case_state_edit'),
'title' => 'Add case state',
'type' => MENU_LOCAL_TASK,
);
$items['admin/settings/casetracker/states/edit/%casetracker_case_state'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'drupal_get_form',
'page arguments' => array('casetracker_case_state_edit', 5),
'title' => 'Edit case state',
'type' => MENU_CALLBACK,
);
$items['admin/settings/casetracker/states/delete/%casetracker_case_state'] = array(
'file' => 'casetracker_admin.inc',
'access arguments' => array('administer case tracker'),
'page callback' => 'drupal_get_form',
'page arguments' => array('casetracker_case_state_confirm_delete', 5),
'title' => 'Delete case state',
'type' => MENU_CALLBACK,
);
/* casetracker autocomplete */
$items['casetracker_autocomplete'] = array(
'title' => 'Case Tracker autocomplete',
'page callback' => 'casetracker_autocomplete',
'access callback' => 'user_access',
'access arguments' => array('assign cases'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implementation of hook_nodeapi().
*/
function casetracker_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
// CASES
if (casetracker_is_case($node->type)) {
switch ($op) {
case 'delete':
// delete case and its comments.
$comment_results = db_query("SELECT cid FROM {comments} WHERE nid = %d", $node->nid);
while ($comment_result = db_fetch_object($comment_results)) {
db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment_result->cid);
}
db_query('DELETE FROM {casetracker_case} WHERE nid = %d', $node->nid);
break;
case 'presave':
// $node->casetracker is an Array and we wanna it to be an object.
$node->casetracker = (object) $node->casetracker;
break;
case 'insert':
// cases: generate a case ID and send it along.
$record = $node->casetracker;
$record->assign_to = is_numeric($record->assign_to) ? $record->assign_to : casetracker_get_uid($record->assign_to);
$record->nid = $node->nid;
$record->vid = $node->vid;
drupal_write_record('casetracker_case', $record);
break;
case 'load':
$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));
if ($casetracker) {
return array('casetracker' => $casetracker);
}
break;
case 'update':
$record = (object) $node->casetracker;
$record->assign_to = is_numeric($record->assign_to) ? $record->assign_to : casetracker_get_uid($record->assign_to);
$record->nid = $node->nid;
$record->vid = $node->vid;
$primary = $node->revision ? array('nid') : array('nid', 'vid');
drupal_write_record('casetracker_case', $record, $primary);
break;
case 'view':
// On preview the case will be an array, we want an object.
if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) {
$node->casetracker = (object)$node->casetracker;
}
// used in the breadcrumb and our theme function, mostly for nid and project number display.
$project = node_load($node->casetracker->pid);
if ($page) {
$trail = array(
l(t('Home'), NULL),
l(t('Case Tracker'), 'casetracker/projects'),
l($project->title, "node/{$node->casetracker->pid}"),
l(t('All cases'), "casetracker/cases/{$node->casetracker->pid}/all"),
);
drupal_set_breadcrumb($trail);
}
$node->content['casetracker_case_summary'] = array(
'#value' => theme('casetracker_case_summary', $node, $project),
'#weight' => -10
);
break;
}
}
// PROJECTS
else if (casetracker_is_project($node->type)) {
switch ($op) {
case 'delete':
// projects: delete all the cases under the project and all the comments under each case.
$case_results = db_query("SELECT nid from {casetracker_case} WHERE pid = %d", $node->nid);
while ($case_result = db_fetch_object($case_results)) {
db_query("DELETE FROM {casetracker_case} WHERE nid = %d", $case_result->nid);
$comment_results = db_query("SELECT cid FROM {comments} WHERE nid = %d", $case_result->nid);
while ($comment_result = db_fetch_object($comment_results)) {
db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment_result->cid);
}
node_delete($case_result->nid); // this'll handle comment deletion too.
}
break;
case 'view':
if ($page) {
$trail = array(
l(t('Home'), NULL),
l(t('Case Tracker'), 'casetracker/projects'),
);
drupal_set_breadcrumb($trail);
}
$node->content['casetracker_project_summary'] = array('#value' => theme('casetracker_project_summary', $node), '#weight' => -10);
break;
}
}
}
/**
* Implementation of hook_comment().
*/
function casetracker_comment(&$comment, $op) {
// Load the node here anyway -- it is almost certainly static cached already.
$node = is_array($comment) ? node_load($comment['nid']) : node_load($comment->nid);
// Bail if this is not a casetracker node.
if (!casetracker_is_case($node->type)) {
return;
}
if ($op == 'insert' || $op == 'update') {
$new = (object) $comment['casetracker'];
$new->cid = $comment['cid'];
$new->nid = $comment['nid'];
$new->vid = $comment['revision_id'];
$new->state = 1;
$new->assign_to = casetracker_get_uid($new->assign_to);
// Populate old state values from node
$old = $node->casetracker;
$old->cid = $comment['cid'];
$old->state = 0;
drupal_write_record('casetracker_case', $new, array('nid', 'vid'));
}
switch ($op) {
case 'insert':
drupal_write_record('casetracker_comment_status', $old);
drupal_write_record('casetracker_comment_status', $new);
break;
case 'update':
drupal_write_record('casetracker_comment_status', $old, array('cid', 'state'));
drupal_write_record('casetracker_comment_status', $new, array('cid', 'state'));
break;
case 'delete':
// @todo theoretically, if you delete a comment, we should reset all the values
// to what they were before the comment was submitted. this doesn't happen yet.
db_query("DELETE FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
break;
case 'view':
// If this is a preview we won't have a cid yet.
if (empty($comment->cid)) {
$case_data['new'] = (object)$comment->casetracker;
$case_data['new']->assign_to = casetracker_get_uid($case_data['new']->assign_to);
$case = node_load($comment->nid);
$case_data['old'] = drupal_clone($case->casetracker);
}
else {
$results = db_query("SELECT * FROM {casetracker_comment_status} WHERE cid = %d", $comment->cid);
while ($result = db_fetch_object($results)) {
$state = $result->state ? 'new' : 'old';
$case_data[$state] = $result;
}
}
$comment->comment = theme('casetracker_comment_changes', $case_data['old'], $case_data['new']) . $comment->comment;
break;
}
}
/**
* Implementation of hook_form_alter().
*/
function casetracker_form_alter(&$form, &$form_state, $form_id) {
if (!empty($form['#node'])) {
$node = $form['#node'];
// Add case options to our basic case type.
if (casetracker_is_case($node->type)) {
$count = count(casetracker_project_options());
if ($count == 0) {
drupal_set_message(t('You must create a project before adding cases.'), 'error');
return;
}
else {
$default_project = null;
if (!isset($form['#node']->nid) && is_numeric(arg(3))) {
$default_project = arg(3);
}
casetracker_case_form_common($form, $default_project);
}
}
}
}
/**
* Implementation of hook_form_comment_form_alter().
*/
function casetracker_form_comment_form_alter(&$form, &$form_state) {
$node = isset($form['nid']['#value']) ? node_load($form['nid']['#value']) : NULL;
if (casetracker_is_case($node->type)) {
$form['#node'] = $node;
// add case options to the comment form.
casetracker_case_form_common($form);
// necessary for our casetracker_comment() callback.
$form['revision_id'] = array('#type' => 'hidden', '#value' => $node->vid);
}
}
/**
* Common form elements for cases, generic enough for use either in
* a full node display, or in comment displays and updating. Default
* values are calculated based on an existing $form['nid']['#value'].
*
* @param $form
* A Forms API $form, as received from a hook_form_alter().
* @param $default_project
* The project ID that should be pre-selected.
* @return $form
* A modified Forms API $form.
*/
function casetracker_case_form_common(&$form, $default_project = NULL) {
global $user;
$node = $form['#node'];
// On preview the case will be an array, we want an object.
if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) {
$node->casetracker = (object)$node->casetracker;
}
// project to set as the default is based on how the user got here.
if (empty($default_project) && !empty($node->casetracker->pid)) {
$default_project = $node->casetracker->pid;
}
$project_options = casetracker_project_options();
$form['casetracker'] = array(
'#type' => 'fieldset',
'#title' => t('Case information'),
'#weight' => -10,
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#tree' => TRUE,
'#theme' => 'casetracker_case_form_common',
);
// if there's no project ID from the URL, or more than one project,
// we'll create a select menu for the user; otherwise, we'll save
// the passed (or only) project ID into a hidden field.
if (count($project_options) > 1) {
$form['casetracker']['pid'] = array(
'#title' => t('Project'),
'#type' => 'select',
'#default_value' => $default_project,
'#options' => $project_options,
);
}
else {
$form['casetracker']['pid'] = array(
'#type' => 'value', // default value, or the only the project ID in the project_options array.
'#value' => !empty($default_project) ? $default_project : key($project_options),
);
}
// Retrieve the assign_to default value.
if (isset($node->casetracker->assign_to)) {
$default_assign_to = is_numeric($node->casetracker->assign_to) ? casetracker_get_name($node->casetracker->assign_to) : $node->casetracker->assign_to;
}
else {
$default_assign_to = casetracker_default_assign_to();
}
// Only show this element if the user has access.
$form['casetracker']['assign_to'] = array(
'#title' => t('Assign to'),
'#required' => TRUE,
'#access' => user_access('assign cases'),
);
// Use different widgets based on the potential assignees.
$options = drupal_map_assoc(casetracker_user_options());
$assign_to_widget = variable_get('casetracker_assign_to_widget', 'flexible');
if (($assign_to_widget == 'flexible' && count($options) < 25) || $assign_to_widget == 'radios') {
$form['casetracker']['assign_to']['#type'] = 'radios';
$form['casetracker']['assign_to']['#options'] = $options;
}
else if (($assign_to_widget == 'flexible' && count($options) < 50) || $assign_to_widget == 'select') {
$form['casetracker']['assign_to']['#type'] = 'select';
$form['casetracker']['assign_to']['#options'] = $options;
}
else {
$form['casetracker']['assign_to']['#type'] = 'textfield';
$form['casetracker']['assign_to']['#autocomplete_path'] = 'casetracker_autocomplete';
$form['casetracker']['assign_to']['#size'] = 12;
}
// Set the default value if it is valid.
$form['casetracker']['assign_to']['#default_value'] = in_array($default_assign_to, $options, TRUE) ? $default_assign_to : NULL;
$case_status_options = casetracker_realm_load('status');
$default_status = !empty($node->casetracker->case_status_id) ? $node->casetracker->case_status_id : variable_get('casetracker_default_case_status', key($case_status_options));
$form['casetracker']['case_status_id'] = array(
'#type' => 'select',
'#title' => t('Status'),
'#options' => $case_status_options,
'#default_value' => $default_status,
);
$case_priority_options = casetracker_realm_load('priority');
$default_priority = !empty($node->casetracker->case_priority_id) ? $node->casetracker->case_priority_id : variable_get('casetracker_default_case_priority', key($case_priority_options));
$form['casetracker']['case_priority_id'] = array(
'#type' => 'select',
'#title' => t('Priority'),
'#options' => $case_priority_options,
'#default_value' => $default_priority,
);
$case_type_options = casetracker_realm_load('type');
$default_type = !empty($node->casetracker->case_type_id) ? $node->casetracker->case_type_id : variable_get('casetracker_default_case_type', key($case_type_options));
$form['casetracker']['case_type_id'] = array(
'#type' => 'select',
'#title' => t('Type'),
'#options' => $case_type_options,
'#default_value' => $default_type,
);
return $form;
}
/**
* Implementation of hook_block
*/
function casetracker_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0] = array(
'info' => t('Jump to case'),
);
return $blocks;
case 'configure':
return array();
case 'save':
return;
case 'view':
switch ($delta) {
case 0:
if (user_access('access content')) {
$block['subject'] = t('Jump to case');
$block['content'] = drupal_get_form('casetracker_block_jump_to_case_number');
}
break;
}
return $block;
}
}
/**
* Form for "Jump to case number" block.
*/
function casetracker_block_jump_to_case_number() {
$form = array();
$form['case_number'] = array(
'#maxlength' => 60,
'#required' => TRUE,
'#size' => 15,
'#title' => t('Case number'),
'#type' => 'textfield',
'#prefix' => '',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Go'),
'#suffix' => '
',
);
return $form;
}
/**
* Submit function for our "Jump to case number" block.
*/
function casetracker_block_jump_to_case_number_submit($form, $form_state) {
list($pid, $nid) = explode('-', $form_state['values']['case_number']);
$case_nid = db_result(db_query("SELECT nid FROM {casetracker_case} WHERE pid = %d AND nid = %d", $pid, $nid));
if (!$case_nid) {
drupal_set_message(t('Your case number was not found.'), 'error');
return;
}
drupal_goto('node/'. $case_nid);
}
/**
* CASE STATE CRUD ====================================================
*/
/**
* Returns information about the various case states and their options.
* The number of parameters passed will determine the return value.
*
* @param $csid
* Optional; the state ID to return from the passed $realm.
* @param $realm
* Optional; the name of the realm ('status', 'priority', or 'type').
* @param $reset
* Optional; set to TRUE to reset the static cache.
*
* @return $values
* If only $realm is passed, you'll receive an array with the keys
* being the state ID and the values being their names. If a $csid
* is also passed, you'll receive just a string of the state name.
* If ONLY a $csid is passed, we'll return a list of 'name', 'realm'.
*/
function casetracker_case_state_load($csid = NULL, $realm = NULL, $reset = FALSE) {
static $states_lookup;
if (!$states_lookup || $reset) {
$results = db_query("SELECT csid, case_state_name AS name, case_state_realm AS realm, weight
FROM {casetracker_case_states} ORDER BY weight");
$states_lookup = array();
while ($row = db_fetch_object($results)) {
$row->display = casetracker_tt("case_states:$row->csid:name", $row->name);
$states_lookup[$row->realm][$row->csid] = $states_lookup['all'][$row->csid] = $row;
}
}
if ($csid && $realm) {
return $states_lookup['all'][$csid]->display;
}
elseif ($csid && !$realm) {
return $states_lookup['all'][$csid];
}
elseif (!$csid && $realm) {
$options = array(); // suitable for form api.
if (!empty($states_lookup[$realm])) {
foreach ($states_lookup[$realm] as $state) {
$options[$state->csid] = $state->display;
}
}
return $options;
}
}
/**
* Translate user defined string. Wrapper function for tt() if i18nstrings enabled.
*
* The string id for case states will be: case:[realm]#[csid]:name
*
* @param $name
* String id without 'casetracker', which will be prepended automatically
*/
function casetracker_tt($name, $string, $langcode = NULL) {
return function_exists('i18nstrings') ? i18nstrings('casetracker:' . $name, $string, $langcode) : $string;
}
/**
* Implementation of hook_locale().
*/
function casetracker_locale($op = 'groups', $group = NULL) {
switch ($op) {
case 'groups':
return array('casetracker' => t('Case Tracker'));
case 'info':
$info['casetracker']['refresh callback'] = 'casetracker_locale_refresh';
$info['casetracker']['format'] = FALSE;
return $info;
}
}
/**
* Refresh locale strings.
*/
function casetracker_locale_refresh() {
$results = db_query("SELECT csid, case_state_name AS name, case_state_realm AS realm FROM {casetracker_case_states}");
while ($row = db_fetch_object($results)) {
i18nstrings_update("casetracker:case_states:$row->csid:name", $row->name);
}
// Meaning it completed with no issues. @see i18nmenu_locale_refresh().
return TRUE;
}
/**
* Load states for a particular realm. Wrapper around casetracker_case_state_load()
*
* @param $realm
* Name of the realm ('status', 'priority', or 'type').
*
* @return
* array with the keys being the state ID and the values being their names.
*/
function casetracker_realm_load($realm) {
return casetracker_case_state_load(null, $realm);
}
/**
* Saves a case state.
*
* @param $case_state
* An array containing 'name' and 'realm' keys. If no 'csid'
* is passed, a new state is created, otherwise, we'll update
* the record that corresponds to that ID.
*/
function casetracker_case_state_save($case_state = NULL) {
if (!$case_state['name'] || !$case_state['realm']) {
return NULL;
}
// Need to collect information into another array since the db columns have different names : (
$record = array(
'case_state_name' => $case_state['name'],
'case_state_realm' => $case_state['realm'],
'weight' => $case_state['weight'],
);
if (isset($case_state['csid'])) {
$record['csid'] = $case_state['csid'];
drupal_write_record('casetracker_case_states', $record, array('csid'));
}
else {
drupal_write_record('casetracker_case_states', $record);
}
// Update translations
if (function_exists('i18nstrings_update')) {
i18nstrings_update('casetracker:case_states:'. $record['csid'] .':name', $case_state['name']);
}
return $result;
}
/**
* Deletes a case state.
*
* @todo There is currently no attempt to do anything with cases which
* have been assigned the $csid that is about to be deleted. We should
* reset them to the default per our settings (and warn the user on our
* confirmation page), or something else entirely.
*
* @param $csid
* The case state ID to delete.
*/
function casetracker_case_state_delete($csid = NULL) {
if (!empty($csid)) {
db_query('DELETE FROM {casetracker_case_states} WHERE csid = %d', $csid);
}
}
/**
* COMMENT DISPLAY ====================================================
*/
/**
* Retrieve autocomplete suggestions for assign to user options.
*
* @TODO: In order to get this down to 1 query and respect any custom
* views selected for use as user option filters, we need to:
* - Submit a patch to the Views user name filter/argument handler to support LIKE filtering.
* - Ensure that the custom view uses this handler or add it if does not.
* - Generate the query & result set using this modified View.
*/
function casetracker_autocomplete($string) {
$matches = array();
$options = casetracker_user_options();
$result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string, 0, 10);
while ($user = db_fetch_object($result)) {
if (in_array($user->name, $options, TRUE)) {
$matches[$user->name] = check_plain($user->name);
}
}
// Special case for 'Unassigned'
$unassigned = t('Unassigned');
if (strpos(strtolower($unassigned), strtolower($string)) !== FALSE) {
$matches[$unassigned] = $unassigned;
}
drupal_json($matches);
}
/**
* Returns an query string needed in case of Organic Groups
* providing preselected audience checkboxes for projects as groups (og)
*
* @param object CT project
* @return string
*/
function _casetracker_get_og_query_string(&$project) {
$querystring = array();
// checking if project is group
if ($project->type == 'group') {
$querystring[] = 'gids[]='. $project->nid;
//checking if group-project is part of another group
if (isset($project->og_groups)
&& is_array($project->og_groups)
) {
foreach ($project->og_groups as $group) {
$querystring[] = 'gids[]='. $group;
}
}
}
//checking if project is part of a group
elseif (isset($project->og_groups)
&& is_array($project->og_groups)
&& $project->type !== 'group'
) {
foreach ($project->og_groups as $group) {
$querystring[] = 'gids[]='. $group;
}
}
return (0 < count($querystring))
? implode('&', $querystring)
: NULL;
}
/**
* THEME ==============================================================
*/
/**
* Implementation of hook_theme
*/
function casetracker_theme() {
return array(
'casetracker_comment_changes' => array(),
'casetracker_case_form_common' => array(),
'casetracker_case_summary' => array(),
'casetracker_project_summary' => array(),
);
}
/**
* Displays the changes a comment has made to the case fields.
*
* @param $case_data
* An array of both 'old' and 'new' objects that contains
* the before and after values this comment has changed.
*/
function theme_casetracker_comment_changes($old, $new) {
$rows = array();
$fields = array(
'pid' => t('Project'),
'title' => t('Title'),
'case_status_id' => t('Status'),
'assign_to' => t('Assigned'),
'case_priority_id' => t('Priority'),
'case_type_id' => t('Type'),
);
foreach ($fields as $field => $label) {
if ($new->{$field} != $old->{$field}) {
switch ($field) {
case 'pid':
$old_title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $old->pid));
$new_title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $new->pid));
$old->{$field} = l($old_title, "node/{$old->pid}");
$new->{$field} = l($new_title, "node/{$new->pid}");
break;
case 'case_status_id':
$old->{$field} = casetracker_case_state_load($old->{$field}, 'status');
$new->{$field} = casetracker_case_state_load($new->{$field}, 'status');
break;
case 'assign_to':
$old->{$field} = casetracker_get_name($old->{$field});
$new->{$field} = casetracker_get_name($new->{$field});
break;
case 'case_priority_id':
$old->{$field} = casetracker_case_state_load($old->{$field}, 'priority');
$new->{$field} = casetracker_case_state_load($new->{$field}, 'priority');
break;
case 'case_type_id':
$old->{$field} = casetracker_case_state_load($old->{$field}, 'type');
$new->{$field} = casetracker_case_state_load($new->{$field}, 'type');
break;
}
$rows[] = array(t('@label: @old » @new', array('@label' => $label, '@old' => $old->{$field}, '@new' => $new->{$field})));
}
}
return theme('table', NULL, $rows, array('class' => 'case_changes'));
}
/**
* Theme function for cleaning up the casetracker common form.
*/
function theme_casetracker_case_form_common($form) {
drupal_add_css(drupal_get_path('module', 'casetracker') .'/casetracker.css');
$output = '';
$output .= drupal_render($form['pid']);
$output .= drupal_render($form['case_title']);
if ($form['assign_to']['#type'] == 'radios') {
if ($form['assign_to']['#access']) {
$header = array_fill(0, 5, array());
$header[0] = $form['assign_to']['#title'];
$radios = array();
foreach (element_children($form['assign_to']) as $id) {
$radios[] = drupal_render($form['assign_to'][$id]);
}
$radios = array_chunk($radios, 5);
$output .= theme('table', $header, $radios, array('class' => 'casetracker-assign-to'));
}
drupal_render($form['assign_to']);
}
else {
$output .= drupal_render($form['assign_to']);
}
$row = array();
foreach (element_children($form) as $id) {
if (!in_array($id, array('pid', 'case_title', 'assign_to'))) {
$row[] = drupal_render($form[$id]);
}
}
$rows = array($row);
$output .= theme('table', array(), $rows);
$output .= drupal_render($form);
return $output;
}
/**
* Theme the case summary shown at the beginning of a case's node.
*
* @param $case
* The node object of the case being viewed.
* @param $project
* The node object of the project this case belongs to.
*/
function theme_casetracker_case_summary($case, $project) {
$last_comment = db_result(db_query('SELECT last_comment_timestamp FROM {node_comment_statistics} WHERE nid = %d', $case->nid));
$rows = array();
// On node preview the form logic can't translate assign_to back to a uid for
// us so we need to be able handle it either way.
if (is_numeric($case->casetracker->assign_to)) {
$assign_to = user_load(array('uid' => $case->casetracker->assign_to));
}
else {
$assign_to = user_load(array('name' => $case->casetracker->assign_to));
}
if (empty($assign_to) || $assign_to->uid == 0) {
$rows[] = array(
t('Assigned to:'),
'' . t('Unassigned') . '',
);
}
else {
$rows[] = array(
t('Assigned to:'),
theme('username', $assign_to),
);
}
$rows[] = array(
t('Created:'),
t('!username at !date', array('!username' => theme('username', $case), '!date' => format_date($case->created, 'medium'))),
);
$rows[] = array(
t('Status:'),
t('@status (@type / Priority @priority)', array(
'@status' => casetracker_case_state_load($case->casetracker->case_status_id, 'status'),
'@type' => casetracker_case_state_load($case->casetracker->case_type_id, 'type'),
'@priority' => casetracker_case_state_load($case->casetracker->case_priority_id, 'priority'),
)),
);
// On node preview a case may not have a nid, so we use some placeholder text.
$case_id = isset($case->nid) ? $case->nid : t("NEW");
$rows[] = array(
t('Case ID:'),
l($project->title, 'node/'. $case->casetracker->pid) .': '. $project->nid .'-'. $case_id,
);
if ($last_comment != $case->created) {
$rows[] = array(
t('Last modified:'),
format_date($last_comment, 'medium')
);
}
$output = '';
$output .= theme('table', NULL, $rows, array('class' => 'summary'));
$output .= '
';
return $output;
}
/**
* Theme the project summary shown at the beginning of a project's node.
*
* @param $project
* The node object of the project being viewed.
*/
function theme_casetracker_project_summary($project) {
$rows = array();
$rows[] = array(t('Project number:'), $project->nid);
$rows[] = array(t('Opened by:'), theme('username', $project));
$rows[] = array(t('Opened on:'), format_date($project->created, 'large'));
$rows[] = array(t('Last modified:'), format_date($project->changed, 'large'));
$querystring = _casetracker_get_og_query_string($project);
$operations = array(); $node_types = node_get_types('names');
foreach (array_filter(variable_get('casetracker_case_node_types', array('casetracker_basic_case'))) as $type) {
$operations[] = l(
t('add !name', array('!name' => $node_types[$type])),
'node/add/'. str_replace('_', '-', $type) .'/'. $project->nid,
array('query' => $querystring)
);
}
$operations = implode(' | ', $operations); // ready for printing in our Operations table cell - delimited by a pipe. nonstandard.
$rows[] = array(t('Operations:'), $operations .' | '. l(t('view all project cases'), 'casetracker', array('query' => 'keys=&pid='. $project->nid)));
$output = '';
$output .= theme('table', NULL, $rows, array('class' => 'summary'));
$output .= '
';
return $output;
}
/**
* API FUNCTIONS ======================================================
*/
/**
* API function that returns valid project options.
*/
function casetracker_project_options() {
$projects = array();
// Fetch the views list of projects, which is space-aware.
if ($view = views_get_view(variable_get('casetracker_view_project_options', 'casetracker_project_options'))) {
$view->set_display();
$view->set_items_per_page(0);
$view->execute();
foreach ($view->result as $row) {
$projects[$row->nid] = $row->node_title;
}
}
return $projects;
}
/**
* API function that returns valid user options.
*/
function casetracker_user_options() {
$users = array();
$options = array();
if ($view = views_get_view(variable_get('casetracker_view_assignee_options', 'casetracker_assignee_options'))) {
$view->set_display();
$view->set_items_per_page(0);
$view->execute();
foreach ($view->result as $row) {
$options[$row->uid] = $row->users_name;
}
}
$anon_user = casetracker_default_assign_to();
// fill in "Unassigned" value because view is not rendered and the redundant option in views is irrelevant
// @TODO render the view before display so this isn't needed.
if (isset($options[0])) {
$options[0] = $anon_user;
}
// if "Unassigned" is the default assignee, we graft it onto the view results here as most views
// that do any substantive filtering will exclude the anonymous user.
elseif (in_array(variable_get('casetracker_default_assign_to', $anon_user), array($anon_user, variable_get('anonymous', t('Anonymous'))))) {
$options = array($anon_user) + $options;
}
return $options;
}
/**
* API function for checking whether a node type is a casetracker case.
*/
function casetracker_is_case($node) {
if (is_object($node) && !empty($node->type)) {
$type = $node->type;
}
else if (is_string($node)) {
$type = $node;
}
if ($type) {
return in_array($type, variable_get('casetracker_case_node_types', array('casetracker_basic_case')), TRUE);
}
return FALSE;
}
/**
* API function for checking whether a node type is a casetracker project.
*/
function casetracker_is_project($node) {
if (is_object($node) && !empty($node->type)) {
$type = $node->type;
}
else if (is_string($node)) {
$type = $node;
}
if ($type) {
return in_array($type, variable_get('casetracker_project_node_types', array('casetracker_basic_project')), TRUE);
}
return FALSE;
}
/**
* Given a user name, returns the uid of that account.
* If the passed name is not found, returns 0.
* See also casetracker_get_name().
*/
function casetracker_get_uid($name = NULL, $reset = FALSE) {
static $users = array();
if (!isset($users[$name]) || $reset) {
$result = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $name));
$users[$name] = $result ? $result : 0;
}
return $users[$name];
}
/**
* Given a uid, returns the name of that account. If the passed uid is
* not found, returns the default "assign to" name as specified in the
* settings. @todo This may not always be desired, but is how we use it.
* See also casetracker_get_uid().
*/
function casetracker_get_name($uid = NULL, $reset = FALSE) {
static $users = array();
if (!isset($users[$uid]) || $reset) {
if ($uid == 0) {
$users[0] = t('Unassigned');
}
else {
$result = db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $uid));
$users[$uid] = $result ? $result : '';
}
}
return !empty($users[$uid]) ? $users[$uid] : casetracker_default_assign_to();
}
/**
* Fetch the proper default assignee.
*/
function casetracker_default_assign_to() {
$assign_to = variable_get('casetracker_default_assign_to', t('Unassigned'));
if ($assign_to == variable_get('anonymous', t('Anonymous'))) {
return t('Unassigned');
}
return $assign_to;
}
/**
* Implementation of hook_token_values().
*/
function casetracker_token_values($type, $object = NULL) {
module_load_include('inc', 'casetracker', 'casetracker.token');
return _casetracker_token_values($type, $object);
}
/**
* Implementation of hook_token_list().
*/
function casetracker_token_list($type = 'all') {
module_load_include('inc', 'casetracker', 'casetracker.token');
return _casetracker_token_list($type);
}