2 /*=====================================================================*\
3 || ###################################################################
5 || # Copyright ©2002-2007 Blue Static
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version 2 of the License.
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
25 * This class determines which emails need to be sent out based on user
26 * options and bug changes, and then it sends said emails.
29 * @copyright Copyright ©2002 - 2007, Blue Static
34 class NotificationCenter
40 private $bug = array();
46 private $original = array();
52 private $modified = array();
55 * Role list: a list of user IDs with their relations to the bug
58 private $roles = array(
59 '-notapplicable-' => array(),
60 'reporter' => array(),
61 'assignee' => array(),
62 'favorite' => array(),
64 'commenter' => array()
71 private $users = array();
74 * A list of notices per-user that are combined together in NotificationCenter::finalize()
77 private $notices = array();
80 * Sets the bug data so that all methods in this class have access to
81 * it when sending emails.
83 * @param array Original bug data
84 * @param array Modified bug data
86 public function setBugData($original, $modified = array())
88 if (sizeof($modified) > 0)
90 $this->bug
= $modified;
94 $this->bug
= $original;
97 $this->original
= $original;
98 $this->modified
= $modified;
100 $this->roles
['-notapplicable-'] = (sizeof($modified) > 0 ? array($original['assignedto'], $modified['assignedto']) : array($original['assignedto']));
101 $this->roles
['reporter'] = array($original['userid']);
102 $this->roles
['assignee'][] = (sizeof($modified) > 0 ? $modified['assignedto'] : $original['assignedto']);
104 $this->_fetchUserCache();
108 * Fetches all the users who could be related to the bug and sticks
109 * their information into an array.
111 private function _fetchUserCache()
113 $newbuggers = BSApp
::$db->query("SELECT userid FROM " . TABLE_PREFIX
. "useremail WHERE relation = " . bugdar
::$emailOptions['relations']['-notapplicable-'] . " AND mask & " . bugdar
::$emailOptions['notifications']['newbug']);
114 foreach ($newbuggers as $newbug)
116 $this->roles
['-notapplicable-']["$newbug[userid]"] = $newbug['userid'];
119 $favorites = BSApp
::$db->query("SELECT userid FROM " . TABLE_PREFIX
. "favorite WHERE bugid = " . BSApp
::$input->clean($this->bug
['bugid'], TYPE_UINT
));
120 foreach ($favorites as $fav)
122 $this->roles
['favorite']["$fav[userid]"] = $fav['userid'];
125 $voters = BSApp
::$db->queryFirst("SELECT userids FROM " . TABLE_PREFIX
. "vote WHERE bugid = " . BSApp
::$input->clean($this->bug
['bugid'], TYPE_UINT
));
126 $this->roles
['voter'] = preg_split('#,#', $voters['userids'], 0, PREG_SPLIT_NO_EMPTY
);
128 $commenters = BSApp
::$db->query("SELECT userid FROM " . TABLE_PREFIX
. "comment WHERE bugid = " . BSApp
::$input->clean($this->bug
['bugid'], TYPE_UINT
));
129 foreach ($commenters as $comment)
131 $this->roles
['commenter']["$comment[userid]"] = $comment['userid'];
134 $masterids = array_merge($this->roles
['-notapplicable-'], $this->roles
['reporter'], $this->roles
['assignee'], $this->roles
['favorite'], $this->roles
['voter'], $this->roles
['commenter']);
135 $masterids = BSFunctions
::array_strip_empty(array_unique($masterids));
137 if (is_array($masterids) && sizeof($masterids) > 0)
139 $userinfo = BSApp
::$db->query("
140 SELECT user.*, useremail.*
141 FROM " . TABLE_PREFIX
. "useremail AS useremail
142 LEFT JOIN " . TABLE_PREFIX
. "user AS user
143 ON (user.userid = useremail.userid)
144 WHERE useremail.userid IN (" . implode(',', $masterids) . ")
146 foreach ($userinfo as $user)
148 if (!is_array($this->users
["$user[userid]"]))
150 $this->users
["$user[userid]"] = $user;
151 unset($this->users
["$user[userid]"]['mask'], $this->users
["$user[userid]"]['relation']);
153 $this->users
["$user[userid]"]['options']["$user[relation]"] = $user['mask'];
159 * Sends the appropriate emails for changes to bugs. This function
160 * works a lot like the Logging class by taking BugAPI->record and
161 * BugAPI->values and then comparing the two arries and sending emails
162 * with the differences.
164 public function sendBugChangeNotice()
166 if (!isset($this->modified
['bugid']))
171 // fields with custom mask information
172 if ($this->original
['assignedto'] != $this->modified
['assignedto'])
174 if ($this->original
['assignedto'] != '')
176 $this->_noticeNoLongerAssigned($this->original
['assignedto']);
178 if ($this->modified
['assignedto'] != '')
180 $this->_noticeNowAssigned($this->modified
['assignedto']);
183 if ($this->original
['status'] != $this->modified
['status'])
185 $this->_noticeStatusChange($this->original
['status'], $this->modified
['status']);
187 if ($this->original
['resolution'] != $this->modified
['resolution'])
189 $this->_noticeResolutionChange($this->original
['resolution'], $this->modified
['resolution']);
191 if ($this->original
['duplicates'] != $this->modified
['duplicates'])
193 $this->_noticeDuplicatesChange($this->original
['duplicates'], $this->modified
['duplicates']);
196 // other standard fields that don't have custom masks
197 if ($this->original
['severity'] != $this->modified
['severity'])
199 $this->_noticeSeverityChange($this->original
['severity'], $this->modified
['severity']);
201 if ($this->original
['priority'] != $this->modified
['priority'])
203 $this->_noticePriorityChange($this->original
['priority'], $this->modified
['priority']);
205 if (($this->original
['product'] != $this->modified
['product']) || ($this->original
['component'] != $this->modified
['component']) || ($this->original
['version'] != $this->modified
['version']))
207 $this->_noticePCVChange(array($this->original
['product'], $this->original
['component'], $this->original
['version']), array($this->modified
['product'], $this->modified
['component'], $this->modified
['version']));
215 foreach ($dofields as $field => $lookup)
217 if ($this->original
["$field"] != $this->modified["$field"])
219 $this->_noticeOtherChange($field, $this->original
["$field"], $this->modified["$field"]);
225 * Sends an email to the specified user ID that they are no longer the
226 * person assigned to the bug.
228 * @param integer User ID to send to
230 private function _noticeNoLongerAssigned($userid)
232 if ($this->users
["$userid"]['options'][0] & bugdar::$emailOptions['notifications']['assignedto'] && in_array($userid, $this->roles['-notapplicable-']))
234 $user = construct_user_display(bugdar::$userinfo, false);
236 $email = get_email_text('notice_unassigned');
237 $this->notices["$userid"][] = sprintf($email['part'], $user);
242 * Informs the user that they have been made the assignee of the bug.
244 * @param integer User ID
246 private function _noticeNowAssigned($userid)
248 if ($this->users
["$userid"]['options'][0] & bugdar::$emailOptions['notifications']['assignedto'] && in_array($userid, $this->roles['-notapplicable-']))
250 $user = construct_user_display(bugdar::$userinfo, false);
252 $email = get_email_text('notice_assigned');
253 $this->notices["$userid"][] = sprintf($email['part'], $user);
258 * Sends a message to inform users that the status has changed.
260 * @param integer Old status
261 * @param integer New status
263 private function _noticeStatusChange($old, $new)
265 $userlist = $this->_fetchUsersWithOnBit('statusresolve');
267 $old = bugdar
::$datastore['status'][$old]['status'];
268 $new = bugdar
::$datastore['status'][$new]['status'];
270 foreach ($userlist as $userid => $user)
272 $email = get_email_text('notice_status');
273 $this->notices
["$user[userid]"][] = sprintf($email['part'], $new, $old);
278 * Sends an email to inform users that the resolution has changed.
280 * @param integer Old resolution
281 * @param integer New resolution
283 private function _noticeResolutionChange($old, $new)
285 $userlist = $this->_fetchUsersWithOnBit('statusresolve');
287 $old = bugdar
::$datastore['resolution'][$old]['resolution'];
288 $new = bugdar
::$datastore['resolution'][$new]['resolution'];
290 foreach ($userlist as $userid => $user)
292 $email = get_email_text('notice_resolution');
293 $this->notices
["$user[userid]"][] = sprintf($email['part'], $new, $old);
298 * Informs users that the duplicates list has changed.
300 * @param string Old duplicates list
301 * @param string New duplicates list
303 private function _noticeDuplicatesChange($old, $new)
305 $userlist = $this->_fetchUsersWithOnBit('duplicates');
307 foreach ($userlist as $userid => $user)
309 $email = get_email_text('notice_duplicates');
310 $this->notices
["$user[userid]"][] = sprintf($email['part'], $old, $new);
315 * Sends an email to inform users that the severity has changed.
317 * @param integer Old severity
318 * @param integer New severity
320 private function _noticeSeverityChange($old, $new)
322 $userlist = $this->_fetchUsersWithOnBit('otherfield');
324 $old = bugdar
::$datastore['severity'][$old]['severity'];
325 $new = bugdar
::$datastore['severity'][$new]['severity'];
327 foreach ($userlist as $userid => $user)
329 $this->notices
["$user[userid]"][] = sprintf($email['part'], $old, $new);
334 * Informs users that the priority changed.
336 * @param integer Old priority
337 * @param integer New priority
339 private function _noticePriorityChange($old, $new)
341 $userlist = $this->_fetchUsersWithOnBit('otherfield');
343 $old = bugdar
::$datastore['priority'][$old]['priority'];
344 $new = bugdar
::$datastore['priority'][$new]['priority'];
346 foreach ($userlist as $userid => $user)
348 $email = get_email_text('notice_priority');
349 $this->notices
["$user[userid]"][] = sprintf($email['part'], $old, $new);
354 * Sends an email telling users that the product, component, or version
355 * has changed. This is done all at once because you really need to see
356 * the whole thing in the notice.
358 * @param array Original PCV
359 * @param array Modified PCV
361 private function _noticePCVChange($old, $new)
363 $userlist = $this->_fetchUsersWithOnBit('otherfield');
365 $products = &bugdar
::$datastore['product'];
366 $versions = &bugdar
::$datastore['version'];
368 $old = $products[$old[0]]['title'] . '/' . ($old[1] ? $products[$old[1]]['title'] . '/' : '') . $versions[$old[2]]['version'];
369 $new = $products[$new[0]]['title'] . '/' . ($new[1] ? $products[$new[1]]['title'] . '/' : '') . $versions[$new[2]]['version'];
371 foreach ($userlist as $userid => $user)
373 $email = get_email_text('notice_product');
374 $this->notices
["$user[userid]"][] = sprintf($email['part'], $old, $new);
379 * Sends the appropriate users information about a new comment being
380 * posted to the bug report.
382 * @param array CommentAPI->values array
384 public function sendNewCommentNotice($comment)
386 $userlist = $this->_fetchUsersWithOnBit('newcomment');
387 foreach ($userlist as $userid => $user)
389 $user = construct_user_display(bugdar
::$userinfo, false);
390 $date = BSApp
::$date->format(bugdar
::$options['dateformat'], $comment['dateline']);
392 $email = get_email_text('notice_comment');
393 $this->notices
["$userid"][] = sprintf($email['part'], $user, $date, $comment['comment']);
398 * A notice for an individual field changing.
400 * @param string Field name
401 * @param mixed Original value
402 * @param mixed Modified value
404 private function _noticeOtherChange($name, $old, $new)
406 $userlist = $this->_fetchUsersWithOnBit('otherfield');
407 foreach ($userlist as $userid => $user)
409 $email = get_email_text('notice_other');
410 $this->notices["$user[userid
]"][] = sprintf($email['part'], $name, $old, $new);
415 * Sends appropriate users a notice when a new attachment has been
418 * @param array AttachmentAPI->values array
419 * @param array List of all attachments made obsolete
420 * @param array Newly-inserted attachment ID
422 public function sendNewAttachmentNotice($attachment, $obsolete, $id)
424 $userlist = $this->_fetchUsersWithOnBit('newattachment');
425 foreach ($userlist as $userid => $user)
427 $user = construct_user_display(bugdar::$userinfo, false);
428 $obsoletes = implode(', ', (array)$obsolete);
430 $email = get_email_text('notice_attachment');
431 $this->notices["$userid"][] = sprintf($email['part'], $user, $attachment['filename'], $attachment['description'], $attachment['filesize'], $obsoletes, bugdar
::$options['trackerurl'], $attachment['attachmentid']);
436 * Sends a new bug notification notice to all those who have the option
437 * turned no. This does not use fetchUsersWithOnBit() because a
438 * query is more effective.
440 * @param array Bug values array
441 * @param array Comment values array
443 public function sendNewBugNotice($bug, $comment)
445 $userinfo = BSApp
::$db->query("
446 SELECT user.*, useremail.*
447 FROM " . TABLE_PREFIX
. "useremail AS useremail
448 LEFT JOIN " . TABLE_PREFIX
. "user AS user
449 ON (user.userid = useremail.userid)
450 WHERE useremail.relation = 0
451 AND useremail.mask & " . bugdar
::$emailOptions['notifications']['newbug'] . "
453 foreach ($userinfo as $userInfo)
455 if (!is_array($this->users
["$userInfo[userid]"]))
457 $user = construct_user_display(bugdar
::$userinfo, false);
458 $this->users
["$userInfo[userid]"] = $userInfo;
459 $product = bugdar
::$datastore['product']["$bug[product]"]['title'] . '/' . ($bug['component'] ? bugdar
::$datastore['product']["$bug[component]"]['title'] . '/' : '') . bugdar
::$datastore['version']["$bug[version]"]['version'];
461 $email = get_email_text('notice_new_bug');
462 $this->notices
["$userInfo[userid]"][] = sprintf($email['part'], $bug['bugid'], $bug['summary'], $user, $product, $comment['comment']);
463 unset($this->users
["$userInfo[userid]"]['mask'], $this->users
["$userInfo[userid]"]['relation']);
465 $this->users
["$userInfo[userid]"]['options']["$userInfo[relation]"] = $userInfo['mask'];
470 * Generates an array of users who have a given email notification flag
471 * turned on in their bitfields.
473 * @param string Notification bitfield name
475 * @return array Array of users and their data
477 private function _fetchUsersWithOnBit($bitname)
481 foreach ($this->users
as $user)
483 foreach (bugdar
::$emailOptions['relations'] as $name => $bit)
485 if (in_array($user['userid'], $this->roles
["$name"]) && $user['options']["$bit"] & bugdar
::$emailOptions['notifications']["$bitname"])
487 $idlist[] = $user['userid'];
492 $masters = array_unique($idlist);
495 foreach ($masters as $userid)
497 $return["$userid"] = &$this->users
["$userid"];
504 * Compiles and sends the actual emails to users.
506 public function finalize()
508 // get the current bug for permissions checks
509 $bug = BSApp::$db->queryFirst("SELECT
* FROM
" . TABLE_PREFIX . "bug WHERE bugid
= " . $this->bug['bugid']);
510 foreach ($this->notices as $userid => $noticelist)
512 if ($userid == bugdar::$userinfo['userid'])
514 BSApp::debug("skipping user
$userid because they
're the one doing the thing");
518 // we wouldn't want people who favorite bugs getting hidden notices
519 if (!check_bug_permissions($bug, $this->users
["$userid"]))
521 BSApp::debug("skipping user
$userid ({$this
->users
[$userid
]['email']}) because of permissions
");
525 $parts = implode("\n\n
", $noticelist);
527 $email = get_email_text('bug_notification');
529 $body = sprintf($email['bodyText'], $this->users[$userid]['displayname'], bugdar::$options['trackertitle'], $this->bug['summary'], $this->bug['bugid'], bugdar::$options['trackerurl'], $parts);
531 $mail = new BSMail();
532 $mail->setSubject(sprintf($email['subject'], bugdar::$options['trackertitle'], $this->bug['summary']));
533 $mail->setBodyText($body);
534 $mail->setFromAddress(MAIL_FROM_ADDRESS);
535 $mail->setFromName(MAIL_FROM_NAME);
537 if (!empty($this->users["$userid"]['email']))
539 $mail->send($this->users
[$userid]['email'], $this->users
[$userid]['displayname']);
543 BSApp
::debug("not sending an email to " . $userid . " because they don't have one?");
549 * Returns the locale name from a given user ID
551 * @param integer User ID
553 * @return string Locale
555 private function _localeFromUserId($userid)
557 $langcode = bugdar
::$datastore['language'][$this->users
[$userid]['languageid']]['langcode'];
560 $langcode = bugdar
::$datastore['language'][bugdar
::$options['defaultlanguage']]['langcode'];
566 /*=====================================================================*\
567 || ###################################################################
570 || ###################################################################
571 \*=====================================================================*/