2 /*=====================================================================*\
3 || ###################################################################
4 || # Bugdar [#]version[#]
5 || # Copyright ©2002-[#]year[#] 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 [#]gpl[#] 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 - [#]year[#], Blue Static
34 class NotificationCenter
48 var $original = array();
55 var $modified = array();
58 * Global bugsys registry
65 * Role list: a list of user IDs with their relations to the bug
70 '-notapplicable-' => array(),
71 'reporter' => array(),
72 'assignee' => array(),
73 'favorite' => array(),
75 'commenter' => array()
86 * A list of notices per-user that are combined together in NotificationCenter::finalize()
90 var $notices = array();
92 // ###################################################################
94 * Constructor: set database objects
98 function __construct()
102 $this->registry
=& $bugsys;
105 // ###################################################################
107 * (PHP 4) Constructor
111 function NotificationCenter()
113 $this->__construct();
116 // ###################################################################
118 * Sets the bug data so that all methods in this class have access to
119 * it when sending emails.
123 * @param array Original bug data
124 * @param array Modified bug data
126 function set_bug_data($original, $modified = array())
128 if (sizeof($modified) > 0)
130 $this->bug
= $modified;
134 $this->bug
= $original;
137 $this->original
= $original;
138 $this->modified
= $modified;
140 $this->roles
['-notapplicable-'] = (sizeof($modified) > 0 ?
array($original['assignedto'], $modified['assignedto']) : array($original['assignedto']));
141 $this->roles
['reporter'] = array($original['userid']);
142 $this->roles
['assignee'] = (sizeof($modified) > 0 ?
array($modified['assignedto']) : array($original['assignedto']));
144 $this->fetch_user_cache();
147 // ###################################################################
149 * Fetches all the users who could be related to the bug and sticks
150 * their information into an array.
154 function fetch_user_cache()
156 // reset all the data each time we do this, just in case it changes within the lifespan of the object
157 $this->users
= array();
158 foreach ($this->roles
AS $role => $users)
160 $this->roles
["$role"] = array();
163 $newbuggers = $this->registry
->db
->query("SELECT userid FROM " . TABLE_PREFIX
. "useremail WHERE relation = " . $this->registry
->emailoptions
['relations']['-notapplicable-'] . " AND mask & " . $this->registry
->emailoptions
['notifications']['newbug']);
164 while ($newbug = $this->registry
->db
->fetch_array($newbuggers))
166 $this->roles
['-notapplicable-']["$newbug[userid]"] = $newbug['userid'];
169 $favorites = $this->registry
->db
->query("SELECT userid FROM " . TABLE_PREFIX
. "favorite WHERE bugid = " . $this->registry
->clean($this->bug
['bugid'], TYPE_UINT
));
170 while ($fav = $this->registry
->db
->fetch_array($favorites))
172 $this->roles
['favorite']["$fav[userid]"] = $fav['userid'];
175 $voters = $this->registry
->db
->query_first("SELECT userids FROM " . TABLE_PREFIX
. "vote WHERE bugid = " . $this->registry
->clean($this->bug
['bugid'], TYPE_UINT
));
176 $this->roles
['voter'] = preg_split('#,#', $voters['userids'], 0, PREG_SPLIT_NO_EMPTY
);
178 $commenters = $this->registry
->db
->query("SELECT userid FROM " . TABLE_PREFIX
. "comment WHERE bugid = " . $this->registry
->clean($this->bug
['bugid'], TYPE_UINT
));
179 while ($comment = $this->registry
->db
->fetch_array($commenters))
181 $this->roles
['commenter']["$comment[userid]"] = $comment['userid'];
184 $masterids = array_merge($this->roles
['-notapplicable-'], $this->roles
['reporter'], $this->roles
['assignee'], $this->roles
['favorite'], $this->roles
['voter'], $this->roles
['commenter']);
185 $masterids = $this->registry
->funct
->array_strip_empty(array_unique($masterids));
187 if (is_array($masterids) AND sizeof($masterids) > 0)
189 $userinfo = $this->registry
->db
->query("
190 SELECT user.*, useremail.*
191 FROM " . TABLE_PREFIX
. "useremail AS useremail
192 LEFT JOIN " . TABLE_PREFIX
. "user AS user
193 ON (user.userid = useremail.userid)
194 WHERE useremail.userid IN (" . implode(',', $masterids) . ")
196 while ($user = $this->registry
->db
->fetch_array($userinfo))
198 if (!is_array($this->users
["$user[userid]"]))
200 $this->users
["$user[userid]"] = $user;
201 unset($this->users
["$user[userid]"]['mask'], $this->users
["$user[userid]"]['relation']);
203 $this->users
["$user[userid]"]['options']["$user[relation]"] = $user['mask'];
208 // ###################################################################
210 * Sends the appropriate emails for changes to bugs. This function
211 * works a lot like the Logging class by taking BugAPI->objdata and
212 * BugAPI->values and then comparing the two arries and sending emails
213 * with the differences.
217 function send_bug_changes_notice()
219 if (!isset($this->modified
['bugid']))
224 // fields with custom mask information
225 if ($this->original
['assignedto'] != $this->modified
['assignedto'])
227 if ($this->original
['assignedto'] != '')
229 $this->notice_no_longer_assigned($this->original
['assignedto']);
231 if ($this->modified
['assignedto'] != '')
233 $this->notice_now_assigned($this->modified
['assignedto']);
236 if ($this->original
['status'] != $this->modified
['status'])
238 $this->notice_status_change($this->original
['status'], $this->modified
['status']);
240 if ($this->original
['resolution'] != $this->modified
['resolution'])
242 $this->notice_resolution_change($this->original
['resolution'], $this->modified
['resolution']);
244 if ($this->original
['duplicates'] != $this->modified
['duplicates'])
246 $this->notice_duplicates_change($this->original
['duplicates'], $this->modified
['duplicates']);
249 // other standard fields that don't have custom masks
250 if ($this->original
['severity'] != $this->modified
['severity'])
252 $this->notice_severity_change($this->original
['severity'], $this->modified
['severity']);
254 if ($this->original
['priority'] != $this->modified
['priority'])
256 $this->notice_priority_change($this->original
['priority'], $this->modified
['priority']);
258 if (($this->original
['product'] != $this->modified
['product']) OR ($this->original
['component'] != $this->modified
['component']) OR ($this->original
['version'] != $this->modified
['version']))
260 $this->notice_pcv_change(array($this->original
['product'], $this->original
['component'], $this->original
['version']), array($this->modified
['product'], $this->modified
['component'], $this->modified
['version']));
268 foreach ($dofields AS $field => $lookup)
270 if ($this->original
["$field"] != $this->modified
["$field"])
272 $this->notice_other_change($field, $this->original
["$field"], $this->modified
["$field"]);
277 // ###################################################################
279 * Sends an email to the specified user ID that they are no longer the
280 * person assigned to the bug.
284 * @param integer User ID to send to
286 function notice_no_longer_assigned($userid)
288 if ($this->users
["$userid"]['options'][0] & $this->registry
->emailoptions
['notifications']['assignedto'] AND in_array($userid, $this->roles
['-notapplicable-']))
290 $user = construct_user_display($this->registry
->userinfo
, false
);
291 $this->notices
["$userid"][] = eval('$part = "' . $this->registry
->template
->fetch('email/notice_unassigned.part') . '";');
295 // ###################################################################
297 * Informs the user that they have been made the assignee of the bug.
301 * @param integer User ID
303 function notice_now_assigned($userid)
305 if ($this->users
["$userid"]['options'][0] & $this->registry
->emailoptions
['notifications']['assignedto'] AND in_array($userid, $this->roles
['-notapplicable-']))
307 $user = construct_user_display($this->registry
->userinfo
, false
);
308 $this->notices
["$userid"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_assigned.part') . '";');
312 // ###################################################################
314 * Sends a message to inform users that the status has changed.
318 * @param integer Old status
319 * @param integer New status
321 function notice_status_change($old, $new)
323 $userlist = $this->fetch_users_with_on_bit('statusresolve');
324 foreach ($userlist AS $userid => $user)
326 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_status.part') . '";');
330 // ###################################################################
332 * Sends an email to inform users that the resolution has changed.
336 * @param integer Old resolution
337 * @param integer New resolution
339 function notice_resolution_change($old, $new)
341 $userlist = $this->fetch_users_with_on_bit('statusresolve');
342 foreach ($userlist AS $userid => $user)
344 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_resolution.part') . '";');
348 // ###################################################################
350 * Informs users that the duplicates list has changed.
354 * @param string Old duplicates list
355 * @param string New duplicates list
357 function notice_duplicates_change($old, $new)
359 $userlist = $this->fetch_useres_with_on_bit('duplicates');
360 foreach ($userlist AS $userid => $user)
362 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_duplicates.part') . '";');
366 // ###################################################################
368 * Sends an email to inform users that the severity has changed.
372 * @param integer Old severity
373 * @param integer New severity
375 function notice_severity_change($old, $new)
377 $userlist = $this->fetch_users_with_on_bit('otherfield');
378 foreach ($userlist AS $userid => $user)
380 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_severity.part') . '";');
384 // ###################################################################
386 * Informs users that the priority changed.
390 * @param integer Old priority
391 * @param integer New priority
393 function notice_priority_change($old, $new)
395 $userlist = $this->fetch_users_with_on_bit('otherfield');
396 foreach ($userlist AS $userid => $user)
398 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_priority.part') . '";');
402 // ###################################################################
404 * Sends an email telling users that the product, component, or version
405 * has changed. This is done all at once because you really need to see
406 * the whole thing in the notice.
410 * @param array Original PCV
411 * @param array Modified PCV
413 function notice_pcv_change($old, $new)
415 $userlist = $this->fetch_users_with_on_bit('otherfield');
417 $old = $this->registry
->datastore
['product']["$old[0]"]['title'] . '/' . ($old[1] ?
$this->registry
->datastore
['product']["$old[1]"]['title'] . '/' : '') . $this->registry
->datastore
['version']["$old[2]"]['version'];
418 $new = $this->registry
->datastore
['product']["$new[0]"]['title'] . '/' . ($new[1] ?
$this->registry
->datastore
['product']["$new[1]"]['title'] . '/' : '') . $this->registry
->datastore
['version']["$new[2]"]['version'];
420 foreach ($userlist AS $userid => $user)
422 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_product.part') . '";');
426 // ###################################################################
428 * Sends the appropriate users information about a new comment being
429 * posted to the bug report.
433 * @param array CommentAPI->values array
435 function send_new_comment_notice($comment)
437 $userlist = $this->fetch_users_with_on_bit('newcomment');
438 foreach ($userlist AS $userid => $user)
440 $user = construct_user_display($this->registry
->userinfo
, false
);
441 $date = $this->registry
->modules
['date']->format($this->registry
->options
['dateformat'], $comment['dateline']);
443 $this->notices
["$userid"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_comment.part') . '";');
447 // ###################################################################
449 * A notice for an individual field changing.
453 * @param string Field name
454 * @param mixed Original value
455 * @param mixed Modified value
457 function notice_other_change($name, $old, $new)
459 $userlist = $this->fetch_users_with_on_bit('otherfield');
460 foreach ($userlist AS $userid => $user)
462 $this->notices
["$user[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_other.part') . '";');
466 // ###################################################################
468 * Sends appropriate users a notice when a new attachment has been
473 * @param array AttachmentAPI->values array
474 * @param array List of all attachments made obsolete
475 * @param array Newly-inserted attachment ID
477 function send_new_attachment_notice($attachment, $obsolete, $id)
479 $userlist = $this->fetch_users_with_on_bit('newattachment');
480 foreach ($userlist AS $userid => $user)
482 $user = construct_user_display($this->registry
->userinfo
, false
);
483 $obsoletes = implode(', ', (array)$obsolete);
485 $this->notices
["$userid"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_attachment.part') . '";');
489 // ###################################################################
491 * Sends a new bug notification notice to all those who have the option
492 * turned no. This does not use fetch_users_with_on_bit() because a
493 * query is more effective.
497 * @param array Bug values array
498 * @param array Comment values array
500 function send_new_bug_notice($bug, $comment)
502 $userinfo = $this->registry
->db
->query("
503 SELECT user.*, useremail.*
504 FROM " . TABLE_PREFIX
. "useremail AS useremail
505 LEFT JOIN " . TABLE_PREFIX
. "user AS user
506 ON (user.userid = useremail.userid)
507 WHERE useremail.relation = 0
508 AND useremail.mask & " . $this->registry
->emailoptions
['notifications']['newbug'] . "
510 while ($userInfo = $this->registry
->db
->fetch_array($userinfo))
512 if (!is_array($this->users
["$userInfo[userid]"]))
514 $user = construct_user_display($this->registry
->userinfo
, false
);
515 $product = $this->registry
->datastore
['product']["$bug[product]"]['title'] . '/' . ($bug['component'] ?
$this->registry
->datastore
['product']["$bug[component]"]['title'] . '/' : '') . $this->registry
->datastore
['version']["$bug[version]"]['version'];
516 $this->notices
["$userInfo[userid]"][] = eval('$email = "' . $this->registry
->template
->fetch('email/notice_new_bug.part') . '";');
517 $this->users
["$userInfo[userid]"] = $userInfo;
518 unset($this->users
["$userInfo[userid]"]['mask'], $this->users
["$userInfo[userid]"]['relation']);
520 $this->users
["$userInfo[userid]"]['options']["$userInfo[relation]"] = $userInfo['mask'];
524 // ###################################################################
526 * Generates an array of users who have a given email notification flag
527 * turned on in their bitfields.
531 * @param string Notification bitfield name
533 * @return array Array of users and their data
535 function fetch_users_with_on_bit($bitname)
539 foreach ($this->users
AS $user)
541 foreach ($this->registry
->emailoptions
['relations'] AS $name => $bit)
543 if (in_array($user['userid'], $this->roles
["$name"]) AND $user['options']["$bit"] & $this->registry
->emailoptions
['notifications']["$bitname"])
545 $idlist[] = $user['userid'];
550 $masters = array_unique($idlist);
553 foreach ($masters AS $userid)
555 $return["$userid"] =& $this->users
["$userid"];
561 // ###################################################################
563 * Compiles and sends the actual emails to users.
569 // get the current bug for permissions checks
570 $bug = $this->registry
->db
->query_first("SELECT * FROM " . TABLE_PREFIX
. "bug WHERE bugid = " . $this->bug
['bugid']);
571 foreach ($this->notices
AS $userid => $noticelist)
573 if ($userid == $this->registry
->userinfo
['userid'])
575 $this->registry
->debug("skipping user $userid because they're the one doing the thing");
579 // we wouldn't want people who favorite bugs getting hidden notices
580 if (!check_bug_permissions($bug, $this->users
["$userid"]))
582 $this->registry
->debug("skipping user $userid ({$this->users[$userid]['email']}) because of permissions");
586 $parts = implode("\n\n", $noticelist);
588 eval('$email = "' . $this->registry
->template
->fetch('email/bugnotification.xml') . '";');
589 $email = $this->registry
->xml
->parse($email);
590 $this->registry
->mail
->setSubject($email['email']['subject']['value']);
591 $this->registry
->mail
->setBodyText($email['email']['bodyText']['value']);
593 if (!empty($this->users
["$userid"]['email']))
595 $this->registry
->mail
->send($this->users
["$userid"]['email'], $this->users
["$userid"]['displayname']);
599 $this->registry
->debug("not sending an email to " . $userid . " because they don't have one?");
605 /*=====================================================================*\
606 || ###################################################################
609 || ###################################################################
610 \*=====================================================================*/