[ Index ] |
PHP Cross Reference of Eventum |
[Summary view] [Print] [Text view]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */ 3 // +----------------------------------------------------------------------+ 4 // | Eventum - Issue Tracking System | 5 // +----------------------------------------------------------------------+ 6 // | Copyright (c) 2003, 2004, 2005, 2006, 2007 MySQL AB | 7 // | | 8 // | This program is free software; you can redistribute it and/or modify | 9 // | it under the terms of the GNU General Public License as published by | 10 // | the Free Software Foundation; either version 2 of the License, or | 11 // | (at your option) any later version. | 12 // | | 13 // | This program is distributed in the hope that it will be useful, | 14 // | but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 // | GNU General Public License for more details. | 17 // | | 18 // | You should have received a copy of the GNU General Public License | 19 // | along with this program; if not, write to: | 20 // | | 21 // | Free Software Foundation, Inc. | 22 // | 59 Temple Place - Suite 330 | 23 // | Boston, MA 02111-1307, USA. | 24 // +----------------------------------------------------------------------+ 25 // | Authors: João Prado Maia <jpm@mysql.com> | 26 // +----------------------------------------------------------------------+ 27 // 28 // @(#) $Id: class.time_tracking.php 3322 2007-04-25 13:08:08Z glen $ 29 // 30 31 require_once (APP_INC_PATH . "class.error_handler.php"); 32 require_once (APP_INC_PATH . "class.misc.php"); 33 require_once (APP_INC_PATH . "class.validation.php"); 34 require_once (APP_INC_PATH . "class.date.php"); 35 require_once (APP_INC_PATH . "class.issue.php"); 36 require_once (APP_INC_PATH . "class.auth.php"); 37 require_once (APP_INC_PATH . "class.user.php"); 38 require_once (APP_INC_PATH . "class.history.php"); 39 40 /** 41 * Class to handle the business logic related to the administration 42 * of time tracking categories in the system. 43 * 44 * @version 1.0 45 * @author João Prado Maia <jpm@mysql.com> 46 */ 47 48 class Time_Tracking 49 { 50 /** 51 * Method used to get the ID of a given category. 52 * 53 * @access public 54 * @param string $ttc_title The time tracking category title 55 * @return integerThe time tracking category ID 56 */ 57 function getCategoryID($ttc_title) 58 { 59 $stmt = "SELECT 60 ttc_id 61 FROM 62 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 63 WHERE 64 ttc_title='" . Misc::escapeString($ttc_title) . "'"; 65 $res = $GLOBALS["db_api"]->dbh->getOne($stmt); 66 if (PEAR::isError($res)) { 67 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 68 return 0; 69 } else { 70 return $res; 71 } 72 } 73 74 75 /** 76 * Method used to get the details of a time tracking category. 77 * 78 * @access public 79 * @param integer $ttc_id The time tracking category ID 80 * @return array The details of the category 81 */ 82 function getDetails($ttc_id) 83 { 84 $stmt = "SELECT 85 * 86 FROM 87 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 88 WHERE 89 ttc_id=" . Misc::escapeInteger($ttc_id); 90 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 91 if (PEAR::isError($res)) { 92 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 93 return ""; 94 } else { 95 return $res; 96 } 97 } 98 99 100 /** 101 * Method used to remove a specific set of time tracking categories 102 * 103 * @access public 104 * @return boolean 105 */ 106 function remove() 107 { 108 $items = @implode(", ", Misc::escapeInteger($_POST["items"])); 109 $stmt = "DELETE FROM 110 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 111 WHERE 112 ttc_id IN ($items)"; 113 $res = $GLOBALS["db_api"]->dbh->query($stmt); 114 if (PEAR::isError($res)) { 115 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 116 return false; 117 } else { 118 return true; 119 } 120 } 121 122 123 /** 124 * Method used to update a specific time tracking category 125 * 126 * @access public 127 * @return integer 1 if the update worked, -1 otherwise 128 */ 129 function update() 130 { 131 if (Validation::isWhitespace($_POST["title"])) { 132 return -2; 133 } 134 $stmt = "UPDATE 135 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 136 SET 137 ttc_title='" . Misc::escapeString($_POST["title"]) . "' 138 WHERE 139 ttc_id=" . Misc::escapeInteger($_POST["id"]); 140 $res = $GLOBALS["db_api"]->dbh->query($stmt); 141 if (PEAR::isError($res)) { 142 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 143 return -1; 144 } else { 145 return 1; 146 } 147 } 148 149 150 /** 151 * Method used to add a new time tracking category 152 * 153 * @access public 154 * @return integer 1 if the update worked, -1 otherwise 155 */ 156 function insert() 157 { 158 if (Validation::isWhitespace($_POST["title"])) { 159 return -2; 160 } 161 $stmt = "INSERT INTO 162 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 163 ( 164 ttc_title, 165 ttc_created_date 166 ) VALUES ( 167 '" . Misc::escapeString($_POST["title"]) . "', 168 '" . Date_API::getCurrentDateGMT() . "' 169 )"; 170 $res = $GLOBALS["db_api"]->dbh->query($stmt); 171 if (PEAR::isError($res)) { 172 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 173 return -1; 174 } else { 175 return 1; 176 } 177 } 178 179 180 /** 181 * Method used to get the full list of time tracking categories available in 182 * the system exclusing those reserved by the system. 183 * 184 * @access public 185 * @return array The list of categories 186 */ 187 function getList() 188 { 189 $stmt = "SELECT 190 ttc_id, 191 ttc_title 192 FROM 193 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 194 WHERE 195 ttc_title NOT IN ('Note Discussion', 'Email Discussion', 'Telephone Discussion') 196 ORDER BY 197 ttc_title ASC"; 198 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 199 if (PEAR::isError($res)) { 200 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 201 return ""; 202 } else { 203 return $res; 204 } 205 } 206 207 208 /** 209 * Method used to get the full list of time tracking categories as an 210 * associative array in the style of (id => title) 211 * 212 * @access public 213 * @return array The list of categories 214 */ 215 function getAssocCategories() 216 { 217 $stmt = "SELECT 218 ttc_id, 219 ttc_title 220 FROM 221 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 222 ORDER BY 223 ttc_title ASC"; 224 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 225 if (PEAR::isError($res)) { 226 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 227 return ""; 228 } else { 229 return $res; 230 } 231 } 232 233 234 /** 235 * Method used to get the time spent on a given list of issues. 236 * 237 * @access public 238 * @param array $result The result set 239 * @return void 240 */ 241 function getTimeSpentByIssues(&$result) 242 { 243 $ids = array(); 244 for ($i = 0; $i < count($result); $i++) { 245 $ids[] = $result[$i]["iss_id"]; 246 } 247 if (count($ids) == 0) { 248 return false; 249 } 250 $ids = implode(", ", Misc::escapeInteger($ids)); 251 $stmt = "SELECT 252 ttr_iss_id, 253 SUM(ttr_time_spent) 254 FROM 255 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 256 WHERE 257 ttr_iss_id IN ($ids) 258 GROUP BY 259 ttr_iss_id"; 260 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 261 if (PEAR::isError($res)) { 262 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 263 } else { 264 for ($i = 0; $i < count($result); $i++) { 265 @$result[$i]['time_spent'] = $res[$result[$i]['iss_id']]; 266 } 267 } 268 } 269 270 271 /** 272 * Method used to get the total time spent for a specific issue. 273 * 274 * @access public 275 * @param integer $issue_id The issue ID 276 * @return integer The total time spent 277 */ 278 function getTimeSpentByIssue($issue_id) 279 { 280 $stmt = "SELECT 281 SUM(ttr_time_spent) 282 FROM 283 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 284 WHERE 285 ttr_iss_id=" . Misc::escapeInteger($issue_id); 286 $res = $GLOBALS["db_api"]->dbh->getOne($stmt); 287 if (PEAR::isError($res)) { 288 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 289 return 0; 290 } else { 291 return $res; 292 } 293 } 294 295 296 /** 297 * Method used to get the full listing of time entries in the system for a 298 * specific issue 299 * 300 * @access public 301 * @param integer $issue_id The issue ID 302 * @return array The full list of time entries 303 */ 304 function getListing($issue_id) 305 { 306 $stmt = "SELECT 307 ttr_id, 308 ttr_created_date, 309 ttr_summary, 310 ttr_time_spent, 311 ttc_title, 312 ttr_usr_id, 313 usr_full_name 314 FROM 315 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking, 316 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category, 317 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 318 WHERE 319 ttr_ttc_id=ttc_id AND 320 ttr_usr_id=usr_id AND 321 ttr_iss_id=" . Misc::escapeInteger($issue_id) . " 322 ORDER BY 323 ttr_created_date ASC"; 324 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 325 if (PEAR::isError($res)) { 326 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 327 return 0; 328 } else { 329 $total_time_spent = 0; 330 for ($i = 0; $i < count($res); $i++) { 331 $res[$i]["ttr_summary"] = Link_Filter::processText(Issue::getProjectID($issue_id), nl2br(htmlspecialchars($res[$i]["ttr_summary"]))); 332 $res[$i]["formatted_time"] = Misc::getFormattedTime($res[$i]["ttr_time_spent"]); 333 $res[$i]["ttr_created_date"] = Date_API::getFormattedDate($res[$i]["ttr_created_date"]); 334 335 $total_time_spent += $res[$i]["ttr_time_spent"]; 336 } 337 return array( 338 "total_time_spent" => Misc::getFormattedTime($total_time_spent), 339 "list" => $res 340 ); 341 } 342 } 343 344 345 /** 346 * Method used to remove all time entries associated with the specified list 347 * of issues. 348 * 349 * @access public 350 * @param array $ids The list of issues 351 * @return boolean 352 */ 353 function removeByIssues($ids) 354 { 355 $items = @implode(", ", Misc::escapeInteger($ids)); 356 $stmt = "DELETE FROM 357 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 358 WHERE 359 ttr_iss_id IN ($items)"; 360 $res = $GLOBALS["db_api"]->dbh->query($stmt); 361 if (PEAR::isError($res)) { 362 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 363 return false; 364 } else { 365 return true; 366 } 367 } 368 369 370 /** 371 * Method used to remove a specific time entry from the system. 372 * 373 * @access public 374 * @param integer $time_id The time entry ID 375 * @param integer $usr_id The user ID of the person trying to remove this entry 376 * @return integer 1 if the update worked, -1 otherwise 377 */ 378 function removeEntry($time_id, $usr_id) 379 { 380 $time_id = Misc::escapeInteger($time_id); 381 $stmt = "SELECT 382 ttr_iss_id issue_id, 383 ttr_usr_id owner_usr_id 384 FROM 385 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 386 WHERE 387 ttr_id=$time_id"; 388 $details = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 389 // check if the owner is the one trying to remove this entry 390 if (($details['owner_usr_id'] != $usr_id) || (!Issue::canAccess($details['issue_id'], $usr_id))) { 391 return -1; 392 } 393 394 $stmt = "DELETE FROM 395 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 396 WHERE 397 ttr_id=$time_id"; 398 $res = $GLOBALS["db_api"]->dbh->query($stmt); 399 if (PEAR::isError($res)) { 400 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 401 return -1; 402 } else { 403 Issue::markAsUpdated($details['issue_id']); 404 // need to save a history entry for this 405 History::add($details['issue_id'], $usr_id, History::getTypeID('time_removed'), ev_gettext('Time tracking entry removed by %1$s', User::getFullName($usr_id))); 406 return 1; 407 } 408 } 409 410 411 /** 412 * Method used to add a new time entry in the system. 413 * 414 * @access public 415 * @return integer 1 if the update worked, -1 otherwise 416 */ 417 function insertEntry() 418 { 419 if (!empty($_POST["date"])) { 420 // format the date from the form 421 $created_date = sprintf('%04d-%02d-%02d %02d:%02d:%02d', 422 $_POST["date"]["Year"], $_POST["date"]["Month"], 423 $_POST["date"]["Day"], $_POST["date"]["Hour"], 424 $_POST["date"]["Minute"], 0); 425 // convert the date to GMT timezone 426 $created_date = Date_API::getDateGMT($created_date); 427 } else { 428 $created_date = Date_API::getCurrentDateGMT(); 429 } 430 $usr_id = Auth::getUserID(); 431 $stmt = "INSERT INTO 432 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 433 ( 434 ttr_ttc_id, 435 ttr_iss_id, 436 ttr_usr_id, 437 ttr_created_date, 438 ttr_time_spent, 439 ttr_summary 440 ) VALUES ( 441 " . $_POST["category"] . ", 442 " . $_POST["issue_id"] . ", 443 $usr_id, 444 '$created_date', 445 " . Misc::escapeInteger($_POST["time_spent"]) . ", 446 '" . Misc::escapeString($_POST["summary"]) . "' 447 )"; 448 $res = $GLOBALS["db_api"]->dbh->query($stmt); 449 if (PEAR::isError($res)) { 450 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 451 return -1; 452 } else { 453 Issue::markAsUpdated($_POST["issue_id"], 'time added'); 454 // need to save a history entry for this 455 History::add($_POST["issue_id"], $usr_id, History::getTypeID('time_added'), ev_gettext('Time tracking entry submitted by %1$s', User::getFullName($usr_id))); 456 return 1; 457 } 458 } 459 460 461 /** 462 * Method used to remotely record a time tracking entry. 463 * 464 * @access public 465 * @param integer $issue_id The issue ID 466 * @param integer $usr_id The user ID 467 * @param integer $cat_id The time tracking category ID 468 * @param string $summary The summary of the work entry 469 * @param integer $time_spent The time spent in minutes 470 * @return integer 1 if the insert worked, -1 otherwise 471 */ 472 function recordRemoteEntry($issue_id, $usr_id, $cat_id, $summary, $time_spent) 473 { 474 $stmt = "INSERT INTO 475 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 476 ( 477 ttr_ttc_id, 478 ttr_iss_id, 479 ttr_usr_id, 480 ttr_created_date, 481 ttr_time_spent, 482 ttr_summary 483 ) VALUES ( 484 " . Misc::escapeInteger($cat_id) . ", 485 " . Misc::escapeInteger($issue_id) . ", 486 " . Misc::escapeInteger($usr_id) . ", 487 '" . Date_API::getCurrentDateGMT() . "', 488 " . Misc::escapeInteger($time_spent) . ", 489 '" . Misc::escapeString($summary) . "' 490 )"; 491 $res = $GLOBALS["db_api"]->dbh->query($stmt); 492 if (PEAR::isError($res)) { 493 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 494 return -1; 495 } else { 496 Issue::markAsUpdated($issue_id); 497 // need to save a history entry for this 498 History::add($issue_id, $usr_id, History::getTypeID('remote_time_added'), ev_gettext('Time tracking entry submitted remotely by %1$s', User::getFullName($usr_id))); 499 return 1; 500 } 501 } 502 503 504 /** 505 * Returns summary information about all time spent by a user in a specified time frame. 506 * 507 * @access public 508 * @param string $usr_id The ID of the user this report is for. 509 * @param integer The timestamp of the beginning of the report. 510 * @param integer The timestamp of the end of this report. 511 * @return array An array of data containing information about time trackinge 512 */ 513 function getSummaryByUser($usr_id, $start, $end) 514 { 515 $stmt = "SELECT 516 ttc_title, 517 COUNT(ttr_id) as total, 518 SUM(ttr_time_spent) as total_time 519 FROM 520 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking, 521 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 522 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking_category 523 WHERE 524 iss_id = ttr_iss_id AND 525 ttr_ttc_id = ttc_id AND 526 iss_prj_id = " . Auth::getCurrentProject() . " AND 527 ttr_usr_id = " . Misc::escapeInteger($usr_id) . " AND 528 ttr_created_date BETWEEN '" . Misc::escapeString($start) . "' AND '" . Misc::escapeString($end) . "' 529 GROUP BY 530 ttc_title"; 531 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt, false, array(), DB_FETCHMODE_ASSOC); 532 if (PEAR::isError($res)) { 533 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 534 return array(); 535 } else { 536 if (count($res) > 0) { 537 foreach ($res as $index => $row) { 538 $res[$index]["formatted_time"] = Misc::getFormattedTime($res[$index]["total_time"], true); 539 } 540 } 541 return $res; 542 } 543 } 544 545 /** 546 * Method used to get the time spent for a specific issue 547 * at a specific time. 548 * 549 * @access public 550 * @param integer $issue_id The issue ID 551 * @param string $usr_id The ID of the user this report is for. 552 * @param integer The timestamp of the beginning of the report. 553 * @param integer The timestamp of the end of this report. 554 * @return integer The time spent 555 */ 556 function getTimeSpentByIssueAndTime($issue_id, $usr_id, $start, $end) 557 { 558 $stmt = "SELECT 559 SUM(ttr_time_spent) 560 FROM 561 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 562 WHERE 563 ttr_usr_id = " . Misc::escapeInteger($usr_id) . " AND 564 ttr_created_date BETWEEN '" . Misc::escapeString($start) . "' AND '" . Misc::escapeString($end) . "' AND 565 ttr_iss_id=" . Misc::escapeInteger($issue_id); 566 $res = $GLOBALS["db_api"]->dbh->getOne($stmt); 567 if (PEAR::isError($res)) { 568 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 569 return 0; 570 } else { 571 return $res; 572 } 573 } 574 /** 575 * Method used to add time spent on issue to a list of user issues. 576 * 577 * @access private 578 * @param array $res User issues 579 * @param string $usr_id The ID of the user this report is for. 580 * @param integer $start The timestamp of the beginning of the report. 581 * @param integer $end The timestamp of the end of this report. 582 * @return void 583 */ 584 function fillTimeSpentByIssueAndTime(&$res, $usr_id, $start, $end) 585 { 586 587 $issue_ids = array(); 588 for ($i = 0; $i < count($res); $i++) { 589 $issue_ids[] = Misc::escapeInteger($res[$i]["iss_id"]); 590 } 591 $ids = implode(", ", $issue_ids); 592 593 $stmt = "SELECT 594 ttr_iss_id, sum(ttr_time_spent) 595 FROM 596 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "time_tracking 597 WHERE 598 ttr_usr_id = " . Misc::escapeInteger($usr_id) . " AND 599 ttr_created_date BETWEEN '" . Misc::escapeString($start) . "' AND '" . Misc::escapeString($end) . "' AND 600 ttr_iss_id in ($ids) 601 GROUP BY ttr_iss_id"; 602 $result = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 603 604 if (PEAR::isError($result)) { 605 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 606 return 0; 607 } else { 608 foreach($res as $key => $item) { 609 @$res[$key]['it_spent'] = $result[$item['iss_id']]; 610 @$res[$key]['time_spent'] = Misc::getFormattedTime($result[$item['iss_id']], false); 611 } 612 } 613 } 614 } 615 616 // benchmarking the included file (aka setup time) 617 if (APP_BENCHMARK) { 618 $GLOBALS['bench']->setMarker('Included Time_Tracking Class'); 619 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Dec 19 21:21:33 2007 | Cross-referenced by PHPXref 0.7 |