if (Post::val('notifyme') == '1') { // If the user wanted to watch this task for changes Backend::add_notification($user->id, $task['task_id']); } $_SESSION['SUCCESS'] = L('commentaddedmsg'); Flyspray::Redirect(CreateURL('details', $task['task_id'])); break; // ################## // Tracking // ################## // ################## // Tracking // ################## case 'details.efforttracking': require_once BASEDIR . '/includes/class.effort.php'; $effort = new effort($task['task_id'], $user->id); if (Post::val('start_tracking')) { if ($effort->startTracking()) { $_SESSION['SUCCESS'] = L('efforttrackingstarted'); } else { $_SESSION['ERROR'] = L('efforttrackingnotstarted'); } } if (Post::val('stop_tracking')) { $effort->stopTracking(); $_SESSION['SUCCESS'] = L('efforttrackingstopped'); } if (Post::val('cancel_tracking')) { $effort->cancelTracking(); $_SESSION['SUCCESS'] = L('efforttrackingcancelled'); }
/** * Adds a new task * @param array $args array containing all task properties. unknown properties will be ignored * @access public * @return integer the task ID on success * @version 1.0 * @notes $args is POST data, bad..bad user.. */ public static function create_task($args) { global $db, $user, $proj; if (!isset($args)) { return 0; } // these are the POST variables that the user MUST send, if one of // them is missing or if one of them is empty, then we have to abort $requiredPostArgs = array('item_summary', 'project_id'); //modify: made description not required foreach ($requiredPostArgs as $required) { if (empty($args[$required])) { return 0; } } $notify = new Notifications(); if ($proj->id != $args['project_id']) { $proj = new Project($args['project_id']); } if (!$user->can_open_task($proj)) { return 0; } // first populate map with default values $sql_args = array('project_id' => $proj->id, 'date_opened' => time(), 'last_edited_time' => time(), 'opened_by' => intval($user->id), 'percent_complete' => 0, 'mark_private' => 0, 'supertask_id' => 0, 'closedby_version' => 0, 'closure_comment' => '', 'task_priority' => 2, 'due_date' => 0, 'anon_email' => '', 'item_status' => STATUS_UNCONFIRMED); // POST variables the user is ALLOWED to provide $allowedPostArgs = array('task_type', 'product_category', 'product_version', 'operating_system', 'task_severity', 'estimated_effort', 'supertask_id', 'item_summary', 'detailed_desc'); // these POST variables the user is only ALLOWED to provide if he got the permissions if ($user->perms('modify_all_tasks')) { $allowedPostArgs[] = 'closedby_version'; $allowedPostArgs[] = 'task_priority'; $allowedPostArgs[] = 'due_date'; $allowedPostArgs[] = 'item_status'; } if ($user->perms('manage_project')) { $allowedPostArgs[] = 'mark_private'; } // now copy all over all POST variables the user is ALLOWED to provide // (but only if they are not empty) foreach ($allowedPostArgs as $allowed) { if (!empty($args[$allowed])) { $sql_args[$allowed] = $args[$allowed]; } } // Process the due_date if (isset($args['due_date']) && ($due_date = $args['due_date']) || ($due_date = 0)) { $due_date = Flyspray::strtotime($due_date); } $sql_params[] = 'mark_private'; $sql_values[] = intval($user->perms('manage_project') && isset($args['mark_private']) && $args['mark_private'] == '1'); $sql_params[] = 'due_date'; $sql_values[] = $due_date; $sql_params[] = 'closure_comment'; $sql_values[] = ''; // Process estimated effort $estimated_effort = 0; if ($proj->prefs['use_effort_tracking'] && isset($sql_args['estimated_effort'])) { if (($estimated_effort = effort::EditStringToSeconds($sql_args['estimated_effort'], $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format'])) === FALSE) { Flyspray::show_error(L('invalideffort')); $estimated_effort = 0; } $sql_args['estimated_effort'] = $estimated_effort; } // Token for anonymous users $token = ''; if ($user->isAnon()) { if (empty($args['anon_email'])) { return 0; } $token = md5(function_exists('openssl_random_pseudo_bytes') ? openssl_random_pseudo_bytes(32) : uniqid(mt_rand(), true)); $sql_args['task_token'] = $token; $sql_args['anon_email'] = $args['anon_email']; } // ensure all variables are in correct format if (!empty($sql_args['due_date'])) { $sql_args['due_date'] = Flyspray::strtotime($sql_args['due_date']); } if (isset($sql_args['mark_private'])) { $sql_args['mark_private'] = intval($sql_args['mark_private'] == '1'); } // split keys and values into two separate arrays $sql_keys = array(); $sql_values = array(); foreach ($sql_args as $key => $value) { $sql_keys[] = $key; $sql_values[] = $value; } /* * TODO: At least with PostgreSQL, this has caused the sequence to be * out of sync with reality. Must be fixed in upgrade process. Check * what's the situation with MySQL. (It's fine, it updates the value even * if the column was manually adjusted. Remove this whole block later.) $result = $db->Query('SELECT MAX(task_id)+1 FROM {tasks}'); $task_id = $db->FetchOne($result); $task_id = $task_id ? $task_id : 1; */ //now, $task_id is always the first element of $sql_values #array_unshift($sql_keys, 'task_id'); #array_unshift($sql_values, $task_id); $sql_keys_string = join(', ', $sql_keys); $sql_placeholder = $db->fill_placeholders($sql_values); $result = $db->Query("INSERT INTO {tasks}\n ({$sql_keys_string})\n VALUES ({$sql_placeholder})", $sql_values); $task_id = $db->Insert_ID(); Backend::upload_links($task_id); // create tags if (isset($args['tags'])) { $tagList = explode(';', $args['tags']); $tagList = array_map('strip_tags', $tagList); $tagList = array_map('trim', $tagList); $tagList = array_unique($tagList); # avoid duplicates for inputs like: "tag1;tag1" or "tag1; tag1<p></p>" foreach ($tagList as $tag) { if ($tag == '') { continue; } # old tag feature #$result2 = $db->Query("INSERT INTO {tags} (task_id, tag) VALUES (?,?)",array($task_id,$tag)); # new tag feature. let's do it in 2 steps, it is getting too complicated to make it cross database compatible, drawback is possible (rare) race condition (use transaction?) $res = $db->Query("SELECT tag_id FROM {list_tag} WHERE (project_id=0 OR project_id=?) AND tag_name LIKE ? ORDER BY project_id", array($proj->id, $tag)); if ($t = $db->FetchRow($res)) { $tag_id = $t['tag_id']; } else { if ($proj->prefs['freetagging'] == 1) { # add to taglist of the project $db->Query("INSERT INTO {list_tag} (project_id,tag_name) VALUES (?,?)", array($proj->id, $tag)); $tag_id = $db->Insert_ID(); } else { continue; } } $db->Query("INSERT INTO {task_tag}(task_id,tag_id) VALUES(?,?)", array($task_id, $tag_id)); } } // Log the assignments and send notifications to the assignees if (isset($args['rassigned_to']) && is_array($args['rassigned_to'])) { // Convert assigned_to and store them in the 'assigned' table foreach ($args['rassigned_to'] as $val) { $db->Replace('{assigned}', array('user_id' => $val, 'task_id' => $task_id), array('user_id', 'task_id')); } // Log to task history Flyspray::logEvent($task_id, 14, implode(' ', $args['rassigned_to'])); // Notify the new assignees what happened. This obviously won't happen if the task is now assigned to no-one. $notify->Create(NOTIFY_NEW_ASSIGNEE, $task_id, null, $notify->SpecificAddresses($args['rassigned_to']), NOTIFY_BOTH, $proj->prefs['lang_code']); } // Log that the task was opened Flyspray::logEvent($task_id, 1); $result = $db->Query('SELECT * FROM {list_category} WHERE category_id = ?', array($args['product_category'])); $cat_details = $db->FetchRow($result); // We need to figure out who is the category owner for this task if (!empty($cat_details['category_owner'])) { $owner = $cat_details['category_owner']; } else { // check parent categories $result = $db->Query('SELECT * FROM {list_category} WHERE lft < ? AND rgt > ? AND project_id = ? ORDER BY lft DESC', array($cat_details['lft'], $cat_details['rgt'], $cat_details['project_id'])); while ($row = $db->FetchRow($result)) { // If there's a parent category owner, send to them if (!empty($row['category_owner'])) { $owner = $row['category_owner']; break; } } } if (!isset($owner)) { $owner = $proj->prefs['default_cat_owner']; } if ($owner) { if ($proj->prefs['auto_assign'] && ($args['item_status'] == STATUS_UNCONFIRMED || $args['item_status'] == STATUS_NEW)) { Backend::add_to_assignees($owner, $task_id, true); } Backend::add_notification($owner, $task_id, true); } // Reminder for due_date field if (!empty($sql_args['due_date'])) { Backend::add_reminder($task_id, L('defaultreminder') . "\n\n" . CreateURL('details', $task_id), 2 * 24 * 60 * 60, time()); } // Create the Notification if (Backend::upload_files($task_id)) { $notify->Create(NOTIFY_TASK_OPENED, $task_id, 'files', null, NOTIFY_BOTH, $proj->prefs['lang_code']); } else { $notify->Create(NOTIFY_TASK_OPENED, $task_id, null, null, NOTIFY_BOTH, $proj->prefs['lang_code']); } // If the reporter wanted to be added to the notification list if (isset($args['notifyme']) && $args['notifyme'] == '1' && $user->id != $owner) { Backend::add_notification($user->id, $task_id, true); } if ($user->isAnon()) { $anonuser = array(); $anonuser[$email] = array('recipient' => $args['anon_email'], 'lang' => $fs->prefs['lang_code']); $recipients = array($anonuser); $notify->Create(NOTIFY_ANON_TASK, $task_id, $token, $recipients, NOTIFY_EMAIL, $proj->prefs['lang_code']); } return array($task_id, $token); }
| It also shows comments, attachments, notifications etc. | \*************************************************************/ if (!defined('IN_FS')) { die('Do not access this file directly.'); } $task_id = Req::num('task_id'); if (!($task_details = Flyspray::GetTaskDetails($task_id))) { Flyspray::show_error(10); } if (!$user->can_view_task($task_details)) { Flyspray::show_error($user->isAnon() ? 102 : 101, false); } else { require_once BASEDIR . '/includes/events.inc.php'; if ($proj->prefs['use_effort_tracking']) { require_once BASEDIR . '/includes/class.effort.php'; $effort = new effort($task_id, $user->id); $effort->populateDetails(); $page->assign('effort', $effort); } $page->uses('task_details'); // Send user variables to the template $page->assign('assigned_users', $task_details['assigned_to']); $page->assign('old_assigned', implode(' ', $task_details['assigned_to'])); $page->assign('tags', $task_details['tags']); $page->setTitle(sprintf('FS#%d : %s', $task_details['task_id'], $task_details['item_summary'])); if ((Get::val('edit') || Post::has('item_summary') && !isset($_SESSION['SUCCESS'])) && $user->can_edit_task($task_details)) { $result = $db->Query(' SELECT g.project_id, u.user_id, u.user_name, u.real_name, g.group_id, g.group_name FROM {users} u JOIN {users_in_groups} uig ON u.user_id = uig.user_id JOIN {groups} g ON g.group_id = uig.group_id
function tpl_draw_cell($task, $colname, $format = "<td class='%s'>%s</td>") { global $fs, $proj, $page, $user; $indexes = array('id' => 'task_id', 'project' => 'project_title', 'tasktype' => 'task_type', 'tasktypename' => 'tasktype_name', 'category' => 'category_name', 'severity' => '', 'priority' => '', 'summary' => 'item_summary', 'dateopened' => 'date_opened', 'status' => 'status_name', 'openedby' => 'opened_by_name', 'assignedto' => 'assigned_to_name', 'lastedit' => 'max_date', 'reportedin' => 'product_version_name', 'dueversion' => 'closedby_version_name', 'duedate' => 'due_date', 'comments' => 'num_comments', 'votes' => 'num_votes', 'attachments' => 'num_attachments', 'dateclosed' => 'date_closed', 'progress' => '', 'os' => 'os_name', 'private' => 'mark_private', 'parent' => 'supertask_id', 'estimatedeffort' => 'estimated_effort'); //must be an array , must contain elements and be alphanumeric (permitted "_") if (!is_array($task) || empty($task) || preg_match('![^A-Za-z0-9_]!', $colname)) { //run away.. return ''; } $class = 'task_' . $colname; switch ($colname) { case 'id': $value = tpl_tasklink($task, $task['task_id']); break; case 'summary': $value = tpl_tasklink($task, utf8_substr($task['item_summary'], 0, 55)); if (utf8_strlen($task['item_summary']) > 55) { $value .= '...'; } break; case 'tasktype': $value = $task['tasktype_name']; $class .= ' typ' . $task['task_type']; break; case 'severity': $value = $task['task_severity'] == 0 ? '' : $fs->severities[$task['task_severity']]; $class .= ' sev' . $task['task_severity']; break; case 'priority': $value = $task['task_priority'] == 0 ? '' : $fs->priorities[$task['task_priority']]; $class .= ' pri' . $task['task_priority']; break; case 'lastedit': case 'duedate': case 'dateopened': case 'dateclosed': $value = formatDate($task[$indexes[$colname]]); break; case 'status': if ($task['is_closed']) { $value = eL('closed'); } else { $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8'); } break; case 'progress': $value = tpl_img($page->get_image('percent-' . $task['percent_complete'], false), $task['percent_complete'] . '%'); break; case 'assignedto': $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8'); if ($task['num_assigned'] > 1) { $value .= ', +' . ($task['num_assigned'] - 1); } break; case 'private': $value = $task[$indexes[$colname]] ? L('yes') : L('no'); break; case 'parent': $value = ''; if ($task['supertask_id'] > 0) { $value = tpl_tasklink($task, $task['supertask_id']); } break; case 'estimatedeffort': $value = ''; if ($user->perms('view_estimated_effort')) { if ($task['estimated_effort'] > 0) { $value = effort::SecondsToString($task['estimated_effort'], $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); } } break; case 'effort': $value = ''; if ($user->perms('view_current_effort_done')) { if ($task['effort'] > 0) { $value = effort::SecondsToString($task['effort'], $proj->prefs['hours_per_manday'], $proj->prefs['current_effort_done_format']); } } break; default: $value = ''; // $colname here is NOT column name in database but a name that can appear // both in a projects visible fields and as a key in language translation // file, which is also used to draw a localized heading. Column names in // database customarily use _ t to separate words, translation file entries // instead do not and can be also be quite different. If you do see an empty // value when you expected something, check your usage, what visible fields // in database actually constains, and maybe add a mapping from $colname to // to the database column name to array $indexes at the beginning of this // function. Note that inconsistencies between $colname, database column // name, translation entry key and name in visible fields do occur sometimes // during development phase. if (array_key_exists($colname, $indexes)) { $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8'); } break; } return sprintf($format, $class, $value); }
/** * XXX: A mess,remove my in 1.0. No time for that, sorry. */ function event_description($history) { $return = ''; global $fs, $baseurl, $details, $proj; $translate = array('item_summary' => 'summary', 'project_id' => 'attachedtoproject', 'task_type' => 'tasktype', 'product_category' => 'category', 'item_status' => 'status', 'task_priority' => 'priority', 'operating_system' => 'operatingsystem', 'task_severity' => 'severity', 'product_version' => 'reportedversion', 'mark_private' => 'visibility', 'estimated_effort' => 'estimatedeffort'); // if somehing gets double escaped, add it here. $noescape = array('new_value', 'old_value'); foreach ($history as $key => $value) { if (!in_array($key, $noescape)) { $history[$key] = Filters::noXSS($value); } } $new_value = $history['new_value']; $old_value = $history['old_value']; switch ($history['event_type']) { case '3': //Field changed if (!$new_value && !$old_value) { $return .= eL('taskedited'); break; } $field = $history['field_changed']; switch ($field) { case 'item_summary': case 'project_id': case 'task_type': case 'product_category': case 'item_status': case 'task_priority': case 'operating_system': case 'task_severity': case 'product_version': if ($field == 'task_priority') { $old_value = $fs->priorities[$old_value]; $new_value = $fs->priorities[$new_value]; } elseif ($field == 'task_severity') { $old_value = $fs->severities[$old_value]; $new_value = $fs->severities[$new_value]; } elseif ($field == 'item_summary') { $old_value = Filters::noXSS($old_value); $new_value = Filters::noXSS($new_value); } else { $old_value = $history[$field . '1']; $new_value = $history[$field . '2']; } $field = eL($translate[$field]); break; case 'closedby_version': $field = eL('dueinversion'); $old_value = $old_value == '0' ? eL('undecided') : $history['product_version1']; $new_value = $new_value == '0' ? eL('undecided') : $history['product_version2']; break; case 'due_date': $field = eL('duedate'); $old_value = formatDate($old_value, false, eL('undecided')); $new_value = formatDate($new_value, false, eL('undecided')); break; case 'percent_complete': $field = eL('percentcomplete'); $old_value .= '%'; $new_value .= '%'; break; case 'mark_private': $field = eL($translate[$field]); if ($old_value == 1) { $old_value = eL('private'); } else { $old_value = eL('public'); } if ($new_value == 1) { $new_value = eL('private'); } else { $new_value = eL('public'); } break; case 'detailed_desc': $field = "<a href=\"javascript:getHistory('{$history['task_id']}', '{$baseurl}', 'history', '{$history['history_id']}');showTabById('history', true);\">" . eL('details') . '</a>'; if (!empty($details)) { $details_previous = TextFormatter::render($old_value); $details_new = TextFormatter::render($new_value); } $old_value = ''; $new_value = ''; break; case 'estimated_effort': $field = eL($translate[$field]); $old_value = effort::SecondsToString($old_value, $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); $new_value = effort::SecondsToString($new_value, $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); break; } $return .= eL('fieldchanged') . ": {$field}"; if ($old_value || $new_value) { $return .= " ({$old_value} → {$new_value})"; } break; case '1': //Task opened $return .= eL('taskopened'); break; case '2': //Task closed $return .= eL('taskclosed'); $return .= " ({$history['resolution_name']}"; if (!empty($old_value)) { $return .= ': ' . TextFormatter::render($old_value, true); } $return .= ')'; break; case '4': //Comment added $return .= '<a href="#comments">' . eL('commentadded') . '</a>'; break; case '5': //Comment edited $return .= "<a href=\"javascript:getHistory('{$history['task_id']}', '{$baseurl}', 'history', '{$history['history_id']}');\">" . eL('commentedited') . "</a>"; if ($history['c_date_added']) { $return .= " (" . eL('commentby') . ' ' . tpl_userlink($history['c_user_id']) . " - " . formatDate($history['c_date_added'], true) . ")"; } if ($details) { $details_previous = TextFormatter::render($old_value); $details_new = TextFormatter::render($new_value); } break; case '6': //Comment deleted $return .= "<a href=\"javascript:getHistory('{$history['task_id']}', '{$baseurl}', 'history', '{$history['history_id']}');\">" . eL('commentdeleted') . "</a>"; if ($new_value != '' && $history['field_changed'] != '') { $return .= " (" . eL('commentby') . ' ' . tpl_userlink($new_value) . " - " . formatDate($history['field_changed'], true) . ")"; } if (!empty($details)) { $details_previous = TextFormatter::render($old_value); $details_new = ''; } break; case '7': //Attachment added $return .= eL('attachmentadded'); if ($history['orig_name']) { $return .= ": <a href=\"{$baseurl}?getfile=" . intval($new_value) . '">' . "{$history['orig_name']}</a>"; } else { if ($history['old_value']) { $return .= ': ' . $history['old_value']; } } break; case '8': //Attachment deleted $return .= eL('attachmentdeleted') . ': ' . Filters::noXSS($new_value); break; case '9': //Notification added $return .= eL('notificationadded') . ': ' . tpl_userlink($new_value); break; case '10': //Notification deleted $return .= eL('notificationdeleted') . ': ' . tpl_userlink($new_value); break; case '11': //Related task added $return .= eL('relatedadded') . ': ' . tpl_tasklink($new_value); break; case '12': //Related task deleted $return .= eL('relateddeleted') . ': ' . tpl_tasklink($new_value); break; case '13': //Task reopened $return .= eL('taskreopened'); break; case '14': //Task assigned if (empty($old_value)) { $users = explode(' ', trim($new_value)); $users = array_map('tpl_userlink', $users); $return .= eL('taskassigned') . ' '; $return .= implode(', ', $users); } elseif (empty($new_value)) { $return .= eL('assignmentremoved'); } else { $users = explode(' ', trim($new_value)); $users = array_map('tpl_userlink', $users); $return .= eL('taskreassigned') . ' '; $return .= implode(', ', $users); } break; // Mentioned in docs, not used anywhere. Will implement if suitable // translations already exist, otherwise leave to 1.1. (Found translations) // Mentioned in docs, not used anywhere. Will implement if suitable // translations already exist, otherwise leave to 1.1. (Found translations) case '15': // This task was added to another task's related list $return .= eL('addedasrelated') . ': ' . tpl_tasklink($new_value); break; case '16': // This task was removed from another task's related list $return .= eL('deletedasrelated') . ': ' . tpl_tasklink($new_value); break; case '17': //Reminder added $return .= eL('reminderadded') . ': ' . tpl_userlink($new_value); break; case '18': //Reminder deleted $return .= eL('reminderdeleted') . ': ' . tpl_userlink($new_value); break; case '19': //User took ownership $return .= eL('ownershiptaken') . ': ' . tpl_userlink($new_value); break; case '20': //User requested task closure $return .= eL('closerequestmade') . ' - ' . $new_value; break; case '21': //User requested task $return .= eL('reopenrequestmade') . ' - ' . $new_value; break; case '22': // Dependency added $return .= eL('depadded') . ' ' . tpl_tasklink($new_value); break; case '23': // Dependency added to other task $return .= eL('depaddedother') . ' ' . tpl_tasklink($new_value); break; case '24': // Dependency removed $return .= eL('depremoved') . ' ' . tpl_tasklink($new_value); break; case '25': // Dependency removed from other task $return .= eL('depremovedother') . ' ' . tpl_tasklink($new_value); break; // 26 and 27 replaced by 0 (mark_private) // 26 and 27 replaced by 0 (mark_private) case '28': // PM request denied $return .= eL('pmreqdenied') . ' - ' . $new_value; break; case '29': // User added to assignees list $return .= eL('addedtoassignees'); break; case '30': // user created $return .= eL('usercreated'); break; case '31': // user deleted $return .= eL('userdeleted'); break; case '32': // Subtask added $return .= eL('subtaskadded') . ' ' . tpl_tasklink($new_value); break; case '33': // Subtask removed $return .= eL('subtaskremoved') . ' ' . tpl_tasklink($new_value); break; case '34': // supertask added $return .= eL('supertaskadded') . ' ' . tpl_tasklink($new_value); break; case '35': // supertask removed $return .= eL('supertaskremoved') . ' ' . tpl_tasklink($new_value); break; } if (isset($details_previous)) { $GLOBALS['details_previous'] = $details_previous; } if (isset($details_new)) { $GLOBALS['details_new'] = $details_new; } return $return; }
if ($proj->prefs['use_effort_tracking'] && $user->perms('track_effort')) { $allowedFields[] = 'estimated_effort'; } if (!in_array(Post::val('name'), $allowedFields)) { header(':', true, 403); die(L('invalidfield')); } $value = Post::val('value'); # check if user is not sending manipulated invalid values switch (Post::val('name')) { case 'due_date': $value = Flyspray::strtotime(Post::val('value')); $value = intval($value); break; case 'estimated_effort': $value = effort::EditStringToSeconds(Post::val('value'), $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); $value = intval($value); break; case 'task_priority': case 'task_severity': if (!preg_match("/^[1-5]\$/", $value)) { header(':', true, 403); die(L('invalidvalue')); } break; case 'percent_complete': if (!is_numeric($value) || $value < 0 || $value > 100) { header(':', true, 403); die(L('invalidvalue')); } break;
function tpl_draw_cell($task, $colname, $format = "<td class='%s'>%s</td>") { global $fs, $db, $proj, $page, $user; $indexes = array('id' => 'task_id', 'project' => 'project_title', 'tasktype' => 'task_type', 'tasktypename' => 'tasktype_name', 'category' => 'category_name', 'severity' => '', 'priority' => '', 'summary' => 'item_summary', 'dateopened' => 'date_opened', 'status' => 'status_name', 'openedby' => 'opened_by', 'openedbyname' => 'opened_by_name', 'assignedto' => 'assigned_to_name', 'lastedit' => 'max_date', 'editedby' => 'last_edited_by', 'reportedin' => 'product_version_name', 'dueversion' => 'closedby_version_name', 'duedate' => 'due_date', 'comments' => 'num_comments', 'votes' => 'num_votes', 'attachments' => 'num_attachments', 'dateclosed' => 'date_closed', 'closedby' => 'closed_by', 'commentedby' => 'commented_by', 'progress' => '', 'os' => 'os_name', 'private' => 'mark_private', 'parent' => 'supertask_id', 'estimatedeffort' => 'estimated_effort'); //must be an array , must contain elements and be alphanumeric (permitted "_") if (!is_array($task) || empty($task) || preg_match('![^A-Za-z0-9_]!', $colname)) { //run away.. return ''; } $class = 'task_' . $colname; switch ($colname) { case 'id': $value = tpl_tasklink($task, $task['task_id']); break; case 'summary': $value = tpl_tasklink($task, utf8_substr($task['item_summary'], 0, 55)); if (utf8_strlen($task['item_summary']) > 55) { $value .= '...'; } # <i> instead of <span> in future for smaller size # we need also some bytes for classes like <i class="tag t123">tagname</i> if ($task['tags'] != '') { $tags = explode(',', $task['tags']); $tagids = explode(',', $task['tagids']); $tagclass = explode(',', $task['tagclass']); $tgs = ''; for ($i = 0; $i < count($tags); $i++) { $tgs .= '<i class="tag t' . $tagids[$i] . ($tagclass[$i] ? ' ' . $tagclass[$i] : '') . '" title="' . $tags[$i] . '"></i>'; } $value .= $tgs; } break; case 'tasktype': $value = $task['tasktype_name']; $class .= ' typ' . $task['task_type']; break; case 'severity': $value = $task['task_severity'] == 0 ? '' : $fs->severities[$task['task_severity']]; $class .= ' sev' . $task['task_severity']; break; case 'priority': $value = $task['task_priority'] == 0 ? '' : $fs->priorities[$task['task_priority']]; $class .= ' pri' . $task['task_priority']; break; case 'lastedit': case 'duedate': case 'dateopened': case 'dateclosed': $value = formatDate($task[$indexes[$colname]]); break; case 'status': if ($task['is_closed']) { $value = eL('closed'); } else { $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8'); } break; case 'progress': $value = tpl_img($page->get_image('percent-' . $task['percent_complete'], false), $task['percent_complete'] . '%'); break; case 'assignedto': # group_concat-ed for mysql/pgsql #$value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8'); $value = ''; $anames = explode(',', $task[$indexes[$colname]]); $aids = explode(',', $task['assignedids']); $aimages = explode(',', $task['assigned_image']); for ($a = 0; $a < count($anames); $a++) { if ($aids[$a]) { if ($fs->prefs['enable_avatars'] == 1 && $aimages[$a]) { $value .= tpl_userlinkavatar($aids[$a], 30); } else { $value .= tpl_userlink($aids[$a]); } #$value.='<a href="'.$aids[$a].'">'.htmlspecialchars($anames[$a], ENT_QUOTES, 'utf-8').'</a>'; } } # fallback for DBs we haven't written sql string aggregation yet (currently with group_concat() mysql and array_agg() postgresql) if ('postgres' != $db->dblink->dataProvider && 'mysql' != $db->dblink->dataProvider && $task['num_assigned'] > 1) { $value .= ', +' . ($task['num_assigned'] - 1); } break; case 'private': $value = $task[$indexes[$colname]] ? L('yes') : L('no'); break; case 'commentedby': case 'openedby': case 'editedby': case 'closedby': $value = ''; # a bit expensive! tpl_userlinkavatar() an additional sql query for each new user in the output table # at least tpl_userlink() uses a $cache array so query for repeated users if ($task[$indexes[$colname]] > 0) { if ($fs->prefs['enable_avatars'] == 1) { $value = tpl_userlinkavatar($task[$indexes[$colname]], 30); } else { $value = tpl_userlink($task[$indexes[$colname]]); } } break; case 'parent': $value = ''; if ($task['supertask_id'] > 0) { $value = tpl_tasklink($task, $task['supertask_id']); } break; case 'estimatedeffort': $value = ''; if ($user->perms('view_estimated_effort')) { if ($task['estimated_effort'] > 0) { $value = effort::SecondsToString($task['estimated_effort'], $proj->prefs['hours_per_manday'], $proj->prefs['estimated_effort_format']); } } break; case 'effort': $value = ''; if ($user->perms('view_current_effort_done')) { if ($task['effort'] > 0) { $value = effort::SecondsToString($task['effort'], $proj->prefs['hours_per_manday'], $proj->prefs['current_effort_done_format']); } } break; default: $value = ''; // $colname here is NOT column name in database but a name that can appear // both in a projects visible fields and as a key in language translation // file, which is also used to draw a localized heading. Column names in // database customarily use _ t to separate words, translation file entries // instead do not and can be also be quite different. If you do see an empty // value when you expected something, check your usage, what visible fields // in database actually constains, and maybe add a mapping from $colname to // to the database column name to array $indexes at the beginning of this // function. Note that inconsistencies between $colname, database column // name, translation entry key and name in visible fields do occur sometimes // during development phase. if (array_key_exists($colname, $indexes)) { $value = htmlspecialchars($task[$indexes[$colname]], ENT_QUOTES, 'utf-8'); } break; } return sprintf($format, $class, $value); }