r865: Finished notifications for new attachments
[bugdar.git] / includes / class_notification.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # Bugdar [#]version[#]
5 || # Copyright ©2002-[#]year[#] Iris Studios, Inc.
6 || #
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.
10 || #
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
14 || # more details.
15 || #
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 \*=====================================================================*/
21
22 /**
23 * Notification Center
24 *
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.
27 *
28 * @author Iris Studios, Inc.
29 * @copyright Copyright ©2002 - [#]year[#], Iris Studios, Inc.
30 * @version $Revision$
31 * @package Bugdar
32 *
33 */
34 class NotificationCenter
35 {
36 /**
37 * Bug information
38 * @var array
39 * @access private
40 */
41 var $bug = array();
42
43 /**
44 * Original bug data
45 * @var array
46 * @access private
47 */
48 var $original = array();
49
50 /**
51 * Modified bug data
52 * @var array
53 * @access private
54 */
55 var $modified = array();
56
57 /**
58 * Global bugsys registry
59 * @var object
60 * @access private
61 */
62 var $registry = null;
63
64 /**
65 * Role list: a list of user IDs with their relations to the bug
66 * @var array
67 * @access private
68 */
69 var $roles = array(
70 '-notapplicable-' => array(),
71 'reporter' => array(),
72 'assignee' => array(),
73 'favourite' => array(),
74 'voter' => array(),
75 'commenter' => array()
76 );
77
78 /**
79 * User cache list
80 * @var array
81 * @access private
82 */
83 var $users = array();
84
85 /**
86 * A list of notices per-user that are combined together in NotificationCenter::finalize()
87 * @var array
88 * @access private
89 */
90 var $notices = array();
91
92 // ###################################################################
93 /**
94 * Constructor: set database objects
95 *
96 * @access public
97 */
98 function __construct()
99 {
100 global $bugsys;
101
102 $this->registry =& $bugsys;
103 }
104
105 // ###################################################################
106 /**
107 * (PHP 4) Constructor
108 *
109 * @access public
110 */
111 function NotificationCenter()
112 {
113 $this->__construct();
114 }
115
116 // ###################################################################
117 /**
118 * Sets the bug data so that all methods in this class have access to
119 * it when sending emails.
120 *
121 * @access public
122 *
123 * @param array Original bug data
124 * @param array Modified bug data
125 */
126 function set_bug_data($original, $modified = array())
127 {
128 if (sizeof($modified) > 0)
129 {
130 $this->bug = $modified;
131 }
132
133 $this->original = $original;
134 $this->modified = $modified;
135
136 $this->roles['-notapplicable-'] = (sizeof($modified) > 0 ? array($original['assignedto'], $modified['assignedto']) : array($original['assignedto']));
137 $this->roles['reporter'] = array($original['userid']);
138 $this->roles['assignee'] = (sizeof($modified) > 0 ? array($modified['assignedto']) : array($original['assignedto']));
139
140 $this->fetch_user_cache();
141 }
142
143 // ###################################################################
144 /**
145 * Fetches all the users who could be related to the bug and sticks
146 * their information into an array.
147 *
148 * @access private
149 */
150 function fetch_user_cache()
151 {
152 $favourites = $this->registry->db->query("SELECT userid FROM " . TABLE_PREFIX . "favourite WHERE bugid = " . $this->registry->clean($this->bug['bugid'], TYPE_UINT));
153 while ($fav = $this->registry->db->fetch_array($favourites))
154 {
155 $this->roles['favourite']["$fav[userid]"] = $fav['userid'];
156 }
157
158 $voters = $this->registry->db->query_first("SELECT userids FROM " . TABLE_PREFIX . "vote WHERE bugid = " . $this->registry->clean($this->bug['bugid'], TYPE_UINT));
159 $this->roles['voter'] = preg_split('#,#', $voters['userids'], 0, PREG_SPLIT_NO_EMPTY);
160
161 $commenters = $this->registry->db->query("SELECT userid FROM " . TABLE_PREFIX . "comment WHERE bugid = " . $this->registry->clean($this->bug['bugid'], TYPE_UINT));
162 while ($comment = $this->registry->db->fetch_array($commenters))
163 {
164 $this->roles['commenter']["$comment[userid]"] = $comment['userid'];
165 }
166
167 $masterids = array_merge($this->roles['-notapplicable-'], $this->roles['reporter'], $this->roles['assignee'], $this->roles['favourite'], $this->roles['voter'], $this->roles['commenter']);
168 $masterids = array_unique($masterids);
169
170 $userinfo = $this->registry->db->query("
171 SELECT user.*, useremail.*
172 FROM " . TABLE_PREFIX . "useremail AS useremail
173 LEFT JOIN " . TABLE_PREFIX . "user AS user
174 ON (user.userid = useremail.userid)
175 WHERE useremail.userid IN (" . implode(',', $masterids) . ")
176 ");
177 while ($user = $this->registry->db->fetch_array($userinfo))
178 {
179 if (!is_array($this->users["$user[userid]"]))
180 {
181 $this->users["$user[userid]"] = $user;
182 unset($this->users["$user[userid]"]['mask'], $this->users["$user[userid]"]['relation']);
183 }
184 $this->users["$user[userid]"]['options']["$user[relation]"] = $user['mask'];
185 }
186 }
187
188 // ###################################################################
189 /**
190 * Sends the appropriate emails for changes to bugs. This function
191 * works a lot like the Logging class by taking BugAPI->objdata and
192 * BugAPI->values and then comparing the two arries and sending emails
193 * with the differences.
194 *
195 * @access public
196 *
197 * @param array Original custom fields data
198 * @param array Modified custom fields data
199 */
200 function send_bug_changes_notice($original, $modified)
201 {
202 if (!isset($this->modified['bugid']))
203 {
204 return;
205 }
206
207 if ($this->original['assignedto'] != $this->modified['assignedto'])
208 {
209 if ($this->original['assignedto'] != '')
210 {
211 $this->notice_no_longer_assigned($this->original['assignedto']);
212 }
213 if ($this->modified['assignedto'] != '')
214 {
215 $this->notice_now_assigned($this->modified['assignedto']);
216 }
217 }
218
219 if ($this->original['status'] != $this->modified['status'])
220 {
221 $this->notice_status_change($this->original['status'], $this->modified['status']);
222 }
223 if ($this->original['resolution'] != $this->modified['resoultion'])
224 {
225 $this->notice_resolution_change($this->original['resolution'], $this->modified['resolution']);
226 }
227
228 if ($this->original['duplicates'] != $this->modified['duplicates'])
229 {
230 $this->notice_duplicates_change($this->original['duplicates'], $this->modified['duplicates']);
231 }
232
233 $dofields = array(
234 'summary' => -1,
235 'severity' => 'severityid',
236 'dependency' => -1,
237 'productid' => -1,
238 'componentid' => -1,
239 'versionid' => -1,
240 'hidden' => -1,
241 'priority' => 'priorityid'
242 );
243 foreach ($dofields AS $field => $lookup)
244 {
245 if ($this->original["$field"] != $this->modified["$field"])
246 {
247 $this->notice_other_change($field, $this->original["$field"], $this->modified["$field"]);
248 }
249 }
250
251 foreach ($modified AS $field => $value)
252 {
253 if ($field == 'bugid')
254 {
255 continue;
256 }
257 if ($original["$field"] != $modified["$field"])
258 {
259 $this->notice_other_change($field, $original["$field"], $modified["$field"]);
260 }
261 }
262 }
263
264 // ###################################################################
265 /**
266 * Sends an email to the specified user ID that they are no longer the
267 * person assigned to the bug.
268 *
269 * @access private
270 *
271 * @param integer User ID to send to
272 */
273 function notice_no_longer_assigned($userid)
274 {
275 if ($this->users["$userid"]['options'][0] & $this->registry->emailoptions['notifications']['assignedto'] AND in_array($userid, $this->roles['-notapplicable-']))
276 {
277 $this->notices["$userid"][] = sprintf(
278 $this->registry->lang->string('You are no longer assigned to this bug, per %1$s\'s changes.'),
279
280 construct_user_display($this->registry->userinfo, false)
281 );
282 }
283 }
284
285 // ###################################################################
286 /**
287 * Informs the user that they have been made the assignee of the bug.
288 *
289 * @access private
290 *
291 * @param integer User ID
292 */
293 function notice_now_assigned($userid)
294 {
295 if ($this->users["$userid"]['options'][0] & $this->registry->emailoptions['notifications']['assignedto'] AND in_array($userid, $this->roles['-notapplicable-']))
296 {
297 $this->notices["$userid"][] = sprintf(
298 $this->registry->lang->string('You have been assigned to this bug by %1$s.'),
299
300 construct_user_display($this->registry->userinfo, false)
301 );
302 }
303 }
304
305 // ###################################################################
306 /**
307 * Sends a message to inform users that the status has changed.
308 *
309 * @access private
310 *
311 * @param integer Old status
312 * @param integer New status
313 */
314 function notice_status_change($old, $new)
315 {
316 $userlist = $this->fetch_users_with_on_bit('statusresolve');
317 foreach ($userlist AS $userid => $user)
318 {
319 $this->notices["$user[userid]"][] = sprintf(
320 $this->registry->lang->string('The status field has changed from "%1$s" to "%2$s".'),
321
322 $this->registry->datastore['status']["$old"]['status'],
323 $this->registry->datastore['status']["$new"]['status']
324 );
325 }
326 }
327
328 // ###################################################################
329 /**
330 * Sends an email to inform users that the resolution has changed.
331 *
332 * @access private
333 *
334 * @param integer Old resolution
335 * @param integer New resolution
336 */
337 function notice_resolution_change($old, $new)
338 {
339 $userlist = $this->fetch_users_with_on_bit('statusresolve');
340 foreach ($userlist AS $userid => $user)
341 {
342 $this->notices["$user[userid]"][] = sprintf(
343 $this->registry->lang->string('The resolution field has changed from "%1$s" to "%2$s".'),
344
345 $this->registry->datastore['resolution']["$old"]['resolution'],
346 $this->registry->datastore['resolution']["$new"]['resolution']
347 );
348 }
349 }
350
351 // ###################################################################
352 /**
353 * Informs users that the duplicates list has changed.
354 *
355 * @access private
356 *
357 * @param string Old duplicates list
358 * @param string New duplicates list
359 */
360 function notice_duplicates_change($old, $new)
361 {
362 $userlist = $this->fetch_useres_with_on_bit('duplicates');
363 foreach ($userlist AS $userid => $user)
364 {
365 $this->notices["$user[userid]"][] = sprintf(
366 $this->registry->lang->string('The duplicates list has changed from "%1$s" to %2$s".'),
367
368 $old,
369 $new
370 );
371 }
372 }
373
374 // ###################################################################
375 /**
376 * Sends the appropriate users information about a new comment being
377 * posted to the bug report.
378 *
379 * @access public
380 *
381 * @param array CommentAPI->values array
382 */
383 function send_new_comment_notice($comment)
384 {
385 $userlist = $this->fetch_users_with_on_bit('newcomment');
386 foreach ($userlist AS $userid => $user)
387 {
388 $this->notices["$user[userid]"][] = sprintf(
389 $this->registry->lang->string('The following comment was added by %1$s on %2$s:
390 ============================================
391 %3$s
392 ============================================'),
393
394 construct_user_display($this->registry->userinfo, false),
395 $this->registry->modules['date']->format($this->registry->options['dateformat'], $comment['dateline']),
396 $comment['comment']
397 );
398 }
399 }
400
401 // ###################################################################
402 /**
403 * A notice for an individual field changing.
404 *
405 * @access private
406 *
407 * @param string Field name
408 * @param mixed Original value
409 * @param mixed Modified value
410 */
411 function notice_other_change($name, $old, $new)
412 {
413 $userlist = $this->fetch_users_with_on_bit('otherfield');
414 foreach ($userlist AS $userid => $user)
415 {
416 $this->notices["$user[userid]"][] = sprintf(
417 $this->registry->lang->string('The %1$s field changed from "%2$s" to "%3$s".'),
418
419 $name,
420 $old,
421 $new
422 );
423 }
424 }
425
426 // ###################################################################
427 /**
428 * Sends appropriate users a notice when a new attachment has been
429 * added.
430 *
431 * @access public
432 *
433 * @param array AttachmentAPI->values array
434 * @param array List of all attachments made obsolete
435 * @param array Newly-inserted attachment ID
436 */
437 function send_new_attachment_notice($attachment, $obsolete, $id)
438 {
439 $userlist = $this->fetch_users_with_on_bit('newattachment');
440 foreach ($userlist AS $userid => $user)
441 {
442 $this->notices["$userid"][] = sprintf(
443 $this->registry->lang->string('%1$s has uploaded a new attachment:
444 ============================================
445 File name: %2$s
446 Description: %3$s
447 File size: %4$s Bytes
448 Makes obsolete: %5$s
449 View: %6$s
450 ============================================'),
451
452 construct_user_display($this->registry->userinfo, false),
453 $attachment['filename'],
454 $attachment['description'],
455 $attachment['filesize'],
456 implode(', ', (array)$obsolete),
457 $this->registry->options['trackerurl'] . '/viewattachment.php?attachmentid=' . $id
458 );
459 }
460 }
461
462 // ###################################################################
463 /**
464 * Generates an array of users who have a given email notification flag
465 * turned on in their bitfields.
466 *
467 * @access private
468 *
469 * @param string Notification bitfield name
470 *
471 * @return array Array of users and their data
472 */
473 function fetch_users_with_on_bit($bitname)
474 {
475 $idlist = array();
476
477 foreach ($this->users AS $user)
478 {
479 foreach ($this->registry->emailoptions['relations'] AS $name => $bit)
480 {
481 if (in_array($user['userid'], $this->roles["$name"]) AND $user['options']["$bit"] & $this->registry->emailoptions['notifications']["$bitname"])
482 {
483 $idlist[] = $user['userid'];
484 }
485 }
486 }
487
488 $masters = array_unique($idlist);
489
490 $return = array();
491 foreach ($masters AS $userid)
492 {
493 $return["$userid"] =& $this->users["$userid"];
494 }
495
496 return $return;
497 }
498 }
499
500 /*=====================================================================*\
501 || ###################################################################
502 || # $HeadURL$
503 || # $Id$
504 || ###################################################################
505 \*=====================================================================*/
506 ?>