blob: 55eeeab25e97a081525aea09311a9961cd63f7f5 [file] [log] [blame]
timothy@apple.comf42518d2008-02-06 20:19:16 +00001# -*- Mode: perl; indent-tabs-mode: nil -*-
2#
3# The contents of this file are subject to the Mozilla Public
4# License Version 1.1 (the "License"); you may not use this file
5# except in compliance with the License. You may obtain a copy of
6# the License at http://www.mozilla.org/MPL/
7#
8# Software distributed under the License is distributed on an "AS
9# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10# implied. See the License for the specific language governing
11# rights and limitations under the License.
12#
13# The Original Code is the Bugzilla Bug Tracking System.
14#
15# The Initial Developer of the Original Code is Netscape Communications
16# Corporation. Portions created by Netscape are
17# Copyright (C) 1998 Netscape Communications Corporation. All
18# Rights Reserved.
19#
20# Contributor(s): Terry Weissman <terry@mozilla.org>,
21# Bryce Nesbitt <bryce-mozilla@nextbus.com>
22# Dan Mosedale <dmose@mozilla.org>
23# Alan Raetz <al_raetz@yahoo.com>
24# Jacob Steenhagen <jake@actex.net>
25# Matthew Tuck <matty@chariot.net.au>
26# Bradley Baetz <bbaetz@student.usyd.edu.au>
27# J. Paul Reed <preed@sigkill.com>
28# Gervase Markham <gerv@gerv.net>
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000029# Byron Jones <bugzilla@glob.com.au>
ddkilzer@apple.com57772842014-10-16 16:00:58 +000030# Reed Loden <reed@reedloden.com>
31# Frédéric Buclin <LpSolit@gmail.com>
32# Guy Pyrzak <guy.pyrzak@gmail.com>
timothy@apple.comf42518d2008-02-06 20:19:16 +000033
34use strict;
35
36package Bugzilla::BugMail;
37
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000038use Bugzilla::Error;
timothy@apple.comf42518d2008-02-06 20:19:16 +000039use Bugzilla::User;
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000040use Bugzilla::Constants;
41use Bugzilla::Util;
42use Bugzilla::Bug;
ddkilzer@apple.com57772842014-10-16 16:00:58 +000043use Bugzilla::Comment;
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000044use Bugzilla::Mailer;
ddkilzer@apple.com57772842014-10-16 16:00:58 +000045use Bugzilla::Hook;
timothy@apple.comf42518d2008-02-06 20:19:16 +000046
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000047use Date::Parse;
48use Date::Format;
ddkilzer@apple.com57772842014-10-16 16:00:58 +000049use Scalar::Util qw(blessed);
50use List::MoreUtils qw(uniq);
ddkilzer@apple.com097da082009-07-03 02:14:25 +000051
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000052use constant BIT_DIRECT => 1;
53use constant BIT_WATCHING => 2;
timothy@apple.comf42518d2008-02-06 20:19:16 +000054
ddkilzer@apple.com57772842014-10-16 16:00:58 +000055sub relationships {
56 my $ref = RELATIONSHIPS;
57 # Clone it so that we don't modify the constant;
58 my %relationships = %$ref;
59 Bugzilla::Hook::process('bugmail_relationships',
60 { relationships => \%relationships });
61 return %relationships;
timothy@apple.comf42518d2008-02-06 20:19:16 +000062}
63
64# This is a bit of a hack, basically keeping the old system()
65# cmd line interface. Should clean this up at some point.
66#
67# args: bug_id, and an optional hash ref which may have keys for:
68# changer, owner, qa, reporter, cc
69# Optional hash contains values of people which will be forced to those
70# roles when the email is sent.
71# All the names are email addresses, not userids
72# values are scalars, except for cc, which is a list
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000073sub Send {
ddkilzer@apple.com57772842014-10-16 16:00:58 +000074 my ($id, $forced, $params) = @_;
75 $params ||= {};
timothy@apple.comf42518d2008-02-06 20:19:16 +000076
77 my $dbh = Bugzilla->dbh;
ddkilzer@apple.com57772842014-10-16 16:00:58 +000078 my $bug = new Bugzilla::Bug($id);
timothy@apple.comf42518d2008-02-06 20:19:16 +000079
ddkilzer@apple.com57772842014-10-16 16:00:58 +000080 my $start = $bug->lastdiffed;
81 my $end = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000082
ddkilzer@apple.com57772842014-10-16 16:00:58 +000083 # Bugzilla::User objects of people in various roles. More than one person
84 # can 'have' a role, if the person in that role has changed, or people are
85 # watching.
86 my @assignees = ($bug->assigned_to);
87 my @qa_contacts = $bug->qa_contact || ();
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +000088
ddkilzer@apple.com57772842014-10-16 16:00:58 +000089 my @ccs = @{ $bug->cc_users };
timothy@apple.comf42518d2008-02-06 20:19:16 +000090 # Include the people passed in as being in particular roles.
91 # This can include people who used to hold those roles.
92 # At this point, we don't care if there are duplicates in these arrays.
93 my $changer = $forced->{'changer'};
94 if ($forced->{'owner'}) {
ddkilzer@apple.com57772842014-10-16 16:00:58 +000095 push (@assignees, Bugzilla::User->check($forced->{'owner'}));
timothy@apple.comf42518d2008-02-06 20:19:16 +000096 }
97
98 if ($forced->{'qacontact'}) {
ddkilzer@apple.com57772842014-10-16 16:00:58 +000099 push (@qa_contacts, Bugzilla::User->check($forced->{'qacontact'}));
timothy@apple.comf42518d2008-02-06 20:19:16 +0000100 }
101
102 if ($forced->{'cc'}) {
103 foreach my $cc (@{$forced->{'cc'}}) {
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000104 push(@ccs, Bugzilla::User->check($cc));
timothy@apple.comf42518d2008-02-06 20:19:16 +0000105 }
106 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000107 my %user_cache = map { $_->id => $_ } (@assignees, @qa_contacts, @ccs);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000108
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000109 my @diffs;
110 if (!$start) {
111 @diffs = _get_new_bugmail_fields($bug);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000112 }
113
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000114 if ($params->{dep_only}) {
115 push(@diffs, { field_name => 'bug_status',
116 old => $params->{changes}->{bug_status}->[0],
117 new => $params->{changes}->{bug_status}->[1],
118 login_name => $changer->login,
119 blocker => $params->{blocker} },
120 { field_name => 'resolution',
121 old => $params->{changes}->{resolution}->[0],
122 new => $params->{changes}->{resolution}->[1],
123 login_name => $changer->login,
124 blocker => $params->{blocker} });
timothy@apple.comf42518d2008-02-06 20:19:16 +0000125 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000126 else {
127 push(@diffs, _get_diffs($bug, $end, \%user_cache));
timothy@apple.comf42518d2008-02-06 20:19:16 +0000128 }
129
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000130 my $comments = $bug->comments({ after => $start, to => $end });
131 # Skip empty comments.
132 @$comments = grep { $_->type || $_->body =~ /\S/ } @$comments;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000133
134 ###########################################################################
135 # Start of email filtering code
136 ###########################################################################
137
138 # A user_id => roles hash to keep track of people.
139 my %recipients;
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000140 my %watching;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000141
142 # Now we work out all the people involved with this bug, and note all of
143 # the relationships in a hash. The keys are userids, the values are an
144 # array of role constants.
145
timothy@apple.comf42518d2008-02-06 20:19:16 +0000146 # CCs
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000147 $recipients{$_->id}->{+REL_CC} = BIT_DIRECT foreach (@ccs);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000148
149 # Reporter (there's only ever one)
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000150 $recipients{$bug->reporter->id}->{+REL_REPORTER} = BIT_DIRECT;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000151
152 # QA Contact
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000153 if (Bugzilla->params->{'useqacontact'}) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000154 foreach (@qa_contacts) {
155 # QA Contact can be blank; ignore it if so.
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000156 $recipients{$_->id}->{+REL_QA} = BIT_DIRECT if $_;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000157 }
158 }
159
160 # Assignee
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000161 $recipients{$_->id}->{+REL_ASSIGNEE} = BIT_DIRECT foreach (@assignees);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000162
163 # The last relevant set of people are those who are being removed from
164 # their roles in this change. We get their names out of the diffs.
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000165 foreach my $change (@diffs) {
166 if ($change->{old}) {
167 # You can't stop being the reporter, so we don't check that
168 # relationship here.
timothy@apple.comf42518d2008-02-06 20:19:16 +0000169 # Ignore people whose user account has been deleted or renamed.
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000170 if ($change->{field_name} eq 'cc') {
171 foreach my $cc_user (split(/[\s,]+/, $change->{old})) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000172 my $uid = login_to_id($cc_user);
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000173 $recipients{$uid}->{+REL_CC} = BIT_DIRECT if $uid;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000174 }
175 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000176 elsif ($change->{field_name} eq 'qa_contact') {
177 my $uid = login_to_id($change->{old});
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000178 $recipients{$uid}->{+REL_QA} = BIT_DIRECT if $uid;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000179 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000180 elsif ($change->{field_name} eq 'assigned_to') {
181 my $uid = login_to_id($change->{old});
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000182 $recipients{$uid}->{+REL_ASSIGNEE} = BIT_DIRECT if $uid;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000183 }
184 }
185 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000186
187 # Make sure %user_cache has every user in it so far referenced
188 foreach my $user_id (keys %recipients) {
189 $user_cache{$user_id} ||= new Bugzilla::User($user_id);
190 }
timothy@apple.comf42518d2008-02-06 20:19:16 +0000191
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000192 Bugzilla::Hook::process('bugmail_recipients',
193 { bug => $bug, recipients => \%recipients,
194 users => \%user_cache, diffs => \@diffs });
timothy@apple.comf42518d2008-02-06 20:19:16 +0000195
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000196 # Find all those user-watching anyone on the current list, who is not
197 # on it already themselves.
198 my $involved = join(",", keys %recipients);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000199
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000200 my $userwatchers =
201 $dbh->selectall_arrayref("SELECT watcher, watched FROM watch
202 WHERE watched IN ($involved)");
203
204 # Mark these people as having the role of the person they are watching
205 foreach my $watch (@$userwatchers) {
206 while (my ($role, $bits) = each %{$recipients{$watch->[1]}}) {
207 $recipients{$watch->[0]}->{$role} |= BIT_WATCHING
208 if $bits & BIT_DIRECT;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000209 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000210 push(@{$watching{$watch->[0]}}, $watch->[1]);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000211 }
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000212
213 # Global watcher
214 my @watchers = split(/[,\s]+/, Bugzilla->params->{'globalwatchers'});
215 foreach (@watchers) {
216 my $watcher_id = login_to_id($_);
217 next unless $watcher_id;
218 $recipients{$watcher_id}->{+REL_GLOBAL_WATCHER} = BIT_DIRECT;
219 }
220
timothy@apple.comf42518d2008-02-06 20:19:16 +0000221 # We now have a complete set of all the users, and their relationships to
222 # the bug in question. However, we are not necessarily going to mail them
223 # all - there are preferences, permissions checks and all sorts to do yet.
224 my @sent;
225 my @excluded;
226
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000227 # The email client will display the Date: header in the desired timezone,
228 # so we can always use UTC here.
229 my $date = $params->{dep_only} ? $end : $bug->delta_ts;
230 $date = format_time($date, '%a, %d %b %Y %T %z', 'UTC');
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000231
timothy@apple.comf42518d2008-02-06 20:19:16 +0000232 foreach my $user_id (keys %recipients) {
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000233 my %rels_which_want;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000234 my $sent_mail = 0;
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000235 $user_cache{$user_id} ||= new Bugzilla::User($user_id);
236 my $user = $user_cache{$user_id};
timothy@apple.comf42518d2008-02-06 20:19:16 +0000237 # Deleted users must be excluded.
238 next unless $user;
239
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000240 if ($user->can_see_bug($id)) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000241 # Go through each role the user has and see if they want mail in
242 # that role.
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000243 foreach my $relationship (keys %{$recipients{$user_id}}) {
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000244 if ($user->wants_bug_mail($bug,
timothy@apple.comf42518d2008-02-06 20:19:16 +0000245 $relationship,
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000246 $start ? \@diffs : [],
247 $comments,
248 $params->{dep_only},
249 $changer))
timothy@apple.comf42518d2008-02-06 20:19:16 +0000250 {
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000251 $rels_which_want{$relationship} =
252 $recipients{$user_id}->{$relationship};
timothy@apple.comf42518d2008-02-06 20:19:16 +0000253 }
254 }
255 }
256
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000257 if (scalar(%rels_which_want)) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000258 # So the user exists, can see the bug, and wants mail in at least
259 # one role. But do we want to send it to them?
260
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000261 # We shouldn't send mail if this is a dependency mail and the
262 # depending bug is not visible to the user.
263 # This is to avoid leaking the summary of a confidential bug.
timothy@apple.comf42518d2008-02-06 20:19:16 +0000264 my $dep_ok = 1;
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000265 if ($params->{dep_only}) {
266 $dep_ok = $user->can_see_bug($params->{blocker}->id) ? 1 : 0;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000267 }
268
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000269 # Make sure the user isn't in the nomail list, and the dep check passed.
270 if ($user->email_enabled && $dep_ok) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000271 # OK, OK, if we must. Email the user.
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000272 $sent_mail = sendMail(
273 { to => $user,
274 bug => $bug,
275 comments => $comments,
276 date => $date,
277 changer => $changer,
278 watchers => exists $watching{$user_id} ?
279 $watching{$user_id} : undef,
280 diffs => \@diffs,
281 rels_which_want => \%rels_which_want,
282 });
timothy@apple.comf42518d2008-02-06 20:19:16 +0000283 }
284 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000285
timothy@apple.comf42518d2008-02-06 20:19:16 +0000286 if ($sent_mail) {
287 push(@sent, $user->login);
288 }
289 else {
290 push(@excluded, $user->login);
291 }
292 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000293
294 # When sending bugmail about a blocker being reopened or resolved,
295 # we say nothing about changes in the bug being blocked, so we must
296 # not update lastdiffed in this case.
297 if (!$params->{dep_only}) {
298 $dbh->do('UPDATE bugs SET lastdiffed = ? WHERE bug_id = ?',
299 undef, ($end, $id));
300 $bug->{lastdiffed} = $end;
301 }
timothy@apple.comf42518d2008-02-06 20:19:16 +0000302
303 return {'sent' => \@sent, 'excluded' => \@excluded};
304}
305
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000306sub sendMail {
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000307 my $params = shift;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000308
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000309 my $user = $params->{to};
310 my $bug = $params->{bug};
311 my @send_comments = @{ $params->{comments} };
312 my $date = $params->{date};
313 my $changer = $params->{changer};
314 my $watchingRef = $params->{watchers};
315 my @diffs = @{ $params->{diffs} };
316 my $relRef = $params->{rels_which_want};
timothy@apple.comf42518d2008-02-06 20:19:16 +0000317
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000318 # Only display changes the user is allowed see.
319 my @display_diffs;
320
321 foreach my $diff (@diffs) {
322 my $add_diff = 0;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000323
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000324 if (grep { $_ eq $diff->{field_name} } TIMETRACKING_FIELDS) {
325 $add_diff = 1 if $user->is_timetracker;
326 }
327 elsif (!$diff->{isprivate} || $user->is_insider) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000328 $add_diff = 1;
329 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000330 push(@display_diffs, $diff) if $add_diff;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000331 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000332
333 if (!$user->is_insider) {
334 @send_comments = grep { !$_->is_private } @send_comments;
335 }
336
337 if (!scalar(@display_diffs) && !scalar(@send_comments)) {
timothy@apple.comf42518d2008-02-06 20:19:16 +0000338 # Whoops, no differences!
339 return 0;
340 }
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000341
342 my (@reasons, @reasons_watch);
343 while (my ($relationship, $bits) = each %{$relRef}) {
344 push(@reasons, $relationship) if ($bits & BIT_DIRECT);
345 push(@reasons_watch, $relationship) if ($bits & BIT_WATCHING);
346 }
347
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000348 my %relationships = relationships();
349 my @headerrel = map { $relationships{$_} } @reasons;
350 my @watchingrel = map { $relationships{$_} } @reasons_watch;
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000351 push(@headerrel, 'None') unless @headerrel;
352 push(@watchingrel, 'None') unless @watchingrel;
353 push @watchingrel, map { user_id_to_login($_) } @$watchingRef;
354
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000355 my $vars = {
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000356 date => $date,
357 to_user => $user,
358 bug => $bug,
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000359 reasons => \@reasons,
360 reasons_watch => \@reasons_watch,
361 reasonsheader => join(" ", @headerrel),
362 reasonswatchheader => join(" ", @watchingrel),
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000363 changer => $changer,
364 diffs => \@display_diffs,
365 changedfields => [uniq map { $_->{field_name} } @display_diffs],
366 new_comments => \@send_comments,
367 threadingmarker => build_thread_marker($bug->id, $user->id, !$bug->lastdiffed),
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000368 };
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000369 my $msg = _generate_bugmail($user, $vars);
timothy@apple.comf42518d2008-02-06 20:19:16 +0000370 MessageToMTA($msg);
371
372 return 1;
373}
374
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000375sub _generate_bugmail {
376 my ($user, $vars) = @_;
377 my $template = Bugzilla->template_inner($user->setting('lang'));
378 my ($msg_text, $msg_html, $msg_header);
379
380 $template->process("email/bugmail-header.txt.tmpl", $vars, \$msg_header)
381 || ThrowTemplateError($template->error());
382 $template->process("email/bugmail.txt.tmpl", $vars, \$msg_text)
383 || ThrowTemplateError($template->error());
timothy@apple.comf42518d2008-02-06 20:19:16 +0000384
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000385 my @parts = (
386 Email::MIME->create(
387 attributes => {
388 content_type => "text/plain",
389 },
390 body => $msg_text,
391 )
392 );
393 if ($user->setting('email_format') eq 'html') {
394 $template->process("email/bugmail.html.tmpl", $vars, \$msg_html)
395 || ThrowTemplateError($template->error());
396 push @parts, Email::MIME->create(
397 attributes => {
398 content_type => "text/html",
399 },
400 body => $msg_html,
401 );
timothy@apple.comf42518d2008-02-06 20:19:16 +0000402 }
403
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000404 # TT trims the trailing newline, and threadingmarker may be ignored.
405 my $email = new Email::MIME("$msg_header\n");
406 if (scalar(@parts) == 1) {
407 $email->content_type_set($parts[0]->content_type);
408 } else {
409 $email->content_type_set('multipart/alternative');
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000410 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000411 $email->parts_set(\@parts);
412 return $email;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000413}
414
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000415sub _get_diffs {
416 my ($bug, $end, $user_cache) = @_;
417 my $dbh = Bugzilla->dbh;
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000418
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000419 my @args = ($bug->id);
420 # If lastdiffed is NULL, then we don't limit the search on time.
421 my $when_restriction = '';
422 if ($bug->lastdiffed) {
423 $when_restriction = ' AND bug_when > ? AND bug_when <= ?';
424 push @args, ($bug->lastdiffed, $end);
ddkilzer@apple.comf3615fc2009-07-03 02:13:41 +0000425 }
ddkilzer@apple.com57772842014-10-16 16:00:58 +0000426
427 my $diffs = $dbh->selectall_arrayref(
428 "SELECT fielddefs.name AS field_name,
429 bugs_activity.bug_when, bugs_activity.removed AS old,
430 bugs_activity.added AS new, bugs_activity.attach_id,
431 bugs_activity.comment_id, bugs_activity.who
432 FROM bugs_activity
433 INNER JOIN fielddefs
434 ON fielddefs.id = bugs_activity.fieldid
435 WHERE bugs_activity.bug_id = ?
436 $when_restriction
437 ORDER BY bugs_activity.bug_when", {Slice=>{}}, @args);
438
439 foreach my $diff (@$diffs) {
440 $user_cache->{$diff->{who}} ||= new Bugzilla::User($diff->{who});
441 $diff->{who} = $user_cache->{$diff->{who}};
442 if ($diff->{attach_id}) {
443 $diff->{isprivate} = $dbh->selectrow_array(
444 'SELECT isprivate FROM attachments WHERE attach_id = ?',
445 undef, $diff->{attach_id});
446 }
447 if ($diff->{field_name} eq 'longdescs.isprivate') {
448 my $comment = Bugzilla::Comment->new($diff->{comment_id});
449 $diff->{num} = $comment->count;
450 $diff->{isprivate} = $diff->{new};
451 }
452 }
453
454 return @$diffs;
455}
456
457sub _get_new_bugmail_fields {
458 my $bug = shift;
459 my @fields = @{ Bugzilla->fields({obsolete => 0, in_new_bugmail => 1}) };
460 my @diffs;
461
462 foreach my $field (@fields) {
463 my $name = $field->name;
464 my $value = $bug->$name;
465
466 if (ref $value eq 'ARRAY') {
467 $value = join(', ', @$value);
468 }
469 elsif (blessed($value) && $value->isa('Bugzilla::User')) {
470 $value = $value->login;
471 }
472 elsif (blessed($value) && $value->isa('Bugzilla::Object')) {
473 $value = $value->name;
474 }
475 elsif ($name eq 'estimated_time') {
476 # "0.00" (which is what we get from the DB) is true,
477 # so we explicitly do a numerical comparison with 0.
478 $value = 0 if $value == 0;
479 }
480 elsif ($name eq 'deadline') {
481 $value = time2str("%Y-%m-%d", str2time($value)) if $value;
482 }
483
484 # If there isn't anything to show, don't include this header.
485 next unless $value;
486
487 push(@diffs, {field_name => $name, new => $value});
488 }
489
490 return @diffs;
timothy@apple.comf42518d2008-02-06 20:19:16 +0000491}
492
4931;