[ 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.round_robin.php 3246 2007-02-09 09:10:12Z glen $ 29 // 30 31 require_once (APP_INC_PATH . 'class.date.php'); 32 require_once (APP_INC_PATH . 'class.error_handler.php'); 33 34 class Round_Robin 35 { 36 /** 37 * Returns the blackout dates according to the user's timezone. 38 * 39 * @access public 40 * @param object $user The Date object associated with the user's timezone 41 * @param integer $start The blackout start hour 42 * @param integer $end The blackout end hour 43 * @return array The blackout dates 44 */ 45 function getBlackoutDates(&$user, $start, $end) 46 { 47 $start = substr($start, 0, 2); 48 $end = substr($end, 0, 2); 49 50 // if start is AM and end is PM, then use only today 51 // if start is AM and end is AM (and end is smaller than start), then use today and tomorrow 52 // - if date is between zero and the end, then use yesterday and today 53 // if start is AM and end is AM (and end is bigger than start), then use only today 54 // if start is PM and end is PM (and end is smaller than start), then use today and tomorrow 55 // - if date is between zero and the end, then use yesterday and today 56 // if start is PM and end is PM (and end is bigger than start), then use only today 57 // if start is PM and end is AM, then use today and tomorrow 58 // - if date is between zero and the end, then use yesterday and today 59 if ((Date_API::isAM($start)) && (Date_API::isPM($end))) { 60 $first = 0; 61 $second = 0; 62 } 63 if ((Date_API::isAM($start)) && (Date_API::isAM($end)) && ($end < $start)) { 64 if (($user->getHour() >= 0) && ($user->getHour() <= $end)) { 65 $first = -DAY; 66 $second = 0; 67 } else { 68 $first = 0; 69 $second = DAY; 70 } 71 } 72 if ((Date_API::isAM($start)) && (Date_API::isAM($end)) && ($end > $start)) { 73 $first = 0; 74 $second = 0; 75 } 76 if ((Date_API::isPM($start)) && (Date_API::isPM($end)) && ($end < $start)) { 77 if (($user->getHour() >= 0) && ($user->getHour() <= $end)) { 78 $first = -DAY; 79 $second = 0; 80 } else { 81 $first = 0; 82 $second = DAY; 83 } 84 } 85 if ((Date_API::isPM($start)) && (Date_API::isPM($end)) && ($end > $start)) { 86 $first = 0; 87 $second = 0; 88 } 89 if ((Date_API::isPM($start)) && (Date_API::isAM($end))) { 90 if (($user->getHour() >= 0) && ($user->getHour() <= $end)) { 91 $first = -DAY; 92 $second = 0; 93 } else { 94 $first = 0; 95 $second = DAY; 96 } 97 } 98 99 return array( 100 date('Y-m-d', $user->getDate(DATE_FORMAT_UNIXTIME) + $first), 101 date('Y-m-d', $user->getDate(DATE_FORMAT_UNIXTIME) + $second) 102 ); 103 } 104 105 106 /** 107 * Retrieves the next assignee in the given project's round robin queue. 108 * 109 * @access public 110 * @param integer $prj_id The project ID 111 * @return integer The assignee's user ID 112 */ 113 function getNextAssignee($prj_id) 114 { 115 // get the full list of users for the given project 116 list($blackout_start, $blackout_end, $users) = Round_Robin::getUsersByProject($prj_id); 117 if (count($users) == 0) { 118 return 0; 119 } else { 120 $user_ids = array_keys($users); 121 $next_usr_id = 0; 122 foreach ($users as $usr_id => $details) { 123 if ($details['is_next']) { 124 $next_usr_id = $usr_id; 125 break; 126 } 127 } 128 // if no user is currently set as the 'next' assignee, 129 // then just get the first one in the list 130 if (empty($next_usr_id)) { 131 $next_usr_id = $user_ids[0]; 132 } 133 // counter to keep the number of times we found an invalid user 134 $ignored_users = 0; 135 // check the blackout hours 136 do { 137 $user = new Date(Date_API::getCurrentUnixTimestampGMT()); 138 $user->convertTZById($users[$next_usr_id]['timezone']); 139 list($today, $tomorrow) = Round_Robin::getBlackoutDates($user, $blackout_start, $blackout_end); 140 $first = new Date($today . ' ' . $blackout_start); 141 $first->setTZById($users[$next_usr_id]['timezone']); 142 $second = new Date($tomorrow . ' ' . $blackout_end); 143 $second->setTZById($users[$next_usr_id]['timezone']); 144 if ((Date::compare($first, $user) == -1) && (Date::compare($user, $second) == -1)) { 145 $ignored_users++; 146 $current_index = array_search($next_usr_id, $user_ids); 147 // if we reached the end of the list of users and none of them 148 // was a valid one, then just select the first one 149 // however, we want to complete at least one full iteration over the list of users 150 // that is, if we didn't start checking the users in the beginning of the list, 151 // then do another run over the users just in case 152 if (($ignored_users >= count($user_ids)) && ($current_index == (count($user_ids) - 1))) { 153 $assignee = $user_ids[0]; 154 break; 155 } 156 // if we reached the end of the list, and we still didn't find an user, 157 // then go back to the beginning of the list one last time 158 if ($current_index == (count($user_ids) - 1)) { 159 $current_index = 0; 160 $next_usr_id = $user_ids[++$current_index]; 161 $found = 0; 162 continue; 163 } 164 $next_usr_id = $user_ids[++$current_index]; 165 $found = 0; 166 } else { 167 $assignee = $next_usr_id; 168 $found = 1; 169 } 170 } while (!$found); 171 // mark the next user in the list as the 'next' assignment 172 $assignee_index = array_search($assignee, $user_ids); 173 if ($assignee_index == (count($user_ids) -1)) { 174 $next_assignee = $user_ids[0]; 175 } else { 176 $next_assignee = $user_ids[++$assignee_index]; 177 } 178 Round_Robin::markNextAssignee($prj_id, $next_assignee); 179 return $assignee; 180 } 181 } 182 183 184 /** 185 * Marks the next user in the round robin list as the next assignee in the 186 * round robin queue. 187 * 188 * @access public 189 * @param integer $prj_id The project ID 190 * @param integer $usr_id The assignee's user ID 191 * @return boolean 192 */ 193 function markNextAssignee($prj_id, $usr_id) 194 { 195 $prr_id = Round_Robin::getID($prj_id); 196 $stmt = "UPDATE 197 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user 198 SET 199 rru_next=0 200 WHERE 201 rru_prr_id=$prr_id"; 202 $res = $GLOBALS["db_api"]->dbh->query($stmt); 203 if (PEAR::isError($res)) { 204 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 205 return false; 206 } else { 207 $stmt = "UPDATE 208 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user 209 SET 210 rru_next=1 211 WHERE 212 rru_usr_id=" . Misc::escapeInteger($usr_id) . " AND 213 rru_prr_id=$prr_id"; 214 $res = $GLOBALS["db_api"]->dbh->query($stmt); 215 if (PEAR::isError($res)) { 216 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 217 return false; 218 } else { 219 return true; 220 } 221 } 222 } 223 224 225 /** 226 * Returns the round robin entry ID associated with a given project. 227 * 228 * @access public 229 * @param integer $prj_id The project ID 230 * @return integer The round robin entry ID 231 */ 232 function getID($prj_id) 233 { 234 $stmt = "SELECT 235 prr_id 236 FROM 237 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin 238 WHERE 239 prr_prj_id=" . Misc::escapeInteger($prj_id); 240 $res = $GLOBALS["db_api"]->dbh->getOne($stmt); 241 if (PEAR::isError($res)) { 242 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 243 return ""; 244 } else { 245 return $res; 246 } 247 } 248 249 250 /** 251 * Retrieves the list of users, round robin blackout hours and their 252 * respective preferences with regards to timezones. 253 * 254 * @access public 255 * @param integer $prj_id The project ID 256 * @return array The list of users 257 */ 258 function getUsersByProject($prj_id) 259 { 260 $stmt = "SELECT 261 usr_id, 262 usr_preferences, 263 rru_next, 264 prr_blackout_start, 265 prr_blackout_end 266 FROM 267 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin, 268 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user, 269 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 270 WHERE 271 prr_prj_id=" . Misc::escapeInteger($prj_id) . " AND 272 prr_id=rru_prr_id AND 273 rru_usr_id=usr_id 274 ORDER BY 275 usr_id ASC"; 276 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 277 if (PEAR::isError($res)) { 278 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 279 return array(); 280 } else { 281 $blackout_start = ''; 282 $blackout_end = ''; 283 $t = array(); 284 for ($i = 0; $i < count($res); $i++) { 285 $blackout_start = $res[$i]['prr_blackout_start']; 286 $blackout_end = $res[$i]['prr_blackout_end']; 287 $prefs = unserialize($res[$i]['usr_preferences']); 288 $t[$res[$i]['usr_id']] = array( 289 'timezone' => $prefs['timezone'], 290 'is_next' => $res[$i]['rru_next'] 291 ); 292 } 293 return array( 294 $blackout_start, 295 $blackout_end, 296 $t 297 ); 298 } 299 } 300 301 302 /** 303 * Creates a new round robin entry. 304 * 305 * @access public 306 * @return integer 1 if the creation worked, -1 otherwise 307 */ 308 function insert() 309 { 310 $blackout_start = $_POST['blackout_start']['Hour'] . ':' . $_POST['blackout_start']['Minute'] . ':00'; 311 $blackout_end = $_POST['blackout_end']['Hour'] . ':' . $_POST['blackout_end']['Minute'] . ':00'; 312 $stmt = "INSERT INTO 313 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin 314 ( 315 prr_prj_id, 316 prr_blackout_start, 317 prr_blackout_end 318 ) VALUES ( 319 " . Misc::escapeInteger($_POST["project"]) . ", 320 '" . Misc::escapeString($blackout_start) . "', 321 '" . Misc::escapeString($blackout_end) . "' 322 )"; 323 $res = $GLOBALS["db_api"]->dbh->query($stmt); 324 if (PEAR::isError($res)) { 325 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 326 return -1; 327 } else { 328 $new_id = $GLOBALS["db_api"]->get_last_insert_id(); 329 // add all of the user associated with this round robin entry 330 foreach ($_POST['users'] as $usr_id) { 331 Round_Robin::addUserAssociation($new_id, $usr_id); 332 } 333 return 1; 334 } 335 } 336 337 338 /** 339 * Associates a round robin entry with a user ID. 340 * 341 * @access public 342 * @param integer $prr_id The round robin entry ID 343 * @param integer $usr_id The user ID 344 * @return boolean 345 */ 346 function addUserAssociation($prr_id, $usr_id) 347 { 348 $stmt = "INSERT INTO 349 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user 350 ( 351 rru_prr_id, 352 rru_usr_id, 353 rru_next 354 ) VALUES ( 355 " . Misc::escapeInteger($prr_id) . ", 356 " . Misc::escapeInteger($usr_id) . ", 357 0 358 )"; 359 $res = $GLOBALS["db_api"]->dbh->query($stmt); 360 if (PEAR::isError($res)) { 361 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 362 return false; 363 } else { 364 return true; 365 } 366 } 367 368 369 /** 370 * Method used to get the list of round robin entries available in the 371 * system. 372 * 373 * @access public 374 * @return array The list of round robin entries 375 */ 376 function getList() 377 { 378 $stmt = "SELECT 379 prr_id, 380 prj_title 381 FROM 382 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin, 383 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project 384 WHERE 385 prr_prj_id=prj_id 386 ORDER BY 387 prj_title ASC"; 388 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 389 if (PEAR::isError($res)) { 390 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 391 return ""; 392 } else { 393 // get the list of associated users 394 for ($i = 0; $i < count($res); $i++) { 395 $res[$i]['users'] = implode(", ", array_values(Round_Robin::getAssociatedUsers($res[$i]['prr_id']))); 396 } 397 return $res; 398 } 399 } 400 401 402 /** 403 * Returns an associative array in the form of user id => name of the users 404 * associated to a given round robin entry ID. 405 * 406 * @access public 407 * @param integer $prr_id The round robin entry ID 408 * @return array The list of users 409 */ 410 function getAssociatedUsers($prr_id) 411 { 412 $stmt = "SELECT 413 usr_id, 414 usr_full_name 415 FROM 416 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user, 417 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 418 WHERE 419 rru_usr_id=usr_id AND 420 rru_prr_id=" . Misc::escapeInteger($prr_id) . " 421 ORDER BY 422 usr_id ASC"; 423 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 424 if (PEAR::isError($res)) { 425 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 426 return array(); 427 } else { 428 return $res; 429 } 430 } 431 432 433 /** 434 * Method used to get the details of a round robin entry. 435 * 436 * @access public 437 * @param integer $prr_id The round robin entry ID 438 * @return array The round robin entry details 439 */ 440 function getDetails($prr_id) 441 { 442 $stmt = "SELECT 443 * 444 FROM 445 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin 446 WHERE 447 prr_id=" . Misc::escapeInteger($prr_id); 448 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 449 if (PEAR::isError($res)) { 450 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 451 return ""; 452 } else { 453 // get all of the user associations here as well 454 $res['users'] = array_keys(Round_Robin::getAssociatedUsers($res['prr_id'])); 455 return $res; 456 } 457 } 458 459 460 /** 461 * Method used to update a round robin entry in the system. 462 * 463 * @access public 464 * @return integer 1 if the update worked, -1 otherwise 465 */ 466 function update() 467 { 468 $blackout_start = $_POST['blackout_start']['Hour'] . ':' . $_POST['blackout_start']['Minute'] . ':00'; 469 $blackout_end = $_POST['blackout_end']['Hour'] . ':' . $_POST['blackout_end']['Minute'] . ':00'; 470 $stmt = "UPDATE 471 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin 472 SET 473 prr_prj_id=" . Misc::escapeInteger($_POST["project"]) . ", 474 prr_blackout_start='" . Misc::escapeString($blackout_start) . "', 475 prr_blackout_end='" . Misc::escapeString($blackout_end) . "' 476 WHERE 477 prr_id=" . Misc::escapeInteger($_POST["id"]); 478 $res = $GLOBALS["db_api"]->dbh->query($stmt); 479 if (PEAR::isError($res)) { 480 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 481 return -1; 482 } else { 483 // remove all of the associations with users, then add them all again 484 Round_Robin::removeUserAssociations($_POST['id']); 485 foreach ($_POST['users'] as $usr_id) { 486 Round_Robin::addUserAssociation($_POST['id'], $usr_id); 487 } 488 return 1; 489 } 490 } 491 492 493 /** 494 * Method used to remove the user associations for a given round robin 495 * entry ID. 496 * 497 * @access public 498 * @param integer $prr_id The round robin ID 499 * @return boolean 500 */ 501 function removeUserAssociations($prr_id) 502 { 503 if (!is_array($prr_id)) { 504 $prr_id = array($prr_id); 505 } 506 $items = @implode(", ", Misc::escapeInteger($prr_id)); 507 $stmt = "DELETE FROM 508 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "round_robin_user 509 WHERE 510 rru_prr_id IN ($items)"; 511 $res = $GLOBALS["db_api"]->dbh->query($stmt); 512 if (PEAR::isError($res)) { 513 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 514 return false; 515 } else { 516 return true; 517 } 518 } 519 520 521 /** 522 * Method used to remove a round robin entry from the system. 523 * 524 * @access public 525 * @return boolean 526 */ 527 function remove() 528 { 529 $items = @implode(", ", Misc::escapeInteger($_POST["items"])); 530 $stmt = "DELETE FROM 531 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_round_robin 532 WHERE 533 prr_id IN ($items)"; 534 $res = $GLOBALS["db_api"]->dbh->query($stmt); 535 if (PEAR::isError($res)) { 536 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 537 return false; 538 } else { 539 Round_Robin::removeUserAssociations($_POST['items']); 540 return true; 541 } 542 } 543 } 544 545 // benchmarking the included file (aka setup time) 546 if (APP_BENCHMARK) { 547 $GLOBALS['bench']->setMarker('Included Round_Robin Class'); 548 }
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 |