[ 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 29 30 /** 31 * Class to handle all of the business logic related to sending email 32 * notifications on actions regarding the issues. 33 * 34 * @version 1.0 35 * @author João Prado Maia <jpm@mysql.com> 36 */ 37 38 require_once (APP_INC_PATH . "class.error_handler.php"); 39 require_once (APP_INC_PATH . "class.misc.php"); 40 require_once (APP_INC_PATH . "class.setup.php"); 41 require_once (APP_INC_PATH . "class.auth.php"); 42 require_once (APP_INC_PATH . "class.user.php"); 43 require_once (APP_INC_PATH . "class.prefs.php"); 44 require_once (APP_INC_PATH . "class.custom_field.php"); 45 require_once (APP_INC_PATH . "class.template.php"); 46 require_once (APP_INC_PATH . "class.mail.php"); 47 require_once (APP_INC_PATH . "class.date.php"); 48 require_once (APP_INC_PATH . "class.project.php"); 49 require_once (APP_INC_PATH . "class.history.php"); 50 require_once (APP_INC_PATH . "class.issue.php"); 51 require_once (APP_INC_PATH . "class.priority.php"); 52 53 class Notification 54 { 55 /** 56 * Method used to check whether a given email address is subsbribed to 57 * email notifications for a given issue. 58 * 59 * @access public 60 * @param integer $issue_id The issue ID 61 * @param string $email The email address 62 * @return boolean 63 */ 64 function isSubscribedToEmails($issue_id, $email) 65 { 66 $email = strtolower(Mail_API::getEmailAddress($email)); 67 if ($email == '@') { 68 // broken address, don't send the email... 69 return true; 70 } 71 $subscribed_emails = Notification::getSubscribedEmails($issue_id, 'emails'); 72 $subscribed_emails = array_map('strtolower', $subscribed_emails); 73 if (@in_array($email, $subscribed_emails)) { 74 return true; 75 } else { 76 return false; 77 } 78 } 79 80 81 /** 82 * Method used to get the list of email addresses currently 83 * subscribed to a notification type for a given issue. 84 * 85 * @access public 86 * @param integer $issue_id The issue ID 87 * @param string $type The notification type 88 * @return array The list of email addresses 89 */ 90 function getSubscribedEmails($issue_id, $type) 91 { 92 $stmt = "SELECT 93 IF(usr_id <> 0, usr_email, sub_email) AS email 94 FROM 95 ( 96 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription, 97 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 98 ) 99 LEFT JOIN 100 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 101 ON 102 usr_id=sub_usr_id 103 WHERE 104 sbt_sub_id=sub_id AND 105 sbt_type='" . Misc::escapeString($type) . "' AND 106 sub_iss_id=" . Misc::escapeInteger($issue_id); 107 $res = $GLOBALS["db_api"]->dbh->getCol($stmt); 108 if (PEAR::isError($res)) { 109 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 110 return ""; 111 } else { 112 return $res; 113 } 114 } 115 116 117 /** 118 * Method used to build a properly encoded email address that will be 119 * used by the email/note routing system. 120 * 121 * @access public 122 * @param integer $issue_id The issue ID 123 * @param string $sender The email address of the sender 124 * @param string $type Whether this is a note or email routing message 125 * @return string The properly encoded email address 126 */ 127 function getFixedFromHeader($issue_id, $sender, $type) 128 { 129 $setup = Setup::load(); 130 if ($type == 'issue') { 131 $routing = 'email_routing'; 132 } else { 133 $routing = 'note_routing'; 134 } 135 $project_id = Issue::getProjectID($issue_id); 136 // if sender is empty, get project email address 137 if (empty($sender)) { 138 $project_info = Project::getOutgoingSenderAddress($project_id); 139 $info = array( 140 "sender_name" => $project_info['name'], 141 'email' => $project_info['email'] 142 ); 143 144 // if no project name, use eventum wide sender name 145 if (empty($info['sender_name'])) { 146 $setup_sender_info = Mail_API::getAddressInfo($setup['smtp']['from']); 147 $info['sender_name'] = $setup_sender_info['sender_name']; 148 } 149 } else { 150 $info = Mail_API::getAddressInfo($sender); 151 } 152 // allow flags even without routing enabled 153 if (!empty($setup[$routing]['recipient_type_flag'])) { 154 $flag = '[' . $setup[$routing]['recipient_type_flag'] . '] '; 155 } else { 156 $flag = ''; 157 } 158 if (@$setup[$routing]['status'] != 'enabled') { 159 // let's use the custom outgoing sender address 160 $project_info = Project::getOutgoingSenderAddress($project_id); 161 if (empty($project_info['email'])) { 162 /// no project email, use main email address 163 $from_email = $setup['smtp']['from']; 164 } else { 165 $from_email = $project_info['email']; 166 } 167 } else { 168 $from_email = $setup[$routing]['address_prefix'] . $issue_id . "@" . $setup[$routing]['address_host']; 169 } 170 if (empty($info['sender_name'])) { 171 // no sender name, check if this email address belongs to a user and if so use that 172 $usr_id = User::getUserIDByEmail($info['email']); 173 if (!empty($usr_id)) { 174 $info['sender_name'] = User::getFullName($usr_id); 175 } else { 176 // no name exists, use email address for name as well 177 $info['sender_name'] = $info['email']; 178 } 179 } 180 // also check where we need to append/prepend a special string to the sender name 181 if (substr($info['sender_name'], strlen($info['sender_name']) - 1) == '"') { 182 if (@$setup[$routing]['flag_location'] == 'before') { 183 $info['sender_name'] = '"' . $flag . substr($info['sender_name'], 1); 184 } else { 185 $info['sender_name'] = substr($info['sender_name'], 0, strlen($info['sender_name']) - 1) . ' ' . trim($flag) . '"'; 186 } 187 } else { 188 if (@$setup[$routing]['flag_location'] == 'before') { 189 $info['sender_name'] = '"' . $flag . $info['sender_name'] . '"'; 190 } else { 191 $info['sender_name'] = '"' . $info['sender_name'] . ' ' . trim($flag) . '"'; 192 } 193 } 194 $from = Mail_API::getFormattedName($info['sender_name'], $from_email); 195 196 return MIME_Helper::encodeAddress(trim($from)); 197 } 198 199 200 /** 201 * Method used to check whether the current sender of the email is the 202 * mailer daemon responsible for dealing with bounces. 203 * 204 * @access public 205 * @param string $email The email address to check against 206 * @return boolean 207 */ 208 function isBounceMessage($email) 209 { 210 if (strtolower(substr($email, 0, 14)) == 'mailer-daemon@') { 211 return true; 212 } else { 213 return false; 214 } 215 } 216 217 218 /** 219 * Method used to check whether the given sender email address is 220 * the same as the issue routing email address. 221 * 222 * @access public 223 * @param integer $issue_id The issue ID 224 * @param string $sender The address of the sender 225 * @return boolean 226 */ 227 function isIssueRoutingSender($issue_id, $sender) 228 { 229 $check = Notification::getFixedFromHeader($issue_id, $sender, 'issue'); 230 $check_email = strtolower(Mail_API::getEmailAddress($check)); 231 $sender_email = strtolower(Mail_API::getEmailAddress($sender)); 232 if ($check_email == $sender_email) { 233 return true; 234 } else { 235 return false; 236 } 237 } 238 239 240 /** 241 * Method used to forward the new email to the list of subscribers. 242 * 243 * @access public 244 * @param integer $user_id The user ID of the person performing this action 245 * @param integer $issue_id The issue ID 246 * @param array $message An array containing the email 247 * @param boolean $internal_only Whether the email should only be redirected to internal users or not 248 * @param boolean $assignee_only Whether the email should only be sent to the assignee 249 * @param boolean $type The type of email this is 250 * @param integer $sup_id the ID of this email 251 * @return void 252 */ 253 function notifyNewEmail($usr_id, $issue_id, $message, $internal_only = FALSE, $assignee_only = FALSE, $type = '', $sup_id = false) 254 { 255 $prj_id = Issue::getProjectID($issue_id); 256 257 $full_message = $message['full_email']; 258 $sender = $message['from']; 259 $sender_email = strtolower(Mail_API::getEmailAddress($sender)); 260 261 // get ID of whoever is sending this. 262 $sender_usr_id = User::getUserIDByEmail($sender_email); 263 if (empty($sender_usr_id)) { 264 $sender_usr_id = false; 265 } 266 267 // automatically subscribe this sender to email notifications on this issue 268 $subscribed_emails = Notification::getSubscribedEmails($issue_id, 'emails'); 269 $subscribed_emails = array_map('strtolower', $subscribed_emails); 270 if ((!Notification::isIssueRoutingSender($issue_id, $sender)) && 271 (!Notification::isBounceMessage($sender_email)) && 272 (!in_array($sender_email, $subscribed_emails)) && 273 (Workflow::shouldAutoAddToNotificationList($prj_id))) { 274 $actions = array('emails'); 275 Notification::subscribeEmail($usr_id, $issue_id, $sender_email, $actions); 276 } 277 278 // get the subscribers 279 $emails = array(); 280 $users = Notification::getUsersByIssue($issue_id, 'emails'); 281 for ($i = 0; $i < count($users); $i++) { 282 if (empty($users[$i]["sub_usr_id"])) { 283 if ($internal_only == false) { 284 $email = $users[$i]["sub_email"]; 285 } 286 } else { 287 // if we are only supposed to send email to internal users, check if the role is lower than standard user 288 if (($internal_only == true) && (User::getRoleByUser($users[$i]["sub_usr_id"], Issue::getProjectID($issue_id)) < User::getRoleID('standard user'))) { 289 continue; 290 } 291 // check if we are only supposed to send email to the assignees 292 if (($internal_only == true) && ($assignee_only == true)) { 293 $assignee_usr_ids = Issue::getAssignedUserIDs($issue_id); 294 if (!in_array($users[$i]["sub_usr_id"], $assignee_usr_ids)) { 295 continue; 296 } 297 } 298 $email = User::getFromHeader($users[$i]["sub_usr_id"]); 299 } 300 if (!empty($email)) { 301 // don't send the email to the same person who sent it 302 if (strtolower(Mail_API::getEmailAddress($email)) == $sender_email) { 303 continue; 304 } 305 $emails[] = $email; 306 } 307 } 308 if (count($emails) == 0) { 309 return; 310 } 311 $setup = Setup::load(); 312 // change the sender of the message to {prefix}{issue_id}@{host} 313 // - keep everything else in the message, except 'From:', 'Sender:', 'To:', 'Cc:' 314 // make 'Joe Blow <joe@example.com>' become 'Joe Blow [CSC] <eventum_59@example.com>' 315 $from = Notification::getFixedFromHeader($issue_id, $sender, 'issue'); 316 317 list($_headers, $body) = Mime_Helper::splitBodyHeader($full_message); 318 // strip any 'Received:' headers 319 $_headers = Mail_API::stripHeaders($_headers); 320 $header_names = Mime_Helper::getHeaderNames($_headers); 321 // we don't want to keep the (B)Cc list for an eventum-based email 322 $ignore_headers = array( 323 'to', 324 'cc', 325 'bcc' 326 ); 327 $headers = array(); 328 // build the headers array required by the smtp library 329 foreach ($message['headers'] as $header_name => $value) { 330 if ((in_array(strtolower($header_name), $ignore_headers)) || 331 (!in_array($header_name, array_keys($header_names))) || 332 (strstr($header_name, ' '))) { 333 continue; 334 } elseif ($header_name == 'from') { 335 $headers['From'] = $from; 336 } else { 337 if (is_array($value)) { 338 $value = implode("; ", $value); 339 } 340 $headers[$header_names[$header_name]] = $value; 341 } 342 } 343 344 $headers["Subject"] = Mail_API::formatSubject($issue_id, $headers['Subject']); 345 346 if (empty($type)) { 347 if (($sender_usr_id != false) && (User::getRoleByUser($sender_usr_id, Issue::getProjectID($issue_id)) == User::getRoleID("Customer"))) { 348 $type = 'customer_email'; 349 } else { 350 $type = 'other_email'; 351 } 352 } 353 354 @require_once(APP_PEAR_PATH . 'Mail/mime.php'); 355 foreach ($emails as $to) { 356 $recipient_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($to)); 357 $to = MIME_Helper::encodeAddress($to); 358 // add the warning message about replies being blocked or not 359 $fixed_body = Mail_API::addWarningMessage($issue_id, $to, $body, $headers); 360 $headers['To'] = $to; 361 $mime = new Mail_mime("\r\n"); 362 $hdrs = $mime->headers($headers); 363 364 Mail_Queue::add($to, $hdrs, $fixed_body, 1, $issue_id, $type, $sender_usr_id, $sup_id); 365 } 366 } 367 368 369 /** 370 * Method used to get the details of a given issue. 371 * 372 * @access public 373 * @param integer $issue_id The issue ID 374 * @return array The issue details 375 */ 376 function getIssueDetails($issue_id) 377 { 378 $stmt = "SELECT 379 iss_id, 380 iss_customer_id, 381 iss_customer_contract_id, 382 iss_summary, 383 iss_description, 384 iss_duplicated_iss_id, 385 prj_id, 386 prj_title, 387 usr_full_name, 388 usr_email, 389 prc_title, 390 pre_title, 391 pri_title, 392 sta_title, 393 sta_color 394 FROM 395 ( 396 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue, 397 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project, 398 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 399 ) 400 LEFT JOIN 401 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_priority 402 ON 403 iss_pri_id=pri_id 404 LEFT JOIN 405 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_category 406 ON 407 iss_prc_id=prc_id 408 LEFT JOIN 409 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_release 410 ON 411 iss_pre_id=pre_id 412 LEFT JOIN 413 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status 414 ON 415 iss_sta_id=sta_id 416 WHERE 417 iss_id=" . Misc::escapeInteger($issue_id) . " AND 418 iss_prj_id=prj_id AND 419 iss_usr_id=usr_id"; 420 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 421 if (PEAR::isError($res)) { 422 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 423 return ""; 424 } else { 425 $res['assigned_users'] = implode(", ", Issue::getAssignedUsers($issue_id)); 426 // get customer information, if any 427 if ((!empty($res['iss_customer_id'])) && (Customer::hasCustomerIntegration($res['prj_id']))) { 428 $res['customer_info'] = Customer::getDetails($res['prj_id'], $res['iss_customer_id'], false, $res['iss_customer_contract_id']); 429 } 430 return $res; 431 } 432 } 433 434 435 /** 436 * Method used to get the details of a given note and issue. 437 * 438 * @access public 439 * @param integer $issue_id The issue ID 440 * @param integer $note_id The note ID 441 * @return array The details of the note / issue 442 */ 443 function getNote($issue_id, $note_id) 444 { 445 $stmt = "SELECT 446 not_usr_id, 447 not_iss_id, 448 not_created_date, 449 not_note, 450 not_title, 451 not_unknown_user, 452 not_blocked_message, 453 not_message_id, 454 not_parent_id, 455 usr_full_name 456 FROM 457 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "note, 458 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 459 WHERE 460 not_id=" . Misc::escapeInteger($note_id) . " AND 461 not_usr_id=usr_id"; 462 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 463 if (PEAR::isError($res)) { 464 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 465 return ""; 466 } else { 467 468 // if there is an unknown user, use instead of full name 469 if (!empty($res["not_unknown_user"])) { 470 $res["usr_full_name"] = $res["not_unknown_user"]; 471 } 472 473 if (!empty($res['not_parent_id'])) { 474 $res['reference_msg_id'] = Note::getMessageIDbyID($res['not_parent_id']); 475 } else { 476 $res['reference_msg_id'] = false; 477 } 478 479 $data = Notification::getIssueDetails($issue_id); 480 $data["note"] = $res; 481 return $data; 482 } 483 } 484 485 486 /** 487 * Method used to get the details of a given issue and its 488 * associated emails. 489 * 490 * @access public 491 * @param integer $issue_id The issue ID 492 * @param array $sup_ids The list of associated emails 493 * @return array The issue / emails details 494 */ 495 function getEmails($issue_id, $sup_ids) 496 { 497 $items = @implode(", ", Misc::escapeInteger($sup_ids)); 498 $stmt = "SELECT 499 sup_from, 500 sup_to, 501 sup_date, 502 sup_subject, 503 sup_has_attachment 504 FROM 505 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email 506 WHERE 507 sup_id IN ($items)"; 508 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 509 if (PEAR::isError($res)) { 510 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 511 return ""; 512 } else { 513 if (count($res) == 0) { 514 return ""; 515 } else { 516 $data = Notification::getIssueDetails($issue_id); 517 $data["emails"] = $res; 518 return $data; 519 } 520 } 521 } 522 523 524 /** 525 * Method used to get the details of a given issue and attachment. 526 * 527 * @access public 528 * @param integer $issue_id The issue ID 529 * @param integer $attachment_id The attachment ID 530 * @return array The issue / attachment details 531 */ 532 function getAttachment($issue_id, $attachment_id) 533 { 534 $stmt = "SELECT 535 iat_id, 536 usr_full_name, 537 iat_created_date, 538 iat_description, 539 iat_unknown_user 540 FROM 541 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_attachment, 542 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 543 WHERE 544 iat_usr_id=usr_id AND 545 iat_iss_id=" . Misc::escapeInteger($issue_id) . " AND 546 iat_id=" . Misc::escapeInteger($attachment_id); 547 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 548 if (PEAR::isError($res)) { 549 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 550 return ""; 551 } else { 552 $res["files"] = Attachment::getFileList($res["iat_id"]); 553 $data = Notification::getIssueDetails($issue_id); 554 $data["attachment"] = $res; 555 return $data; 556 } 557 } 558 559 560 /** 561 * Method used to get the list of users / emails that are 562 * subscribed for notifications of changes for a given issue. 563 * 564 * @access public 565 * @param integer $issue_id The issue ID 566 * @param string $type The notification type 567 * @return array The list of users / emails 568 */ 569 function getUsersByIssue($issue_id, $type) 570 { 571 $issue_id = Misc::escapeInteger($issue_id); 572 if ($type == 'notes') { 573 $stmt = "SELECT 574 DISTINCT sub_usr_id, 575 sub_email 576 FROM 577 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 578 WHERE 579 sub_iss_id=$issue_id AND 580 sub_usr_id IS NOT NULL AND 581 sub_usr_id <> 0"; 582 } else { 583 $stmt = "SELECT 584 DISTINCT sub_usr_id, 585 sub_email 586 FROM 587 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription, 588 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 589 WHERE 590 sub_iss_id=$issue_id AND 591 sub_id=sbt_sub_id AND 592 sbt_type='" . Misc::escapeString($type) . "'"; 593 } 594 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 595 if (PEAR::isError($res)) { 596 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 597 return array(); 598 } else { 599 return $res; 600 } 601 602 } 603 604 605 /** 606 * Method used to send a diff-style notification email to the issue 607 * subscribers about updates to its attributes. 608 * 609 * @access public 610 * @param integer $issue_id The issue ID 611 * @param array $old The old issue details 612 * @param array $new The new issue details 613 */ 614 function notifyIssueUpdated($issue_id, $old, $new) 615 { 616 $prj_id = Issue::getProjectID($issue_id); 617 $diffs = array(); 618 if (@$new["keep_assignments"] == "no") { 619 if (empty($new['assignments'])) { 620 $new['assignments'] = array(); 621 } 622 $assign_diff = Misc::arrayDiff($old['assigned_users'], $new['assignments']); 623 if (count($assign_diff) > 0) { 624 $diffs[] = '-' . ev_gettext('Assignment List') . ': ' . $old['assignments']; 625 @$diffs[] = '+' . ev_gettext('Assignment List') . ': ' . implode(', ', User::getFullName($new['assignments'])); 626 } 627 } 628 if (@$old['iss_expected_resolution_date'] != $new['expected_resolution_date']) { 629 $diffs[] = '-' . ev_gettext('Expected Resolution Date') . ': ' . $old['iss_expected_resolution_date']; 630 $diffs[] = '+' . ev_gettext('Expected Resolution Date') . ': ' . $new['expected_resolution_date']; 631 } 632 if ($old["iss_prc_id"] != $new["category"]) { 633 $diffs[] = '-' . ev_gettext('Category') . ': ' . Category::getTitle($old["iss_prc_id"]); 634 $diffs[] = '+' . ev_gettext('Category') . ': ' . Category::getTitle($new["category"]); 635 } 636 if ((@$new["keep"] == "no") && ($old["iss_pre_id"] != $new["release"])) { 637 $diffs[] = '-' . ev_gettext('Release') . ': ' . Release::getTitle($old["iss_pre_id"]); 638 $diffs[] = '+' . ev_gettext('Release') . ': ' . Release::getTitle($new["release"]); 639 } 640 if ($old["iss_pri_id"] != $new["priority"]) { 641 $diffs[] = '-' . ev_gettext('Priority') . ': ' . Priority::getTitle($old["iss_pri_id"]); 642 $diffs[] = '+' . ev_gettext('Priority') . ': ' . Priority::getTitle($new["priority"]); 643 } 644 if ($old["iss_sta_id"] != $new["status"]) { 645 $diffs[] = '-' . ev_gettext('Status') . ': ' . Status::getStatusTitle($old["iss_sta_id"]); 646 $diffs[] = '+' . ev_gettext('Status') . ': ' . Status::getStatusTitle($new["status"]); 647 } 648 if ($old["iss_res_id"] != $new["resolution"]) { 649 $diffs[] = '-' . ev_gettext('Resolution') . ': ' . Resolution::getTitle($old["iss_res_id"]); 650 $diffs[] = '+' . ev_gettext('Resolution') . ': ' . Resolution::getTitle($new["resolution"]); 651 } 652 if ($old["iss_dev_time"] != $new["estimated_dev_time"]) { 653 $diffs[] = '-' . ev_gettext('Estimated Dev. Time') . ': ' . Misc::getFormattedTime($old["iss_dev_time"]*60); 654 $diffs[] = '+' . ev_gettext('Estimated Dev. Time') . ': ' . Misc::getFormattedTime($new["estimated_dev_time"]*60); 655 } 656 if ($old["iss_summary"] != $new["summary"]) { 657 $diffs[] = '-' . ev_gettext('Summary') . ': ' . $old['iss_summary']; 658 $diffs[] = '+' . ev_gettext('Summary') . ': ' . $new['summary']; 659 } 660 if ($old["iss_description"] != $new["description"]) { 661 // need real diff engine here 662 require_once 'Text/Diff.php'; 663 require_once 'Text/Diff/Renderer.php'; 664 require_once 'Text/Diff/Renderer/unified.php'; 665 $old['iss_description'] = explode("\n", $old['iss_description']); 666 $new['description'] = explode("\n", $new['description']); 667 $diff = &new Text_Diff($old["iss_description"], $new["description"]); 668 $renderer = &new Text_Diff_Renderer_unified(); 669 $desc_diff = explode("\n", trim($renderer->render($diff))); 670 $diffs[] = 'Description:'; 671 for ($i = 0; $i < count($desc_diff); $i++) { 672 $diffs[] = $desc_diff[$i]; 673 } 674 } 675 676 $emails = array(); 677 $users = Notification::getUsersByIssue($issue_id, 'updated'); 678 $user_emails = Project::getUserEmailAssocList(Issue::getProjectID($issue_id), 'active', User::getRoleID('Customer')); 679 $user_emails = array_map('strtolower', $user_emails); 680 for ($i = 0; $i < count($users); $i++) { 681 if (empty($users[$i]["sub_usr_id"])) { 682 $email = $users[$i]["sub_email"]; 683 } else { 684 if (Auth::getUserID() == $users[$i]["sub_usr_id"]) { 685 // don't notify the user who made this change 686 continue; 687 } 688 $email = User::getFromHeader($users[$i]["sub_usr_id"]); 689 } 690 // now add it to the list of emails 691 if ((!empty($email)) && (!in_array($email, $emails))) { 692 $emails[] = $email; 693 } 694 } 695 // get additional email addresses to notify 696 $emails = array_merge($emails, Workflow::getAdditionalEmailAddresses($prj_id, $issue_id, 'issue_updated', array('old' => $old, 'new' => $new))); 697 698 $data = Notification::getIssueDetails($issue_id); 699 $data['diffs'] = implode("\n", $diffs); 700 $data['updated_by'] = User::getFullName(Auth::getUserID()); 701 Notification::notifySubscribers($issue_id, $emails, 'updated', $data, ev_gettext('Updated'), FALSE); 702 } 703 704 705 /** 706 * Method used to send a diff-style notification email to the issue 707 * subscribers about status changes 708 * 709 * @access public 710 * @param integer $issue_id The issue ID 711 * @param array $old_status The old issue status 712 * @param array $new_status The new issue status 713 */ 714 function notifyStatusChange($issue_id, $old_status, $new_status) 715 { 716 $diffs = array(); 717 if ($old_status != $new_status) { 718 $diffs[] = '-Status: ' . Status::getStatusTitle($old_status); 719 $diffs[] = '+Status: ' . Status::getStatusTitle($new_status); 720 } 721 722 if (count($diffs) < 1) { 723 return false; 724 } 725 726 $emails = array(); 727 $users = Notification::getUsersByIssue($issue_id, 'updated'); 728 $user_emails = Project::getUserEmailAssocList(Issue::getProjectID($issue_id), 'active', User::getRoleID('Customer')); 729 $user_emails = array_map('strtolower', $user_emails); 730 for ($i = 0; $i < count($users); $i++) { 731 if (empty($users[$i]["sub_usr_id"])) { 732 $email = $users[$i]["sub_email"]; 733 } else { 734 if (Auth::getUserID() == $users[$i]["sub_usr_id"]) { 735 // don't notify the user who made this change 736 continue; 737 } 738 $email = User::getFromHeader($users[$i]["sub_usr_id"]); 739 } 740 // now add it to the list of emails 741 if ((!empty($email)) && (!in_array($email, $emails))) { 742 $emails[] = $email; 743 } 744 } 745 $data = Notification::getIssueDetails($issue_id); 746 $data['diffs'] = implode("\n", $diffs); 747 $data['updated_by'] = User::getFullName(Auth::getUserID()); 748 Notification::notifySubscribers($issue_id, $emails, 'updated', $data, 'Status Change', FALSE); 749 } 750 751 752 /** 753 * Method used to send email notifications for a given issue. 754 * 755 * @access public 756 * @param integer $issue_id The issue ID 757 * @param string $type The notification type 758 * @param array $ids The list of entries that were changed 759 * @param integer $internal_only Whether the notification should only be sent to internal users or not 760 * @return void 761 */ 762 function notify($issue_id, $type, $ids = FALSE, $internal_only = FALSE, $extra_recipients = FALSE) 763 { 764 if ($extra_recipients) { 765 $extra = array(); 766 for ($i = 0; $i < count($extra_recipients); $i++) { 767 $extra[] = array( 768 'sub_usr_id' => $extra_recipients[$i], 769 'sub_email' => '' 770 ); 771 } 772 } 773 $emails = array(); 774 $users = Notification::getUsersByIssue($issue_id, $type); 775 if (($extra_recipients) && (count($extra) > 0)) { 776 $users = array_merge($users, $extra); 777 } 778 $user_emails = Project::getUserEmailAssocList(Issue::getProjectID($issue_id), 'active', User::getRoleID('Customer')); 779 $user_emails = array_map('strtolower', $user_emails); 780 for ($i = 0; $i < count($users); $i++) { 781 if (empty($users[$i]["sub_usr_id"])) { 782 if (($internal_only == false) || (in_array(strtolower($users[$i]["sub_email"]), array_values($user_emails)))) { 783 $email = $users[$i]["sub_email"]; 784 } 785 } else { 786 // don't send the notification email to the person who performed the action 787 if (Auth::getUserID() == $users[$i]["sub_usr_id"]) { 788 continue; 789 } 790 // if we are only supposed to send email to internal users, check if the role is lower than standard user 791 if (($internal_only == true) && (User::getRoleByUser($users[$i]["sub_usr_id"], Issue::getProjectID($issue_id)) < User::getRoleID('standard user'))) { 792 continue; 793 } 794 $email = User::getFromHeader($users[$i]["sub_usr_id"]); 795 } 796 // now add it to the list of emails 797 if ((!empty($email)) && (!in_array($email, $emails))) { 798 $emails[] = $email; 799 } 800 } 801 // prevent the primary customer contact from receiving two emails about the issue being closed 802 if ($type == 'closed') { 803 $stmt = "SELECT 804 iss_customer_contact_id 805 FROM 806 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue 807 WHERE 808 iss_id=" . Misc::escapeInteger($issue_id); 809 $customer_contact_id = $GLOBALS["db_api"]->dbh->getOne($stmt); 810 if (!empty($customer_contact_id)) { 811 list($contact_email,,) = Customer::getContactLoginDetails(Issue::getProjectID($issue_id), $customer_contact_id); 812 for ($i = 0; $i < count($emails); $i++) { 813 $email = Mail_API::getEmailAddress($emails[$i]); 814 if ($email == $contact_email) { 815 unset($emails[$i]); 816 $emails = array_values($emails); 817 break; 818 } 819 } 820 } 821 } 822 if (count($emails) > 0) { 823 $headers = false; 824 switch ($type) { 825 case 'closed': 826 $data = Notification::getIssueDetails($issue_id); 827 $data["closer_name"] = User::getFullName(History::getIssueCloser($issue_id)); 828 $subject = ev_gettext('Closed'); 829 830 if ($ids != false) { 831 $data['reason'] = Support::getEmail($ids); 832 } 833 break; 834 case 'updated': 835 // this should not be used anymore 836 return false; 837 break; 838 case 'notes': 839 $data = Notification::getNote($issue_id, $ids); 840 $headers = array( 841 'Message-ID' => $data['note']['not_message_id'], 842 ); 843 if (@$data['note']['reference_msg_id'] != false) { 844 $headers['In-Reply-To'] = $data['note']['reference_msg_id']; 845 } else { 846 $headers['In-Reply-To'] = Issue::getRootMessageID($issue_id); 847 } 848 $headers['References'] = Mail_API::fold(join(' ', Mail_API::getReferences($issue_id, @$data['note']['reference_msg_id'], 'note'))); 849 $subject = 'Note'; 850 break; 851 case 'emails': 852 // this should not be used anymore 853 return false; 854 break; 855 case 'files': 856 $data = Notification::getAttachment($issue_id, $ids); 857 $subject = 'File Attached'; 858 break; 859 } 860 Notification::notifySubscribers($issue_id, $emails, $type, $data, $subject, $internal_only, $ids, $headers); 861 } 862 } 863 864 /** 865 * Method used to get list of addresses that were email sent to. 866 * 867 * @access public 868 * @param integer $issue_id The issue ID 869 * @return array list of addresse 870 */ 871 function getLastNotifiedAddresses($issue_id = null) 872 { 873 global $_EVENTUM_LAST_NOTIFIED_LIST; 874 875 if (is_null($_EVENTUM_LAST_NOTIFIED_LIST)) { 876 return null; 877 } 878 879 if (is_null($issue_id)) { 880 // return all addresses in flat view 881 $ret = array_values($_EVENTUM_LAST_NOTIFIED_LIST); 882 } else { 883 // return address list for specific issue_id only. 884 $ret = $_EVENTUM_LAST_NOTIFIED_LIST[$issue_id]; 885 } 886 return array_unique($ret); 887 } 888 889 890 /** 891 * Method used to format and send the email notifications. 892 * 893 * @access public 894 * @param integer $issue_id The issue ID 895 * @param array $emails The list of emails 896 * @param string $type The notification type 897 * @param array $data The issue details 898 * @param string $subject The subject of the email 899 * @param integer $type_id The ID of the event that triggered this notification (issue_id, sup_id, not_id, etc) 900 * @param array $headers Any extra headers that need to be added to this email (Default false) 901 * @return void 902 */ 903 function notifySubscribers($issue_id, $emails, $type, $data, $subject, $internal_only, $type_id = false, $headers = false) 904 { 905 global $_EVENTUM_LAST_NOTIFIED_LIST; 906 907 // open text template 908 $tpl = new Template_API; 909 $tpl->setTemplate('notifications/' . $type . '.tpl.text'); 910 $tpl->bulkAssign(array( 911 "app_title" => Misc::getToolCaption(), 912 "data" => $data 913 )); 914 915 $setup = Setup::load(); 916 $final_type = $type; 917 $sender_usr_id = false; 918 $threading_headers = Mail_API::getBaseThreadingHeaders($issue_id); 919 for ($i = 0; $i < count($emails); $i++) { 920 921 $can_access = true; 922 $recipient_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($emails[$i])); 923 if (!empty($recipient_usr_id)) { 924 if (!Issue::canAccess($issue_id, $recipient_usr_id)) { 925 $can_access = false; 926 } 927 $tpl->assign("recipient_role", User::getRoleByUser($recipient_usr_id, Issue::getProjectID($issue_id))); 928 if (isset($data['custom_fields'])) { 929 $data['custom_fields'] = Custom_Field::getListByIssue($data['iss_prj_id'], $issue_id, $recipient_usr_id); 930 } 931 $is_assigned = Issue::isAssignedToUser($issue_id, $recipient_usr_id); 932 } else { 933 $tpl->assign("recipient_role", 0); 934 unset($data['custom_fields']); 935 936 $is_assigned = false; 937 } 938 $tpl->assign("data", $data); 939 $tpl->assign("is_assigned", $is_assigned); 940 941 if ($can_access != true) { 942 continue; 943 } 944 945 // change the current locale 946 if (!empty($recipient_usr_id)) { 947 Language::set(User::getLang($recipient_usr_id)); 948 } else { 949 Language::set(APP_DEFAULT_LOCALE); 950 } 951 952 // send email (use PEAR's classes) 953 $mail = new Mail_API; 954 $mail->setTextBody($tpl->getTemplateContents()); 955 if ($headers != false) { 956 $mail->setHeaders($headers); 957 } 958 if (($headers == false) || (($headers != false) && ((empty($headers['Message-ID'])) && (empty($headers['In-Reply-To'])) && (empty($headers['References']))))) { 959 $mail->setHeaders($threading_headers); 960 } 961 962 if ($type == 'notes') { 963 // special handling of blocked messages 964 if (!empty($data['note']['not_blocked_message'])) { 965 $subject = ev_gettext('BLOCKED'); 966 $final_type = 'blocked_email'; 967 } 968 if (!empty($data["note"]["not_unknown_user"])) { 969 $sender = $data["note"]["not_unknown_user"]; 970 } else { 971 $sender = User::getFromHeader($data["note"]["not_usr_id"]); 972 } 973 $sender_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($sender)); 974 if (empty($sender_usr_id)) { 975 $sender_usr_id = false; 976 } 977 978 $from = Notification::getFixedFromHeader($issue_id, $sender, 'note'); 979 } else { 980 $from = Notification::getFixedFromHeader($issue_id, '', 'issue'); 981 } 982 // show the title of the note, not the issue summary 983 if ($type == 'notes') { 984 $extra_subject = $data['note']['not_title']; 985 // don't add the "[#3333] Note: " prefix to messages that already have that in the subject line 986 if (strstr($extra_subject, "[#$issue_id] $subject: ")) { 987 $pos = strpos($extra_subject, "[#$issue_id] $subject: "); 988 $full_subject = substr($extra_subject, 4); 989 } else { 990 $full_subject = "[#$issue_id] $subject: $extra_subject"; 991 } 992 } elseif (($type == 'new_issue') && ($is_assigned)) { 993 $full_subject = "[#$issue_id] New Issue Assigned: " . $data['iss_summary']; 994 } else { 995 $extra_subject = $data['iss_summary']; 996 $full_subject = "[#$issue_id] $subject: $extra_subject"; 997 } 998 $mail->send($from, $emails[$i], $full_subject, TRUE, $issue_id, $final_type, $sender_usr_id, $type_id); 999 1000 $_EVENTUM_LAST_NOTIFIED_LIST[$issue_id][] = $emails[$i]; 1001 } 1002 1003 // restore correct language 1004 Language::restore(); 1005 } 1006 1007 1008 /** 1009 * Method used to send an email notification to users that want 1010 * to be alerted when new issues are created in the system. 1011 * 1012 * @access public 1013 * @param integer $prj_id The project ID 1014 * @param integer $issue_id The issue ID 1015 * @param array $exclude_list The list of users NOT to notify. 1016 * @return void 1017 */ 1018 function notifyNewIssue($prj_id, $issue_id, $exclude_list = array()) 1019 { 1020 $prj_id = Misc::escapeInteger($prj_id); 1021 $issue_id = Misc::escapeInteger($issue_id); 1022 $exclude_list = Misc::escapeInteger($exclude_list); 1023 1024 // get all users associated with this project 1025 $stmt = "SELECT 1026 usr_id, 1027 usr_full_name, 1028 usr_email, 1029 usr_preferences, 1030 pru_role, 1031 usr_customer_id, 1032 usr_customer_contact_id 1033 FROM 1034 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user, 1035 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_user 1036 WHERE 1037 pru_prj_id=$prj_id AND 1038 usr_id=pru_usr_id AND 1039 usr_status = 'active' AND 1040 pru_role > " . User::getRoleID("Customer"); 1041 if (count($exclude_list) > 0) { 1042 $stmt .= " AND 1043 usr_id NOT IN (" . join(', ', $exclude_list) . ")"; 1044 } 1045 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 1046 $emails = array(); 1047 for ($i = 0; $i < count($res); $i++) { 1048 @$res[$i]['usr_preferences'] = unserialize($res[$i]['usr_preferences']); 1049 $subscriber = Mail_API::getFormattedName($res[$i]['usr_full_name'], $res[$i]['usr_email']); 1050 // don't send these emails to customers 1051 if (($res[$i]['pru_role'] == User::getRoleID('Customer')) || (!empty($res[$i]['usr_customer_id'])) 1052 || (!empty($res[$i]['usr_customer_contact_id']))) { 1053 continue; 1054 } 1055 if ((!empty($res[$i]['usr_preferences']['receive_new_emails'][$prj_id])) 1056 && (@$res[$i]['usr_preferences']['receive_new_emails'][$prj_id]) 1057 && (!in_array($subscriber, $emails))) { 1058 $emails[] = $subscriber; 1059 } 1060 } 1061 1062 // get assignees 1063 $stmt = "SELECT 1064 usr_id, 1065 usr_full_name, 1066 usr_email, 1067 usr_preferences 1068 FROM 1069 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user, 1070 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user 1071 WHERE 1072 isu_iss_id=$issue_id AND 1073 usr_id=isu_usr_id AND 1074 usr_status = 'active'"; 1075 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 1076 for ($i = 0; $i < count($res); $i++) { 1077 @$res[$i]['usr_preferences'] = unserialize($res[$i]['usr_preferences']); 1078 $subscriber = Mail_API::getFormattedName($res[$i]['usr_full_name'], $res[$i]['usr_email']); 1079 1080 if ((!empty($res[$i]['usr_preferences']['receive_assigned_emails'][$prj_id])) && 1081 (@$res[$i]['usr_preferences']['receive_assigned_emails'][$prj_id]) && (!in_array($subscriber, $emails))) { 1082 $emails[] = $subscriber; 1083 } 1084 } 1085 1086 // get any additional emails 1087 $emails = array_merge($emails, Workflow::getAdditionalEmailAddresses($prj_id, $issue_id, 'new_issue')); 1088 1089 $data = Issue::getDetails($issue_id, true); 1090 $data['attachments'] = Attachment::getList($issue_id); 1091 1092 // notify new issue to irc channel 1093 $irc_notice = "New Issue #$issue_id ("; 1094 $quarantine = Issue::getQuarantineInfo($issue_id); 1095 if (!empty($quarantine)) { 1096 $irc_notice .= "Quarantined; "; 1097 } 1098 $irc_notice .= "Priority: " . $data['pri_title']; 1099 // also add information about the assignee, if any 1100 $assignment = Issue::getAssignedUsers($issue_id); 1101 if (count($assignment) > 0) { 1102 $irc_notice .= "; Assignment: " . implode(', ', $assignment); 1103 } 1104 if (!empty($data['iss_grp_id'])) { 1105 $irc_notice .= "; Group: " . Group::getName($data['iss_grp_id']); 1106 } 1107 $irc_notice .= "), "; 1108 if (@isset($data['customer_info'])) { 1109 $irc_notice .= $data['customer_info']['customer_name'] . ", "; 1110 } 1111 $irc_notice .= $data['iss_summary']; 1112 Notification::notifyIRC($prj_id, $irc_notice, $issue_id); 1113 $data['custom_fields'] = array();// empty place holder so notifySubscribers will fill it in with appropriate data for the user 1114 $subject = ev_gettext('New Issue'); 1115 $headers = array( 1116 "Message-ID" => $data['iss_root_message_id'] 1117 ); 1118 Notification::notifySubscribers($issue_id, $emails, 'new_issue', $data, $subject, false, false, $headers); 1119 } 1120 1121 1122 /** 1123 * Method used to send an email notification to the sender of an 1124 * email message that was automatically converted into an issue. 1125 * 1126 * @access public 1127 * @param integer $prj_id The project ID 1128 * @param integer $issue_id The issue ID 1129 * @param string $sender The sender of the email message (and the recipient of this notification) 1130 * @param string $date The arrival date of the email message 1131 * @param string $subject The subject line of the email message 1132 * @param string $additional_recipient The user who should recieve this email who is not the sender of the original email. 1133 * @return void 1134 */ 1135 function notifyAutoCreatedIssue($prj_id, $issue_id, $sender, $date, $subject, $additional_recipient = false) 1136 { 1137 if (Customer::hasCustomerIntegration($prj_id)) { 1138 Customer::notifyAutoCreatedIssue($prj_id, $issue_id, $sender, $date, $subject); 1139 } else { 1140 if ($additional_recipient != false) { 1141 $recipient = $additional_recipient; 1142 $is_message_sender = false; 1143 } else { 1144 $recipient = $sender; 1145 $is_message_sender = true; 1146 } 1147 $recipient_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($recipient)); 1148 1149 if (!Workflow::shouldEmailAddress($prj_id, Mail_API::getEmailAddress($recipient), $issue_id, 'auto_created')) { 1150 return; 1151 } 1152 $data = Issue::getDetails($issue_id); 1153 1154 // open text template 1155 $tpl = new Template_API; 1156 $tpl->setTemplate('notifications/new_auto_created_issue.tpl.text'); 1157 $tpl->bulkAssign(array( 1158 "app_title" => Misc::getToolCaption(), 1159 "data" => $data, 1160 "sender_name" => Mail_API::getName($sender), 1161 'recipient_name' => Mail_API::getName($recipient), 1162 'is_message_sender' => $is_message_sender 1163 )); 1164 1165 // figure out if sender has a real account or not 1166 $sender_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($sender)); 1167 if ((!empty($sender_usr_id)) && (Issue::canAccess($issue_id, $sender_usr_id))) { 1168 $can_access = 1; 1169 } else { 1170 $can_access = 0; 1171 } 1172 1173 $tpl->assign(array( 1174 'sender_can_access' => $can_access, 1175 'email' => array( 1176 'date' => $date, 1177 'from' => Mime_Helper::fixEncoding($sender), 1178 'subject' => $subject 1179 ) 1180 )); 1181 1182 // change the current locale 1183 if (!empty($recipient_usr_id)) { 1184 Language::set(User::getLang($recipient_usr_id)); 1185 } else { 1186 Language::set(APP_DEFAULT_LOCALE); 1187 } 1188 1189 $text_message = $tpl->getTemplateContents(); 1190 1191 // send email (use PEAR's classes) 1192 $mail = new Mail_API; 1193 $mail->setTextBody($text_message); 1194 $mail->setHeaders(Mail_API::getBaseThreadingHeaders($issue_id)); 1195 $setup = $mail->getSMTPSettings(); 1196 $from = Notification::getFixedFromHeader($issue_id, $setup["from"], 'issue'); 1197 $recipient = Mime_Helper::fixEncoding($recipient); 1198 $mail->send($from, $recipient, "[#$issue_id] Issue Created: " . $data['iss_summary'], 0, $issue_id, 'auto_created_issue'); 1199 1200 Language::restore(); 1201 } 1202 } 1203 1204 1205 /** 1206 * Method used to send an email notification to the sender of a 1207 * set of email messages that were manually converted into an 1208 * issue. 1209 * 1210 * @access public 1211 * @param integer $prj_id The project ID 1212 * @param integer $issue_id The issue ID 1213 * @param array $sup_ids The email IDs 1214 * @param integer $customer_id The customer ID 1215 * @return array The list of recipient emails 1216 */ 1217 function notifyEmailConvertedIntoIssue($prj_id, $issue_id, $sup_ids, $customer_id = FALSE) 1218 { 1219 if (Customer::hasCustomerIntegration($prj_id)) { 1220 return Customer::notifyEmailConvertedIntoIssue($prj_id, $issue_id, $sup_ids, $customer_id); 1221 } else { 1222 // build the list of recipients 1223 $recipients = array(); 1224 $recipient_emails = array(); 1225 for ($i = 0; $i < count($sup_ids); $i++) { 1226 $senders = Support::getSender(array($sup_ids[$i])); 1227 if (count($senders) > 0) { 1228 $sender_email = Mail_API::getEmailAddress($senders[0]); 1229 $recipients[$sup_ids[$i]] = $senders[0]; 1230 $recipient_emails[] = $sender_email; 1231 } 1232 } 1233 if (count($recipients) == 0) { 1234 return false; 1235 } 1236 1237 $data = Issue::getDetails($issue_id); 1238 foreach ($recipients as $sup_id => $recipient) { 1239 1240 $recipient_usr_id = User::getUserIDByEmail(Mail_API::getEmailAddress($recipient)); 1241 1242 // open text template 1243 $tpl = new Template_API; 1244 $tpl->setTemplate('notifications/new_auto_created_issue.tpl.text'); 1245 $tpl->bulkAssign(array( 1246 "data" => $data, 1247 "sender_name" => Mail_API::getName($recipient) 1248 )); 1249 $email_details = Support::getEmailDetails(Email_Account::getAccountByEmail($sup_id), $sup_id); 1250 $tpl->assign(array( 1251 'email' => array( 1252 'date' => $email_details['sup_date'], 1253 'from' => $email_details['sup_from'], 1254 'subject' => $email_details['sup_subject'] 1255 ) 1256 )); 1257 1258 // change the current locale 1259 if (!empty($recipient_usr_id)) { 1260 Language::set(User::getLang($recipient_usr_id)); 1261 } else { 1262 Language::set(APP_DEFAULT_LOCALE); 1263 } 1264 1265 $text_message = $tpl->getTemplateContents(); 1266 1267 // send email (use PEAR's classes) 1268 $mail = new Mail_API; 1269 $mail->setTextBody($text_message); 1270 $setup = $mail->getSMTPSettings(); 1271 $from = Notification::getFixedFromHeader($issue_id, $setup["from"], 'issue'); 1272 $mail->setHeaders(Mail_API::getBaseThreadingHeaders($issue_id)); 1273 $mail->send($from, $recipient, 'New Issue Created', 1, $issue_id, 'email_converted_to_issue'); 1274 } 1275 Language::restore(); 1276 return $recipient_emails; 1277 } 1278 } 1279 1280 1281 /** 1282 * Method used to send an IRC notification about changes in the assignment 1283 * list of an issue. 1284 * 1285 * @access public 1286 * @param integer $issue_id The issue ID 1287 * @param integer $usr_id The person who is performing this change 1288 * @param array $old The old issue assignment list 1289 * @param array $new The new issue assignment list 1290 * @param boolean $is_remote Whether this change was made remotely or not 1291 */ 1292 function notifyIRCAssignmentChange($issue_id, $usr_id, $old, $new, $is_remote = FALSE) 1293 { 1294 // do not notify about clearing the assignment of an issue 1295 if (count($new) == 0) { 1296 return false; 1297 } 1298 // only notify on irc if the assignment is being done to more than one person, 1299 // or in the case of a one-person-assignment-change, if the person doing it 1300 // is different than the actual assignee 1301 if ((count($new) == 1) && ($new[0] == $usr_id)) { 1302 return false; 1303 } 1304 $assign_diff = Misc::arrayDiff($old, $new); 1305 if ((count($new) != count($old)) || (count($assign_diff) > 0)) { 1306 $notice = "Issue #$issue_id "; 1307 if ($is_remote) { 1308 $notice .= "remotely "; 1309 } 1310 if (count($old) == 0) { 1311 $old_assignees = '[empty]'; 1312 } else { 1313 $old_assignees = implode(', ', User::getFullName($old)); 1314 } 1315 $notice .= "updated (Old Assignment: " . $old_assignees . 1316 "; New Assignment: " . implode(', ', User::getFullName($new)) . ")"; 1317 Notification::notifyIRC(Issue::getProjectID($issue_id), $notice, $issue_id); 1318 } 1319 } 1320 1321 1322 /** 1323 * Method used to send an IRC notification about a blocked email that was 1324 * saved into an internal note. 1325 * 1326 * @access public 1327 * @param integer $issue_id The issue ID 1328 * @param string $from The sender of the blocked email message 1329 */ 1330 function notifyIRCBlockedMessage($issue_id, $from) 1331 { 1332 $notice = "Issue #$issue_id updated ("; 1333 // also add information about the assignee, if any 1334 $assignment = Issue::getAssignedUsers($issue_id); 1335 if (count($assignment) > 0) { 1336 $notice .= "Assignment: " . implode(', ', $assignment) . "; "; 1337 } 1338 $notice .= "BLOCKED email from '$from')"; 1339 Notification::notifyIRC(Issue::getProjectID($issue_id), $notice, $issue_id); 1340 } 1341 1342 1343 /** 1344 * Method used to save the IRC notification message in the queue table. 1345 * 1346 * @access public 1347 * @param integer $project_id The ID of the project. 1348 * @param string $notice The notification summary that should be displayed on IRC 1349 * @param integer $issue_id The issue ID 1350 * @return boolean 1351 */ 1352 function notifyIRC($project_id, $notice, $issue_id = false) 1353 { 1354 // don't save any irc notification if this feature is disabled 1355 $setup = Setup::load(); 1356 if (@$setup['irc_notification'] != 'enabled') { 1357 return false; 1358 } 1359 1360 $stmt = "INSERT INTO 1361 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "irc_notice 1362 ( 1363 ino_prj_id, 1364 ino_created_date, 1365 ino_status, 1366 ino_message"; 1367 if ($issue_id != false) { 1368 $stmt .= ",\n ino_iss_id"; 1369 } 1370 $stmt .= ") VALUES ( 1371 " . Misc::escapeInteger($project_id) . ", 1372 '" . Date_API::getCurrentDateGMT() . "', 1373 'pending', 1374 '" . Misc::escapeString($notice) . "'"; 1375 if ($issue_id != false) { 1376 $stmt .= ",\n $issue_id"; 1377 } 1378 $stmt .= ")"; 1379 $res = $GLOBALS["db_api"]->dbh->query($stmt); 1380 if (PEAR::isError($res)) { 1381 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 1382 return false; 1383 } else { 1384 return true; 1385 } 1386 } 1387 1388 1389 /** 1390 * Method used to send an email notification when the account 1391 * details of an user is changed. 1392 * 1393 * @access public 1394 * @param integer $usr_id The user ID 1395 * @return void 1396 */ 1397 function notifyUserAccount($usr_id) 1398 { 1399 $info = User::getDetails($usr_id); 1400 $info["projects"] = Project::getAssocList($usr_id, true, true); 1401 // open text template 1402 $tpl = new Template_API; 1403 $tpl->setTemplate('notifications/updated_account.tpl.text'); 1404 $tpl->bulkAssign(array( 1405 "app_title" => Misc::getToolCaption(), 1406 "user" => $info 1407 )); 1408 1409 // change the current locale 1410 Language::set(User::getLang($usr_id)); 1411 1412 $text_message = $tpl->getTemplateContents(); 1413 1414 // send email (use PEAR's classes) 1415 $mail = new Mail_API; 1416 $mail->setTextBody($text_message); 1417 $setup = $mail->getSMTPSettings(); 1418 $mail->send($setup["from"], $mail->getFormattedName($info["usr_full_name"], $info["usr_email"]), APP_SHORT_NAME . ": " . ev_gettext("User account information updated")); 1419 1420 Language::restore(); 1421 } 1422 1423 1424 /** 1425 * Method used to send an email notification when the account 1426 * password of an user is changed. 1427 * 1428 * @access public 1429 * @param integer $usr_id The user ID 1430 * @param string $password The user' password 1431 * @return void 1432 */ 1433 function notifyUserPassword($usr_id, $password) 1434 { 1435 $info = User::getDetails($usr_id); 1436 $info["usr_password"] = $password; 1437 $info["projects"] = Project::getAssocList($usr_id, true, true); 1438 // open text template 1439 $tpl = new Template_API; 1440 $tpl->setTemplate('notifications/updated_password.tpl.text'); 1441 $tpl->bulkAssign(array( 1442 "app_title" => Misc::getToolCaption(), 1443 "user" => $info 1444 )); 1445 1446 // change the current locale 1447 Language::set(User::getLang($usr_id)); 1448 1449 $text_message = $tpl->getTemplateContents(); 1450 1451 // send email (use PEAR's classes) 1452 $mail = new Mail_API; 1453 $mail->setTextBody($text_message); 1454 $setup = $mail->getSMTPSettings(); 1455 $mail->send($setup["from"], $mail->getFormattedName($info["usr_full_name"], $info["usr_email"]), APP_SHORT_NAME . ": " . ev_gettext("User account password changed")); 1456 1457 Language::restore(); 1458 } 1459 1460 1461 /** 1462 * Method used to send an email notification when a new user 1463 * account is created. 1464 * 1465 * @access public 1466 * @param integer $usr_id The user ID 1467 * @param string $password The user' password 1468 * @return void 1469 */ 1470 function notifyNewUser($usr_id, $password) 1471 { 1472 $info = User::getDetails($usr_id); 1473 $info["usr_password"] = $password; 1474 $info["projects"] = Project::getAssocList($usr_id, true, true); 1475 // open text template 1476 $tpl = new Template_API; 1477 $tpl->setTemplate('notifications/new_user.tpl.text'); 1478 $tpl->bulkAssign(array( 1479 "app_title" => Misc::getToolCaption(), 1480 "user" => $info 1481 )); 1482 1483 // change the current locale 1484 Language::set(User::getLang($usr_id)); 1485 1486 $text_message = $tpl->getTemplateContents(); 1487 1488 // send email (use PEAR's classes) 1489 $mail = new Mail_API; 1490 $mail->setTextBody($text_message); 1491 $setup = $mail->getSMTPSettings(); 1492 $mail->send($setup["from"], $mail->getFormattedName($info["usr_full_name"], $info["usr_email"]), APP_SHORT_NAME . ": " . ev_gettext("New User information")); 1493 1494 Language::restore(); 1495 } 1496 1497 1498 /** 1499 * Send an email to all issue assignees 1500 * 1501 * @access public 1502 * @param integer $issue_id The ID of the issue 1503 * @param string $type The type of notification to send 1504 * @param array $data Any extra data to pass to the template 1505 */ 1506 function notifyAssignees($issue_id, $type, $data, $title = '') 1507 { 1508 $prj_id = Issue::getProjectID($issue_id); 1509 $assignees = Issue::getAssignedUserIDs($issue_id); 1510 if (count($assignees) > 0) { 1511 1512 // get issue details 1513 $issue = Notification::getIssueDetails($issue_id); 1514 // open text template 1515 $tpl = new Template_API; 1516 $tpl->setTemplate('notifications/' . $type . '.tpl.text'); 1517 $tpl->bulkAssign(array( 1518 "app_title" => Misc::getToolCaption(), 1519 "issue" => $issue, 1520 "data" => $data 1521 )); 1522 1523 for ($i = 0; $i < count($assignees); $i++) { 1524 if (!Workflow::shouldEmailAddress($prj_id, Mail_API::getEmailAddress(User::getFromHeader($assignees[$i])))) { 1525 continue; 1526 } 1527 1528 // change the current locale 1529 Language::set(User::getLang($assignees[$i])); 1530 $text_message = $tpl->getTemplateContents(); 1531 1532 // send email (use PEAR's classes) 1533 $mail = new Mail_API; 1534 $mail->setTextBody($text_message); 1535 $mail->setHeaders(Mail_API::getBaseThreadingHeaders($issue_id)); 1536 $mail->send(Notification::getFixedFromHeader($issue_id, '', 'issue'), User::getFromHeader($assignees[$i]), "[#$issue_id] $title: " . $issue['iss_summary'], TRUE, $issue_id, $type); 1537 } 1538 Language::restore(); 1539 } 1540 1541 } 1542 1543 1544 /** 1545 * Method used to send an email notification when an issue is 1546 * assigned to an user. 1547 * 1548 * @access public 1549 * @param array $users The list of users 1550 * @param integer $issue_id The issue ID 1551 * @return void 1552 */ 1553 function notifyNewAssignment($users, $issue_id) 1554 { 1555 $prj_id = Issue::getProjectID($issue_id); 1556 $emails = array(); 1557 for ($i = 0; $i < count($users); $i++) { 1558 $prefs = Prefs::get($users[$i]); 1559 if ((!empty($prefs)) && (@$prefs["receive_assigned_emails"][$prj_id]) && ($users[$i] != Auth::getUserID())) { 1560 $emails[] = User::getFromHeader($users[$i]); 1561 } 1562 } 1563 if (count($emails) == 0) { 1564 return false; 1565 } 1566 // get issue details 1567 $issue = Notification::getIssueDetails($issue_id); 1568 // open text template 1569 $tpl = new Template_API; 1570 $tpl->setTemplate('notifications/assigned.tpl.text'); 1571 $tpl->bulkAssign(array( 1572 "app_title" => Misc::getToolCaption(), 1573 "issue" => $issue, 1574 "current_user" => User::getFullName(Auth::getUserID()) 1575 )); 1576 1577 for ($i = 0; $i < count($emails); $i++) { 1578 $text_message = $tpl->getTemplateContents(); 1579 Language::set(User::getLang(User::getUserIDByEmail(Mail_API::getEmailAddress($emails[$i])))); 1580 1581 // send email (use PEAR's classes) 1582 $mail = new Mail_API; 1583 $mail->setTextBody($text_message); 1584 $mail->setHeaders(Mail_API::getBaseThreadingHeaders($issue_id)); 1585 $mail->send(Notification::getFixedFromHeader($issue_id, '', 'issue'), $emails[$i], "[#$issue_id] New Assignment: " . $issue['iss_summary'], TRUE, $issue_id, 'assignment'); 1586 } 1587 Language::restore(); 1588 } 1589 1590 1591 /** 1592 * Method used to send the account details of an user. 1593 * 1594 * @access public 1595 * @param integer $usr_id The user ID 1596 * @return void 1597 */ 1598 function notifyAccountDetails($usr_id) 1599 { 1600 $info = User::getDetails($usr_id); 1601 $info["projects"] = Project::getAssocList($usr_id, true, true); 1602 // open text template 1603 $tpl = new Template_API; 1604 $tpl->setTemplate('notifications/account_details.tpl.text'); 1605 $tpl->bulkAssign(array( 1606 "app_title" => Misc::getToolCaption(), 1607 "user" => $info 1608 )); 1609 1610 Language::set(User::getLang($usr_id)); 1611 $text_message = $tpl->getTemplateContents(); 1612 1613 // send email (use PEAR's classes) 1614 $mail = new Mail_API; 1615 $mail->setTextBody($text_message); 1616 $setup = $mail->getSMTPSettings(); 1617 $mail->send($setup["from"], $mail->getFormattedName($info["usr_full_name"], $info["usr_email"]), APP_SHORT_NAME . ": " . ev_gettext("Your User Account Details")); 1618 Language::restore(); 1619 } 1620 1621 1622 /** 1623 * Method used to get the list of subscribers for a given issue. 1624 * 1625 * @access public 1626 * @param integer $issue_id The issue ID 1627 * @param integer $type The type of subscription 1628 * @param integer $min_role Only show subscribers with this role or above 1629 * @return array An array containing 2 elements. Each a list of subscribers, separated by commas 1630 */ 1631 function getSubscribers($issue_id, $type = false, $min_role = false) 1632 { 1633 $issue_id = Misc::escapeInteger($issue_id); 1634 $subscribers = array( 1635 'staff' => array(), 1636 'customers' => array(), 1637 'all' => array() 1638 ); 1639 $prj_id = Issue::getProjectID($issue_id); 1640 $stmt = "SELECT 1641 sub_usr_id, 1642 usr_full_name, 1643 usr_email, 1644 pru_role 1645 FROM 1646 ( 1647 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription, 1648 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user"; 1649 1650 if ($type != false) { 1651 $stmt .= ", 1652 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type"; 1653 } 1654 $stmt .= " 1655 ) 1656 LEFT JOIN 1657 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_user 1658 ON 1659 (sub_usr_id = pru_usr_id AND pru_prj_id = $prj_id) 1660 WHERE 1661 sub_usr_id=usr_id AND 1662 sub_iss_id=$issue_id"; 1663 if ($min_role != false) { 1664 $stmt .= " AND 1665 pru_role >= " . Misc::escapeInteger($min_role); 1666 } 1667 if ($type != false) { 1668 $stmt .= " AND\nsbt_sub_id = sub_id AND 1669 sbt_type = '" . Misc::escapeString($type) . "'"; 1670 } 1671 $users = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 1672 if (PEAR::isError($users)) { 1673 Error_Handler::logError(array($users->getMessage(), $users->getDebugInfo()), __FILE__, __LINE__); 1674 return array(); 1675 } 1676 1677 for ($i = 0; $i < count($users); $i++) { 1678 if ($users[$i]['pru_role'] != User::getRoleID('Customer')) { 1679 $subscribers['staff'][] = $users[$i]['usr_full_name']; 1680 } else { 1681 $subscribers['customers'][] = $users[$i]['usr_full_name']; 1682 } 1683 } 1684 1685 if ($min_role == false) { 1686 $stmt = "SELECT 1687 DISTINCT sub_email, 1688 usr_full_name, 1689 pru_role 1690 FROM 1691 ( 1692 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription, 1693 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 1694 ) 1695 LEFT JOIN 1696 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user 1697 ON 1698 usr_email = sub_email 1699 LEFT JOIN 1700 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_user 1701 ON 1702 usr_id = pru_usr_id AND 1703 pru_prj_id = $prj_id 1704 WHERE 1705 sub_id = sbt_sub_id AND 1706 sub_iss_id=$issue_id"; 1707 if ($type != false) { 1708 $stmt .= " AND\nsbt_type = '" . Misc::escapeString($type) . "'"; 1709 } 1710 $emails = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 1711 if (PEAR::isError($emails)) { 1712 Error_Handler::logError(array($emails->getMessage(), $emails->getDebugInfo()), __FILE__, __LINE__); 1713 return array(); 1714 } 1715 for ($i = 0; $i < count($emails); $i++) { 1716 if (empty($emails[$i]['sub_email'])) { 1717 continue; 1718 } 1719 if ((!empty($emails[$i]['pru_role'])) && ($emails[$i]['pru_role'] != User::getRoleID('Customer'))) { 1720 $subscribers['staff'][] = $emails[$i]['usr_full_name']; 1721 } else { 1722 $subscribers['customers'][] = $emails[$i]['sub_email']; 1723 } 1724 } 1725 } 1726 1727 $subscribers['all'] = @join(', ', array_merge($subscribers['staff'], $subscribers['customers'])); 1728 $subscribers['staff'] = @implode(', ', $subscribers['staff']); 1729 $subscribers['customers'] = @implode(', ', $subscribers['customers']); 1730 return $subscribers; 1731 } 1732 1733 1734 /** 1735 * Method used to get the details of a given email notification 1736 * subscription. 1737 * 1738 * @access public 1739 * @param integer $sub_id The subcription ID 1740 * @return array The details of the subscription 1741 */ 1742 function getDetails($sub_id) 1743 { 1744 $stmt = "SELECT 1745 * 1746 FROM 1747 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 1748 WHERE 1749 sub_id=" . Misc::escapeInteger($sub_id); 1750 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 1751 if (PEAR::isError($res)) { 1752 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 1753 return ""; 1754 } else { 1755 if ($res["sub_usr_id"] != 0) { 1756 $user_info = User::getNameEmail($res["sub_usr_id"]); 1757 $res["sub_email"] = $user_info["usr_email"]; 1758 } 1759 return array_merge($res, Notification::getSubscribedActions($sub_id)); 1760 } 1761 } 1762 1763 1764 /** 1765 * Method used to get the subscribed actions for a given 1766 * subscription ID. 1767 * 1768 * @access public 1769 * @param integer $sub_id The subcription ID 1770 * @return array The subscribed actions 1771 */ 1772 function getSubscribedActions($sub_id) 1773 { 1774 $stmt = "SELECT 1775 sbt_type, 1776 1 1777 FROM 1778 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 1779 WHERE 1780 sbt_sub_id=" . Misc::escapeInteger($sub_id); 1781 $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt); 1782 if (PEAR::isError($res)) { 1783 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 1784 return ""; 1785 } else { 1786 return $res; 1787 } 1788 } 1789 1790 1791 /** 1792 * Method used to get the list of subscribers for a given issue. 1793 * 1794 * @access public 1795 * @param integer $issue_id The issue ID 1796 * @return array The list of subscribers 1797 */ 1798 function getSubscriberListing($issue_id) 1799 { 1800 $stmt = "SELECT 1801 sub_id, 1802 sub_iss_id, 1803 sub_usr_id, 1804 sub_email 1805 FROM 1806 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 1807 WHERE 1808 sub_iss_id=" . Misc::escapeInteger($issue_id); 1809 $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC); 1810 if (PEAR::isError($res)) { 1811 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 1812 return ""; 1813 } else { 1814 for ($i = 0; $i < count($res); $i++) { 1815 if ($res[$i]["sub_usr_id"] != 0) { 1816 $res[$i]["sub_email"] = User::getFromHeader($res[$i]["sub_usr_id"]); 1817 } 1818 // need to get the list of subscribed actions now 1819 $actions = Notification::getSubscribedActions($res[$i]["sub_id"]); 1820 $res[$i]["actions"] = @implode(", ", array_keys($actions)); 1821 } 1822 return $res; 1823 } 1824 } 1825 1826 1827 /** 1828 * Method used to remove all subscriptions associated with a given 1829 * set of issues. 1830 * 1831 * @access public 1832 * @param array $ids The list of issues 1833 * @return boolean 1834 */ 1835 function removeByIssues($ids) 1836 { 1837 $items = @implode(", ", Misc::escapeInteger($ids)); 1838 $stmt = "SELECT 1839 sub_id 1840 FROM 1841 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 1842 WHERE 1843 sub_iss_id IN ($items)"; 1844 $res = $GLOBALS["db_api"]->dbh->getCol($stmt); 1845 if (PEAR::isError($res)) { 1846 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 1847 return false; 1848 } else { 1849 Notification::remove($res); 1850 return true; 1851 } 1852 } 1853 1854 1855 /** 1856 * Method used to remove all rows associated with a set of 1857 * subscription IDs 1858 * 1859 * @access public 1860 * @param array $items The list of subscription IDs 1861 * @return boolean 1862 */ 1863 function remove($items) 1864 { 1865 $items = Misc::escapeInteger($items); 1866 $stmt = "SELECT 1867 sub_iss_id 1868 FROM 1869 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 1870 WHERE 1871 sub_id IN (" . implode(", ", $items) . ")"; 1872 $issue_id = $GLOBALS["db_api"]->dbh->getOne($stmt); 1873 1874 for ($i = 0; $i < count($items); $i++) { 1875 $sub_id = $items[$i]; 1876 $subscriber = Notification::getSubscriber($sub_id); 1877 $stmt = "DELETE FROM 1878 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 1879 WHERE 1880 sub_id=$sub_id"; 1881 $GLOBALS["db_api"]->dbh->query($stmt); 1882 $stmt = "DELETE FROM 1883 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 1884 WHERE 1885 sbt_sub_id=$sub_id"; 1886 $GLOBALS["db_api"]->dbh->query($stmt); 1887 // need to save a history entry for this 1888 History::add($issue_id, Auth::getUserID(), History::getTypeID('notification_removed'), 1889 ev_gettext('Notification list entry (%1$s) removed by %2$s', $subscriber, User::getFullName(Auth::getUserID()))); 1890 } 1891 Issue::markAsUpdated($issue_id); 1892 return true; 1893 } 1894 1895 1896 /** 1897 * Returns the email address associated with a notification list 1898 * subscription, user based or otherwise. 1899 * 1900 * @access public 1901 * @param integer $sub_id The subscription ID 1902 * @return string The email address 1903 */ 1904 function getSubscriber($sub_id) 1905 { 1906 $stmt = "SELECT 1907 sub_usr_id, 1908 sub_email 1909 FROM 1910 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 1911 WHERE 1912 sub_id=" . Misc::escapeInteger($sub_id); 1913 $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC); 1914 if (PEAR::isError($res)) { 1915 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 1916 return ''; 1917 } else { 1918 if (empty($res['sub_usr_id'])) { 1919 return $res['sub_email']; 1920 } else { 1921 return User::getFromHeader($res['sub_usr_id']); 1922 } 1923 } 1924 } 1925 1926 1927 /** 1928 * Method used to get the full list of possible notification actions. 1929 * 1930 * @access public 1931 * @return array All of the possible notification actions 1932 */ 1933 function getAllActions() 1934 { 1935 return array( 1936 'updated', 1937 'closed', 1938 'emails', 1939 'files' 1940 ); 1941 } 1942 1943 1944 /** 1945 * Method used to get the full list of default notification 1946 * actions. 1947 * 1948 * @access public 1949 * @return array The list of default notification actions 1950 */ 1951 function getDefaultActions() 1952 { 1953 $actions = array(); 1954 $setup = Setup::load(); 1955 1956 if (@$setup['update'] == 1) { 1957 $actions[] = 'updated'; 1958 } 1959 if (@$setup['closed'] == 1) { 1960 $actions[] = 'closed'; 1961 } 1962 if (@$setup['files'] == 1) { 1963 $actions[] = 'files'; 1964 } 1965 if (@$setup['emails'] == 1) { 1966 $actions[] = 'emails'; 1967 } 1968 return $actions; 1969 } 1970 1971 1972 /** 1973 * Method used to subscribe an user to a set of actions in an issue. 1974 * 1975 * @access public 1976 * @param integer $usr_id The user ID of the person performing this action 1977 * @param integer $issue_id The issue ID 1978 * @param integer $subscriber_usr_id The user ID of the subscriber 1979 * @param array $actions The list of actions to subscribe this user to 1980 * @param boolean $add_history Whether to add a history entry about this change or not 1981 * @return integer 1 if the update worked, -1 otherwise 1982 */ 1983 function subscribeUser($usr_id, $issue_id, $subscriber_usr_id, $actions, $add_history = TRUE) 1984 { 1985 $issue_id = Misc::escapeInteger($issue_id); 1986 $prj_id = Issue::getProjectID($issue_id); 1987 $subscriber_usr_id = Misc::escapeInteger($subscriber_usr_id); 1988 1989 // call workflow to modify actions or cancel adding this user. 1990 $email = ''; 1991 $workflow = Workflow::handleSubscription($prj_id, $issue_id, $subscriber_usr_id, $email, $actions); 1992 if ($workflow === false) { 1993 // cancel subscribing the user 1994 return -2; 1995 } 1996 if ($subscriber_usr_id == APP_SYSTEM_USER_ID) { 1997 return -2; 1998 } 1999 2000 $stmt = "SELECT 2001 COUNT(sub_id) 2002 FROM 2003 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 2004 WHERE 2005 sub_iss_id=$issue_id AND 2006 sub_usr_id=$subscriber_usr_id"; 2007 $total = $GLOBALS["db_api"]->dbh->getOne($stmt); 2008 if ($total > 0) { 2009 return -1; 2010 } 2011 $stmt = "INSERT INTO 2012 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 2013 ( 2014 sub_iss_id, 2015 sub_usr_id, 2016 sub_created_date, 2017 sub_level, 2018 sub_email 2019 ) VALUES ( 2020 $issue_id, 2021 $subscriber_usr_id, 2022 '" . Date_API::getCurrentDateGMT() . "', 2023 'issue', 2024 '' 2025 )"; 2026 $res = $GLOBALS["db_api"]->dbh->query($stmt); 2027 if (PEAR::isError($res)) { 2028 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 2029 return -1; 2030 } else { 2031 $sub_id = $GLOBALS["db_api"]->get_last_insert_id(); 2032 for ($i = 0; $i < count($actions); $i++) { 2033 Notification::addType($sub_id, $actions[$i]); 2034 } 2035 // need to mark the issue as updated 2036 Issue::markAsUpdated($issue_id); 2037 // need to save a history entry for this 2038 if ($add_history) { 2039 History::add($issue_id, $usr_id, History::getTypeID('notification_added'), 2040 ev_gettext('Notification list entry (%1$s) added by %2$s', User::getFromHeader($subscriber_usr_id), User::getFullName($usr_id))); 2041 } 2042 return 1; 2043 } 2044 } 2045 2046 2047 /** 2048 * Method used to add a new subscriber manually, by using the 2049 * email notification interface. 2050 * 2051 * @access public 2052 * @param integer $usr_id The user ID of the person performing this change 2053 * @param integer $issue_id The issue ID 2054 * @param string $form_email The email address to subscribe 2055 * @param array $actions The actions to subcribe to 2056 * @return integer 1 if the update worked, -1 otherwise 2057 */ 2058 function subscribeEmail($usr_id, $issue_id, $form_email, $actions) 2059 { 2060 $form_email = strtolower(Mail_API::getEmailAddress($form_email)); 2061 // first check if this is an actual user or just an email address 2062 $user_emails = User::getAssocEmailList(); 2063 if (in_array($form_email, array_keys($user_emails))) { 2064 return Notification::subscribeUser($usr_id, $issue_id, $user_emails[$form_email], $actions); 2065 } 2066 2067 $issue_id = Misc::escapeInteger($issue_id); 2068 $email = Misc::escapeString($form_email); 2069 $prj_id = Issue::getProjectID($issue_id); 2070 2071 // call workflow to modify actions or cancel adding this user. 2072 $subscriber_usr_id = false; 2073 $workflow = Workflow::handleSubscription($prj_id, $issue_id, $subscriber_usr_id, $email, $actions); 2074 if ($workflow === false) { 2075 // cancel subscribing the user 2076 return -2; 2077 } 2078 2079 // manual check to prevent duplicates 2080 if (!empty($email)) { 2081 $stmt = "SELECT 2082 COUNT(sub_id) 2083 FROM 2084 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 2085 WHERE 2086 sub_iss_id=$issue_id AND 2087 sub_email='$email'"; 2088 $total = $GLOBALS["db_api"]->dbh->getOne($stmt); 2089 if ($total > 0) { 2090 return -1; 2091 } 2092 } 2093 $stmt = "INSERT INTO 2094 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 2095 ( 2096 sub_iss_id, 2097 sub_usr_id, 2098 sub_created_date, 2099 sub_level, 2100 sub_email 2101 ) VALUES ( 2102 $issue_id, 2103 0, 2104 '" . Date_API::getCurrentDateGMT() . "', 2105 'issue', 2106 '$email' 2107 )"; 2108 $res = $GLOBALS["db_api"]->dbh->query($stmt); 2109 if (PEAR::isError($res)) { 2110 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 2111 return -1; 2112 } else { 2113 $sub_id = $GLOBALS["db_api"]->get_last_insert_id(); 2114 for ($i = 0; $i < count($actions); $i++) { 2115 Notification::addType($sub_id, $actions[$i]); 2116 } 2117 // need to mark the issue as updated 2118 Issue::markAsUpdated($issue_id); 2119 // need to save a history entry for this 2120 History::add($issue_id, $usr_id, History::getTypeID('notification_added'), 2121 ev_gettext('Notification list entry (\'%1$s\') added by %2$s', $email, User::getFullName($usr_id))); 2122 return 1; 2123 } 2124 } 2125 2126 2127 /** 2128 * Method used to add the subscription type to the given 2129 * subscription. 2130 * 2131 * @access public 2132 * @param integer $sub_id The subscription ID 2133 * @param string $type The subscription type 2134 * @return void 2135 */ 2136 function addType($sub_id, $type) 2137 { 2138 $stmt = "INSERT INTO 2139 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 2140 ( 2141 sbt_sub_id, 2142 sbt_type 2143 ) VALUES ( 2144 " . Misc::escapeInteger($sub_id) . ", 2145 '" . Misc::escapeString($type) . "' 2146 )"; 2147 $GLOBALS["db_api"]->dbh->query($stmt); 2148 } 2149 2150 2151 /** 2152 * Method used to update the details of a given subscription. 2153 * 2154 * @access public 2155 * @param integer $sub_id The subscription ID 2156 * @return integer 1 if the update worked, -1 otherwise 2157 */ 2158 function update($sub_id) 2159 { 2160 $sub_id = Misc::escapeInteger($sub_id); 2161 2162 $stmt = "SELECT 2163 sub_iss_id, 2164 sub_usr_id 2165 FROM 2166 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 2167 WHERE 2168 sub_id=$sub_id"; 2169 list($issue_id, $usr_id) = $GLOBALS["db_api"]->dbh->getRow($stmt); 2170 2171 $email = strtolower(Mail_API::getEmailAddress($_POST["email"])); 2172 $usr_id = User::getUserIDByEmail($email); 2173 if (!empty($usr_id)) { 2174 $email = ''; 2175 } else { 2176 $usr_id = 0; 2177 $email = Misc::escapeString($_POST["email"]); 2178 } 2179 $prj_id = Issue::getProjectID($issue_id); 2180 2181 // call workflow to modify actions or cancel adding this user. 2182 $actions = array(); 2183 $subscriber_usr_id = false; 2184 $workflow = Workflow::handleSubscription($prj_id, $issue_id, $subscriber_usr_id, $email, $actions); 2185 if ($workflow === false) { 2186 // cancel subscribing the user 2187 return -2; 2188 } 2189 2190 // always set the type of notification to issue-level 2191 $stmt = "UPDATE 2192 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription 2193 SET 2194 sub_level='issue', 2195 sub_email='" . Misc::escapeString($email) . "', 2196 sub_usr_id=$usr_id 2197 WHERE 2198 sub_id=$sub_id"; 2199 $res = $GLOBALS["db_api"]->dbh->query($stmt); 2200 if (PEAR::isError($res)) { 2201 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__); 2202 return -1; 2203 } else { 2204 $stmt = "DELETE FROM 2205 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type 2206 WHERE 2207 sbt_sub_id=$sub_id"; 2208 $GLOBALS["db_api"]->dbh->query($stmt); 2209 // now add them all again 2210 for ($i = 0; $i < count($_POST["actions"]); $i++) { 2211 Notification::addType($sub_id, $_POST["actions"][$i]); 2212 } 2213 // need to mark the issue as updated 2214 Issue::markAsUpdated($issue_id); 2215 // need to save a history entry for this 2216 History::add($issue_id, Auth::getUserID(), History::getTypeID('notification_updated'), 2217 ev_gettext('Notification list entry (\'%1$s\') updated by %2$s', Notification::getSubscriber($sub_id), User::getFullName(Auth::getUserID()))); 2218 return 1; 2219 } 2220 } 2221 } 2222 2223 // benchmarking the included file (aka setup time) 2224 if (APP_BENCHMARK) { 2225 $GLOBALS['bench']->setMarker('Included Notification Class'); 2226 }
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 |