array(), 'reporter' => array(), 'assignee' => array(), 'favorite' => array(), 'voter' => array(), 'commenter' => array() ); /** * User cache list * @var array */ private $users = array(); /** * A list of notices per-user that are combined together in NotificationCenter::finalize() * @var array */ private $notices = array(); /** * Sets the bug data so that all methods in this class have access to * it when sending emails. * * @param array Original bug data * @param array Modified bug data */ public function setBugData($original, $modified = array()) { if (sizeof($modified) > 0) { $this->bug = $modified; } else { $this->bug = $original; } $this->original = $original; $this->modified = $modified; $this->roles['-notapplicable-'] = (sizeof($modified) > 0 ? array($original['assignedto'], $modified['assignedto']) : array($original['assignedto'])); $this->roles['reporter'] = array($original['userid']); $this->roles['assignee'][] = (sizeof($modified) > 0 ? $modified['assignedto'] : $original['assignedto']); $this->_fetchUserCache(); } /** * Fetches all the users who could be related to the bug and sticks * their information into an array. */ private function _fetchUserCache() { $newbuggers = BSApp::$db->query("SELECT userid FROM " . TABLE_PREFIX . "useremail WHERE relation = " . bugdar::$emailOptions['relations']['-notapplicable-'] . " AND mask & " . bugdar::$emailOptions['notifications']['newbug']); foreach ($newbuggers as $newbug) { $this->roles['-notapplicable-']["$newbug[userid]"] = $newbug['userid']; } $favorites = BSApp::$db->query("SELECT userid FROM " . TABLE_PREFIX . "favorite WHERE bugid = " . BSApp::$input->clean($this->bug['bugid'], TYPE_UINT)); foreach ($favorites as $fav) { $this->roles['favorite']["$fav[userid]"] = $fav['userid']; } $voters = BSApp::$db->queryFirst("SELECT userids FROM " . TABLE_PREFIX . "vote WHERE bugid = " . BSApp::$input->clean($this->bug['bugid'], TYPE_UINT)); $this->roles['voter'] = preg_split('#,#', $voters['userids'], 0, PREG_SPLIT_NO_EMPTY); $commenters = BSApp::$db->query("SELECT userid FROM " . TABLE_PREFIX . "comment WHERE bugid = " . BSApp::$input->clean($this->bug['bugid'], TYPE_UINT)); foreach ($commenters as $comment) { $this->roles['commenter']["$comment[userid]"] = $comment['userid']; } $masterids = array_merge($this->roles['-notapplicable-'], $this->roles['reporter'], $this->roles['assignee'], $this->roles['favorite'], $this->roles['voter'], $this->roles['commenter']); $masterids = BSFunctions::array_strip_empty(array_unique($masterids)); if (is_array($masterids) && sizeof($masterids) > 0) { $userinfo = BSApp::$db->query(" SELECT user.*, useremail.* FROM " . TABLE_PREFIX . "useremail AS useremail LEFT JOIN " . TABLE_PREFIX . "user AS user ON (user.userid = useremail.userid) WHERE useremail.userid IN (" . implode(',', $masterids) . ") "); foreach ($userinfo as $user) { if (!is_array($this->users["$user[userid]"])) { $this->users["$user[userid]"] = $user; unset($this->users["$user[userid]"]['mask'], $this->users["$user[userid]"]['relation']); } $this->users["$user[userid]"]['options']["$user[relation]"] = $user['mask']; } } } /** * Sends the appropriate emails for changes to bugs. This function * works a lot like the Logging class by taking BugAPI->record and * BugAPI->values and then comparing the two arries and sending emails * with the differences. */ public function sendBugChangeNotice() { if (!isset($this->modified['bugid'])) { return; } // fields with custom mask information if ($this->original['assignedto'] != $this->modified['assignedto']) { if ($this->original['assignedto'] != '') { $this->_noticeNoLongerAssigned($this->original['assignedto']); } if ($this->modified['assignedto'] != '') { $this->_noticeNowAssigned($this->modified['assignedto']); } } if ($this->original['status'] != $this->modified['status']) { $this->_noticeStatusChange($this->original['status'], $this->modified['status']); } if ($this->original['resolution'] != $this->modified['resolution']) { $this->_noticeResolutionChange($this->original['resolution'], $this->modified['resolution']); } if ($this->original['duplicates'] != $this->modified['duplicates']) { $this->_noticeDuplicatesChange($this->original['duplicates'], $this->modified['duplicates']); } // other standard fields that don't have custom masks if ($this->original['severity'] != $this->modified['severity']) { $this->_noticeSeverityChange($this->original['severity'], $this->modified['severity']); } if ($this->original['priority'] != $this->modified['priority']) { $this->_noticePriorityChange($this->original['priority'], $this->modified['priority']); } if (($this->original['product'] != $this->modified['product']) || ($this->original['component'] != $this->modified['component']) || ($this->original['version'] != $this->modified['version'])) { $this->_noticePCVChange(array($this->original['product'], $this->original['component'], $this->original['version']), array($this->modified['product'], $this->modified['component'], $this->modified['version'])); } $dofields = array( 'summary' => -1, 'dependency' => -1, 'hidden' => -1 ); foreach ($dofields as $field => $lookup) { if ($this->original["$field"] != $this->modified["$field"]) { $this->_noticeOtherChange($field, $this->original["$field"], $this->modified["$field"]); } } } /** * Sends an email to the specified user ID that they are no longer the * person assigned to the bug. * * @param integer User ID to send to */ private function _noticeNoLongerAssigned($userid) { if ($this->users["$userid"]['options'][0] & bugdar::$emailOptions['notifications']['assignedto'] && in_array($userid, $this->roles['-notapplicable-'])) { $user = construct_user_display(bugdar::$userinfo, false); $email = get_email_text('notice_unassigned'); $this->notices["$userid"][] = sprintf($email['part'], $user); } } /** * Informs the user that they have been made the assignee of the bug. * * @param integer User ID */ private function _noticeNowAssigned($userid) { if ($this->users["$userid"]['options'][0] & bugdar::$emailOptions['notifications']['assignedto'] && in_array($userid, $this->roles['-notapplicable-'])) { $user = construct_user_display(bugdar::$userinfo, false); $email = get_email_text('notice_assigned'); $this->notices["$userid"][] = sprintf($email['part'], $user); } } /** * Sends a message to inform users that the status has changed. * * @param integer Old status * @param integer New status */ private function _noticeStatusChange($old, $new) { $userlist = $this->_fetchUsersWithOnBit('statusresolve'); $old = bugdar::$datastore['status'][$old]['status']; $new = bugdar::$datastore['status'][$new]['status']; foreach ($userlist as $userid => $user) { $email = get_email_text('notice_status'); $this->notices["$user[userid]"][] = sprintf($email['part'], $new, $old); } } /** * Sends an email to inform users that the resolution has changed. * * @param integer Old resolution * @param integer New resolution */ private function _noticeResolutionChange($old, $new) { $userlist = $this->_fetchUsersWithOnBit('statusresolve'); $old = bugdar::$datastore['resolution'][$old]['resolution']; $new = bugdar::$datastore['resolution'][$new]['resolution']; foreach ($userlist as $userid => $user) { $email = get_email_text('notice_resolution'); $this->notices["$user[userid]"][] = sprintf($email['part'], $new, $old); } } /** * Informs users that the duplicates list has changed. * * @param string Old duplicates list * @param string New duplicates list */ private function _noticeDuplicatesChange($old, $new) { $userlist = $this->_fetchUsersWithOnBit('duplicates'); foreach ($userlist as $userid => $user) { $email = get_email_text('notice_duplicates'); $this->notices["$user[userid]"][] = sprintf($email['part'], $old, $new); } } /** * Sends an email to inform users that the severity has changed. * * @param integer Old severity * @param integer New severity */ private function _noticeSeverityChange($old, $new) { $userlist = $this->_fetchUsersWithOnBit('otherfield'); $old = bugdar::$datastore['severity'][$old]['severity']; $new = bugdar::$datastore['severity'][$new]['severity']; foreach ($userlist as $userid => $user) { $this->notices["$user[userid]"][] = sprintf($email['part'], $old, $new); } } /** * Informs users that the priority changed. * * @param integer Old priority * @param integer New priority */ private function _noticePriorityChange($old, $new) { $userlist = $this->_fetchUsersWithOnBit('otherfield'); $old = bugdar::$datastore['priority'][$old]['priority']; $new = bugdar::$datastore['priority'][$new]['priority']; foreach ($userlist as $userid => $user) { $email = get_email_text('notice_priority'); $this->notices["$user[userid]"][] = sprintf($email['part'], $old, $new); } } /** * Sends an email telling users that the product, component, or version * has changed. This is done all at once because you really need to see * the whole thing in the notice. * * @param array Original PCV * @param array Modified PCV */ private function _noticePCVChange($old, $new) { $userlist = $this->_fetchUsersWithOnBit('otherfield'); $products = &bugdar::$datastore['product']; $versions = &bugdar::$datastore['version']; $old = $products[$old[0]]['title'] . '/' . ($old[1] ? $products[$old[1]]['title'] . '/' : '') . $versions[$old[2]]['version']; $new = $products[$new[0]]['title'] . '/' . ($new[1] ? $products[$new[1]]['title'] . '/' : '') . $versions[$new[2]]['version']; foreach ($userlist as $userid => $user) { $email = get_email_text('notice_product'); $this->notices["$user[userid]"][] = sprintf($email['part'], $old, $new); } } /** * Sends the appropriate users information about a new comment being * posted to the bug report. * * @param array CommentAPI->values array */ public function sendNewCommentNotice($comment) { $userlist = $this->_fetchUsersWithOnBit('newcomment'); foreach ($userlist as $userid => $user) { $user = construct_user_display(bugdar::$userinfo, false); $date = BSApp::$date->format(bugdar::$options['dateformat'], $comment['dateline']); $email = get_email_text('notice_comment'); $this->notices["$userid"][] = sprintf($email['part'], $user, $date, $comment['comment']); } } /** * A notice for an individual field changing. * * @param string Field name * @param mixed Original value * @param mixed Modified value */ private function _noticeOtherChange($name, $old, $new) { $userlist = $this->_fetchUsersWithOnBit('otherfield'); foreach ($userlist as $userid => $user) { $email = get_email_text('notice_other'); $this->notices["$user[userid]"][] = sprintf($email['part'], $name, $old, $new); } } /** * Sends appropriate users a notice when a new attachment has been * added. * * @param array AttachmentAPI->values array * @param array List of all attachments made obsolete * @param array Newly-inserted attachment ID */ public function sendNewAttachmentNotice($attachment, $obsolete, $id) { $userlist = $this->_fetchUsersWithOnBit('newattachment'); foreach ($userlist as $userid => $user) { $user = construct_user_display(bugdar::$userinfo, false); $obsoletes = implode(', ', (array)$obsolete); $email = get_email_text('notice_attachment'); $this->notices["$userid"][] = sprintf($email['part'], $user, $attachment['filename'], $attachment['description'], $attachment['filesize'], $obsoletes, bugdar::$options['trackerurl'], $attachment['attachmentid']); } } /** * Sends a new bug notification notice to all those who have the option * turned no. This does not use fetchUsersWithOnBit() because a * query is more effective. * * @param array Bug values array * @param array Comment values array */ public function sendNewBugNotice($bug, $comment) { $userinfo = BSApp::$db->query(" SELECT user.*, useremail.* FROM " . TABLE_PREFIX . "useremail AS useremail LEFT JOIN " . TABLE_PREFIX . "user AS user ON (user.userid = useremail.userid) WHERE useremail.relation = 0 AND useremail.mask & " . bugdar::$emailOptions['notifications']['newbug'] . " "); foreach ($userinfo as $userInfo) { if (!is_array($this->users["$userInfo[userid]"])) { $user = construct_user_display(bugdar::$userinfo, false); $this->users["$userInfo[userid]"] = $userInfo; $product = bugdar::$datastore['product']["$bug[product]"]['title'] . '/' . ($bug['component'] ? bugdar::$datastore['product']["$bug[component]"]['title'] . '/' : '') . bugdar::$datastore['version']["$bug[version]"]['version']; $email = get_email_text('notice_new_bug'); $this->notices["$userInfo[userid]"][] = sprintf($email['part'], $bug['bugid'], $bug['summary'], $user, $product, $comment['comment']); unset($this->users["$userInfo[userid]"]['mask'], $this->users["$userInfo[userid]"]['relation']); } $this->users["$userInfo[userid]"]['options']["$userInfo[relation]"] = $userInfo['mask']; } } /** * Generates an array of users who have a given email notification flag * turned on in their bitfields. * * @param string Notification bitfield name * * @return array Array of users and their data */ private function _fetchUsersWithOnBit($bitname) { $idlist = array(); foreach ($this->users as $user) { foreach (bugdar::$emailOptions['relations'] as $name => $bit) { if (in_array($user['userid'], $this->roles["$name"]) && $user['options']["$bit"] & bugdar::$emailOptions['notifications']["$bitname"]) { $idlist[] = $user['userid']; } } } $masters = array_unique($idlist); $return = array(); foreach ($masters as $userid) { $return["$userid"] = &$this->users["$userid"]; } return $return; } /** * Compiles and sends the actual emails to users. */ public function finalize() { // get the current bug for permissions checks $bug = BSApp::$db->queryFirst("SELECT * FROM " . TABLE_PREFIX . "bug WHERE bugid = " . $this->bug['bugid']); foreach ($this->notices as $userid => $noticelist) { if ($userid == bugdar::$userinfo['userid']) { BSApp::debug("skipping user $userid because they're the one doing the thing"); continue; } // we wouldn't want people who favorite bugs getting hidden notices if (!check_bug_permissions($bug, $this->users["$userid"])) { BSApp::debug("skipping user $userid ({$this->users[$userid]['email']}) because of permissions"); continue; } $parts = implode("\n\n", $noticelist); $email = get_email_text('bug_notification'); $body = sprintf($email['bodyText'], $this->users[$userid]['displayname'], bugdar::$options['trackertitle'], $this->bug['summary'], $this->bug['bugid'], bugdar::$options['trackerurl'], $parts); $mail = new BSMail(); $mail->setSubject(sprintf($email['subject'], bugdar::$options['trackertitle'], $this->bug['summary'])); $mail->setBodyText($body); $mail->setFromAddress(MAIL_FROM_ADDRESS); $mail->setFromName(MAIL_FROM_NAME); if (!empty($this->users["$userid"]['email'])) { $mail->send($this->users[$userid]['email'], $this->users[$userid]['displayname']); } else { BSApp::debug("not sending an email to " . $userid . " because they don't have one?"); } } } /** * Returns the locale name from a given user ID * * @param integer User ID * * @return string Locale */ private function _localeFromUserId($userid) { $langcode = bugdar::$datastore['language'][$this->users[$userid]['languageid']]['langcode']; if (!$langcode) { $langcode = bugdar::$datastore['language'][bugdar::$options['defaultlanguage']]['langcode']; } return $langcode; } } ?>