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 * @param array Original custom fields data
218 * @param array Modified custom fields data
220 function send_bug_changes_notice($original, $modified)
222 if (!isset($this->modified['bugid']))
227 // fields with custom mask information
228 if ($this->original['assignedto'] != $this->modified['assignedto'])
230 if ($this->original['assignedto'] != '')
232 $this->notice_no_longer_assigned($this->original['assignedto']);
234 if ($this->modified['assignedto'] != '')
236 $this->notice_now_assigned($this->modified['assignedto']);
239 if ($this->original['status'] != $this->modified['status'])
241 $this->notice_status_change($this->original['status'], $this->modified['status']);
243 if ($this->original['resolution'] != $this->modified['resolution'])
245 $this->notice_resolution_change($this->original['resolution'], $this->modified['resolution']);
247 if ($this->original['duplicates'] != $this->modified['duplicates'])
249 $this->notice_duplicates_change($this->original['duplicates'], $this->modified['duplicates']);
252 // other standard fields that don't have custom masks
253 if ($this->original['severity'] != $this->modified['severity'])
255 $this->notice_severity_change($this->original['severity'], $this->modified['severity']);
257 if ($this->original['priority'] != $this->modified['priority'])
259 $this->notice_priority_change($this->original['priority'], $this->modified['priority']);
261 if (($this->original['product'] != $this->modified['product']) OR ($this->original['component'] != $this->modified['component']) OR ($this->original['version'] != $this->modified['version']))
263 $this->notice_pcv_change(array($this->original['product'], $this->original['component'], $this->original['version']), array($this->modified['product'], $this->modified['component'], $this->modified['version']));
271 foreach ($dofields AS $field => $lookup)
273 if ($this->original["$field"] != $this->modified
["$field"])
275 $this->notice_other_change($field, $this->original["$field"], $this->modified
["$field"]);
280 foreach ($modified AS $field => $value)
282 if ($field == 'bugid')
286 if ($original["$field"] != $modified["$field"])
288 $this->notice_other_change($field, $original["$field"], $modified["$field"]);
293 // ###################################################################
295 * Sends an email to the specified user ID that they are no longer the
296 * person assigned to the bug.
300 * @param integer User ID to send to
302 function notice_no_longer_assigned($userid)
304 if ($this->users["$userid"]['options'][0] & $this->registry
->emailoptions
['notifications']['assignedto'] AND in_array($userid, $this->roles
['-notapplicable-']))
306 $this->notices
["$userid"][] = sprintf(
307 _('You are no longer assigned to this bug, per %1$s\'s changes.'),
309 construct_user_display($this->registry->userinfo, false)
314 // ###################################################################
316 * Informs the user that they have been made the assignee of the bug.
320 * @param integer User ID
322 function notice_now_assigned($userid)
324 if ($this->users["$userid"]['options'][0] & $this->registry
->emailoptions
['notifications']['assignedto'] AND in_array($userid, $this->roles
['-notapplicable-']))
326 $this->notices
["$userid"][] = sprintf(
327 _('You have been assigned to this bug by %1$s.'),
329 construct_user_display($this->registry->userinfo, false)
334 // ###################################################################
336 * Sends a message to inform users that the status has changed.
340 * @param integer Old status
341 * @param integer New status
343 function notice_status_change($old, $new)
345 $userlist = $this->fetch_users_with_on_bit('statusresolve');
346 foreach ($userlist AS $userid => $user)
348 $this->notices["$user[userid
]"][] = sprintf(
349 _('The status of the bug is now "%
2$s", from
"%1$s".'),
351 $this->registry->datastore['status']["$old"]['status'],
352 $this->registry
->datastore
['status']["$new"]['status']
357 // ###################################################################
359 * Sends an email to inform users that the resolution has changed.
363 * @param integer Old resolution
364 * @param integer New resolution
366 function notice_resolution_change($old, $new)
368 $userlist = $this->fetch_users_with_on_bit('statusresolve');
369 foreach ($userlist AS $userid => $user)
371 $this->notices["$user[userid
]"][] = sprintf(
372 _('This bug has been resolved with resolution "%
2$s", from
"%1$s".'),
374 $this->registry->datastore['resolution']["$old"]['resolution'],
375 $this->registry
->datastore
['resolution']["$new"]['resolution']
380 // ###################################################################
382 * Informs users that the duplicates list has changed.
386 * @param string Old duplicates list
387 * @param string New duplicates list
389 function notice_duplicates_change($old, $new)
391 $userlist = $this->fetch_useres_with_on_bit('duplicates');
392 foreach ($userlist AS $userid => $user)
394 $this->notices["$user[userid
]"][] = sprintf(
395 _('The duplicates list has changed from "%
1$s" to %
2$s".'),
403 // ###################################################################
405 * Sends an email to inform users that the severity has changed.
409 * @param integer Old severity
410 * @param integer New severity
412 function notice_severity_change($old, $new)
414 $userlist = $this->fetch_users_with_on_bit('otherfield
');
415 foreach ($userlist AS $userid => $user)
417 $this->notices["$user[userid]"][] = sprintf(
418 _('The severity has been elevated from
"%1$s" to "%
2$s".'),
420 $this->registry->datastore['severity
']["$old"]['severity
'],
421 $this->registry->datastore['severity
']["$new"]['severity
']
426 // ###################################################################
428 * Informs users that the priority changed.
432 * @param integer Old priority
433 * @param integer New priority
435 function notice_priority_change($old, $new)
437 $userlist = $this->fetch_users_with_on_bit('otherfield
');
438 foreach ($userlist AS $userid => $user)
440 $this->notices["$user[userid]"][] = sprintf(
441 _('The priority has been elevatd from
"%1$s" to "%
2$s".'),
443 $this->registry->datastore['priority
']["$old"]['priority
'],
444 $this->registry->datastore['priority
']["$new"]['priority
']
449 // ###################################################################
451 * Sends an email telling users that the product, component, or version
452 * has changed. This is done all at once because you really need to see
453 * the whole thing in the notice.
457 * @param array Original PCV
458 * @param array Modified PCV
460 function notice_pcv_change($old, $new)
462 $userlist = $this->fetch_users_with_on_bit('otherfield
');
463 foreach ($userlist AS $userid => $user)
465 $this->notices["$user[userid]"][] = sprintf(
466 _('The product
, component
, and version combination has changed from
"%1$s" to "%
2$s".'),
468 $this->registry->datastore['product
']["$old[0]"]['title
'] . '/' . ($old[1] ? $this->registry->datastore['product
']["$old[1]"]['title
'] . '/' : '') . $this->registry->datastore['version
']["$old[2]"]['version
'],
469 $this->registry->datastore['product
']["$new[0]"]['title
'] . '/' . ($new[1] ? $this->registry->datastore['product
']["$new[1]"]['title
'] . '/' : '') . $this->registry->datastore['version
']["$new[2]"]['version
']
474 // ###################################################################
476 * Sends the appropriate users information about a new comment being
477 * posted to the bug report.
481 * @param array CommentAPI->values array
483 function send_new_comment_notice($comment)
485 $userlist = $this->fetch_users_with_on_bit('newcomment
');
486 foreach ($userlist AS $userid => $user)
488 $this->notices["$user[userid]"][] = sprintf(
489 _('The following comment was added by %
1$s on %
2$s:
490 ============================================
492 ============================================'),
494 construct_user_display($this->registry->userinfo, false),
495 $this->registry->modules['date
']->format($this->registry->options['dateformat
'], $comment['dateline
']),
501 // ###################################################################
503 * A notice for an individual field changing.
507 * @param string Field name
508 * @param mixed Original value
509 * @param mixed Modified value
511 function notice_other_change($name, $old, $new)
513 $userlist = $this->fetch_users_with_on_bit('otherfield
');
514 foreach ($userlist AS $userid => $user)
516 $this->notices["$user[userid]"][] = sprintf(
517 _('The %
1$s field changed from
"%2$s" to "%
3$s".'),
526 // ###################################################################
528 * Sends appropriate users a notice when a new attachment has been
533 * @param array AttachmentAPI->values array
534 * @param array List of all attachments made obsolete
535 * @param array Newly-inserted attachment ID
537 function send_new_attachment_notice($attachment, $obsolete, $id)
539 $userlist = $this->fetch_users_with_on_bit('newattachment
');
540 foreach ($userlist AS $userid => $user)
542 $this->notices["$userid"][] = sprintf(
543 _('%
1$s has uploaded a
new attachment
:
544 ============================================
547 File size
: %
4$s Bytes
550 ============================================'),
552 construct_user_display($this->registry->userinfo, false),
553 $attachment['filename
'],
554 $attachment['description
'],
555 $attachment['filesize
'],
556 implode(', ', (array)$obsolete),
557 $this->registry->options['trackerurl
'] . '/viewattachment
.php
?attachmentid
=' . $id
562 // ###################################################################
564 * Sends a new bug notification notice to all those who have the option
565 * turned no. This does not use fetch_users_with_on_bit() because a
566 * query is more effective.
570 * @param array Bug values array
571 * @param array Comment values array
573 function send_new_bug_notice($bug, $comment)
575 $userinfo = $this->registry->db->query("
576 SELECT user.*, useremail.*
577 FROM " . TABLE_PREFIX . "useremail AS useremail
578 LEFT JOIN " . TABLE_PREFIX . "user AS user
579 ON (user.userid = useremail.userid)
580 WHERE useremail.relation = 0
581 AND useremail.mask & " . $this->registry->emailoptions['notifications
']['newbug
'] . "
583 while ($user = $this->registry->db->fetch_array($userinfo))
585 if (!is_array($this->users["$user[userid]"]))
587 $this->notices["$user[userid]"][] = sprintf(
589 This bug has been added to the database
:
590 ============================================
594 Product
/Component
/Version
: %
4$s
596 --------------------------------------------
598 --------------------------------------------
599 ============================================'),
602 construct_user_display($this->registry->userinfo, false),
603 $this->registry->datastore['product
']["$bug[product]"]['title
'] . '/' . ($bug['component
'] ? $this->registry->datastore['product
']["$bug[component]"]['title
'] . '/' : '') . $this->registry->datastore['version
']["$bug[version]"]['version
'],
606 $this->users["$user[userid]"] = $user;
607 unset($this->users["$user[userid]"]['mask
'], $this->users["$user[userid]"]['relation
']);
609 $this->users["$user[userid]"]['options
']["$user[relation]"] = $user['mask
'];
613 // ###################################################################
615 * Generates an array of users who have a given email notification flag
616 * turned on in their bitfields.
620 * @param string Notification bitfield name
622 * @return array Array of users and their data
624 function fetch_users_with_on_bit($bitname)
628 foreach ($this->users AS $user)
630 foreach ($this->registry->emailoptions['relations
'] AS $name => $bit)
632 if (in_array($user['userid
'], $this->roles["$name"]) AND $user['options
']["$bit"] & $this->registry->emailoptions['notifications
']["$bitname"])
634 $idlist[] = $user['userid
'];
639 $masters = array_unique($idlist);
642 foreach ($masters AS $userid)
644 $return["$userid"] =& $this->users["$userid"];
650 // ###################################################################
652 * Compiles and sends the actual emails to users.
658 // get the current bug for permissions checks
659 $bug = $this->registry->db->query_first("SELECT * FROM " . TABLE_PREFIX . "bug WHERE bugid = " . $this->bug['bugid
']);
660 $this->registry->mail->setSubject(sprintf(_('%
1$s Bug Notification
- %
2$s'), $this->registry
->options
['trackertitle'], $this->bug
['summary']));
661 foreach ($this->notices
AS $userid => $noticelist)
663 if ($userid == $this->registry
->userinfo
['userid'])
668 // we wouldn't want people who favorite bugs getting hidden notices
669 if (!check_bug_permissions($bug, $this->users
["$userid"]))
671 $this->registry->debug("skipping user
$userid ({$this
->users
[$userid
]['email']}) because of permissions
");
675 $this->registry->mail->setBodyText(sprintf(_('Hi %1$s,
677 You are receiving this email because you have opted to get notifications for the %2$s bug tracker.
679 The bug is "%
5$s" (id
: %
6$s) located at %
4$s/showreport
.php
?bugid
=%
6$s
681 Here are the notices
:
682 ###################################################################
686 ###################################################################
687 If you no longer want to receive email from us
, please log into your account
and click the
"My Controls" tab at the top of the screen to change email preferences
.
690 $this->users
["$userid"]['displayname'],
691 $this->registry->options['trackertitle'],
692 implode("\n\n
", $noticelist),
693 $this->registry->options['trackerurl'],
694 $this->bug['summary'],
697 if (!empty($this->users["$userid"]['email']))
699 $this->registry
->mail
->send($this->users
["$userid"]['email'], $this->users["$userid"]['displayname']);
703 $this->registry
->debug("not sending an email to " . $userid . " because they don't have one?");
709 /*=====================================================================*\
710 || ###################################################################
713 || ###################################################################
714 \*=====================================================================*/