[ Index ]

PHP Cross Reference of Eventum

title

Body

[close]

/include/ -> class.notification.php (source)

   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  }


Generated: Wed Dec 19 21:21:33 2007 Cross-referenced by PHPXref 0.7