Commit 71320dae authored by Guy Pyrzak's avatar Guy Pyrzak

Bug 65477: Send HTML bugmail

r=mkanat, a=mkanat
parent 22c14482
......@@ -29,6 +29,7 @@
# Byron Jones <bugzilla@glob.com.au>
# Reed Loden <reed@reedloden.com>
# Frédéric Buclin <LpSolit@gmail.com>
# Guy Pyrzak <guy.pyrzak@gmail.com>
use strict;
......@@ -83,10 +84,9 @@ sub Send {
# can 'have' a role, if the person in that role has changed, or people are
# watching.
my @assignees = ($bug->assigned_to);
my @qa_contacts = ($bug->qa_contact);
my @qa_contacts = $bug->qa_contact || ();
my @ccs = @{ $bug->cc_users };
# Include the people passed in as being in particular roles.
# This can include people who used to hold those roles.
# At this point, we don't care if there are duplicates in these arrays.
......@@ -104,6 +104,7 @@ sub Send {
push(@ccs, Bugzilla::User->check($cc));
}
}
my %user_cache = map { $_->id => $_ } (@assignees, @qa_contacts, @ccs);
my @diffs;
if (!$start) {
......@@ -123,7 +124,7 @@ sub Send {
blocker => $params->{blocker} });
}
else {
push(@diffs, _get_diffs($bug, $end));
push(@diffs, _get_diffs($bug, $end, \%user_cache));
}
my $comments = $bug->comments({ after => $start, to => $end });
......@@ -220,7 +221,8 @@ sub Send {
foreach my $user_id (keys %recipients) {
my %rels_which_want;
my $sent_mail = 0;
my $user = new Bugzilla::User($user_id);
$user_cache{$user_id} ||= new Bugzilla::User($user_id);
my $user = $user_cache{$user_id};
# Deleted users must be excluded.
next unless $user;
......@@ -353,19 +355,47 @@ sub sendMail {
new_comments => \@send_comments,
threadingmarker => build_thread_marker($bug->id, $user->id, !$bug->lastdiffed),
};
my $msg = _generate_bugmail($user, $vars);
MessageToMTA($msg);
return 1;
}
my $msg;
sub _generate_bugmail {
my ($user, $vars) = @_;
my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
$template->process("email/newchangedmail.txt.tmpl", $vars, \$msg)
|| ThrowTemplateError($template->error());
my ($msg_text, $msg_html, $msg_header);
MessageToMTA($msg);
$template->process("email/bugmail-header.txt.tmpl", $vars, \$msg_header)
|| ThrowTemplateError($template->error());
$template->process("email/bugmail.txt.tmpl", $vars, \$msg_text)
|| ThrowTemplateError($template->error());
$template->process("email/bugmail.html.tmpl", $vars, \$msg_html)
|| ThrowTemplateError($template->error());
return 1;
my @parts = (
Email::MIME->create(
attributes => {
content_type => "text/plain",
},
body => $msg_text,
),
Email::MIME->create(
attributes => {
content_type => "text/html",
},
body => $msg_html,
),
);
my $email = new Email::MIME($msg_header);
$email->parts_set(\@parts);
$email->content_type_set('multipart/alternative');
return $email;
}
sub _get_diffs {
my ($bug, $end) = @_;
my ($bug, $end, $user_cache) = @_;
my $dbh = Bugzilla->dbh;
my @args = ($bug->id);
......@@ -377,20 +407,20 @@ sub _get_diffs {
}
my $diffs = $dbh->selectall_arrayref(
"SELECT profiles.login_name, profiles.realname, fielddefs.name AS field_name,
"SELECT fielddefs.name AS field_name,
bugs_activity.bug_when, bugs_activity.removed AS old,
bugs_activity.added AS new, bugs_activity.attach_id,
bugs_activity.comment_id
bugs_activity.comment_id, bugs_activity.who
FROM bugs_activity
INNER JOIN fielddefs
ON fielddefs.id = bugs_activity.fieldid
INNER JOIN profiles
ON profiles.userid = bugs_activity.who
WHERE bugs_activity.bug_id = ?
$when_restriction
ORDER BY bugs_activity.bug_when", {Slice=>{}}, @args);
foreach my $diff (@$diffs) {
$user_cache->{$diff->{who}} ||= new Bugzilla::User($diff->{who});
$diff->{who} = $user_cache->{$diff->{who}};
if ($diff->{attach_id}) {
$diff->{isprivate} = $dbh->selectrow_array(
'SELECT isprivate FROM attachments WHERE attach_id = ?',
......
......@@ -348,8 +348,12 @@ sub get_bug_link {
}
# Prevent code injection in the title.
$title = html_quote(clean_text($title));
my $linkval = "show_bug.cgi?id=" . $bug->id;
if ($options->{full_url}) {
$linkval = correct_urlbase() . $linkval;
}
if (defined $options->{comment_num}) {
$linkval .= "#c" . $options->{comment_num};
}
......
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Guy Pyrzak
# Portions created by the Initial Developer are Copyright (C) 2010 the
# Initial Developer. All Rights Reserved.
#
# Contributor(s): Guy Pyrzak <guy.pyrzak@gmail.com>
#%]
[% PROCESS "global/field-descs.none.tmpl" %]
[% field_label = field_descs.${change.field_name} %]
[% old_value = display_value(change.field_name, change.old) %]
[% new_value = display_value(change.field_name, change.new) %]
[% IF change.field_name == "estimated_time" || change.field_name == "remaining_time" %]
[% old_value = old_value FILTER format('%.2f') %]
[% new_value = new_value FILTER format('%.2f') %]
[% END %]
[% IF change.attach_id %]
[% field_label = field_label.replace('^(Attachment )?', "Attachment #${change.attach_id} ") %]
[% END %]
[% IF change.field_name == 'longdescs.isprivate' %]
[% field_label = field_label.replace('^(Comment )?', "Comment #${change.num} ") %]
[% END %]
\ No newline at end of file
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): André Batosti <batosti@async.com.br>
# Frédéric Buclin <LpSolit@gmail.com>
# Guy Pyrzak <guy.pyrzak@gmail.com>
#%]
[% PROCESS "global/field-descs.none.tmpl" %]
[% PROCESS "global/reason-descs.none.tmpl" %]
[% isnew = bug.lastdiffed ? 0 : 1 %]
From: [% Param('mailfrom') %]
To: [% to_user.email %]
Subject: [[% terms.Bug %] [%+ bug.id %]] [% 'New: ' IF isnew %][%+ bug.short_desc %]
Date: [% delta_ts || bug.delta_ts FILTER time("%a, %d %b %Y %T %z", to_user.timezone) %]
X-Bugzilla-Reason: [% reasonsheader %]
X-Bugzilla-Type: [% isnew ? 'new' : 'changed' %]
X-Bugzilla-Watch-Reason: [% reasonswatchheader %]
[% IF Param('useclassification') %]
X-Bugzilla-Classification: [% bug.classification %]
[% END %]
X-Bugzilla-Product: [% bug.product %]
X-Bugzilla-Component: [% bug.component %]
X-Bugzilla-Keywords: [% bug.keywords %]
X-Bugzilla-Severity: [% bug.bug_severity %]
X-Bugzilla-Who: [% changer.login %]
X-Bugzilla-Status: [% bug.bug_status %]
X-Bugzilla-Priority: [% bug.priority %]
X-Bugzilla-Assigned-To: [% bug.assigned_to.login %]
X-Bugzilla-Target-Milestone: [% bug.target_milestone %]
X-Bugzilla-Changed-Fields: [% changedfields.join(" ") %]
[%+ threadingmarker %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Guy Pyrzak
# Portions created by the Initial Developer are Copyright (C) 2010 the
# Initial Developer. All Rights Reserved.
#
# Contributor(s): Guy Pyrzak <guy.pyrzak@gmail.com>
#%]
[% PROCESS "global/field-descs.none.tmpl" %]
[% PROCESS "global/reason-descs.none.tmpl" %]
[% isnew = bug.lastdiffed ? 0 : 1 %]
<html>
<head>
<base href="[% %]" />
</head>
<body>
[% PROCESS generate_diffs %]
<p>
[% FOREACH comment = new_comments.reverse %]
<div>
[% IF comment.count %]
<b>[% "Comment # ${comment.count}" FILTER bug_link( bug,
{comment_num => comment.count, full_url => 1}) FILTER none %]
from [% INCLUDE global/user.html.tmpl who = comment.author %]</b>
[% END %]
<pre>[% comment.body_full({ wrap => 1 }) FILTER html %]</pre>
</div>
[% END %]
</p>
<p>
<a href="[% urlbase FILTER html %]userprefs.cgi?tab=email">
Configure [% terms.bug %]mail
</a>
</p>
<span>You are receiving this mail because:</span>
<ul>
[% FOREACH reason = reasons %]
[% IF reason_descs.$reason %]
<li>[% reason_descs.$reason FILTER html %]</li>
[% END %]
[% END %]
[% FOREACH reason = reasons_watch %]
[% IF watch_reason_descs.$reason %]
<li>[% watch_reason_descs.$reason FILTER html %]</li>
[% END %]
[% END %]
</ul>
</body>
</html>
[% BLOCK generate_diffs %]
[% "${terms.Bug} ${bug.id}" FILTER bug_link(bug, full_url => 1) FILTER none %]
[% last_changer = "" %]
<table border="1" cellspacing="0" cellpadding="2">
[% FOREACH change = diffs %]
[% IF !isnew && change.who.login != last_changer %]
[% last_changer = change.who.login %]
[% IF change.blocker %]
<tr>
<td colspan="3">
[% "${terms.Bug} ${bug.id}" FILTER bug_link(bug, full_url => 1) FILTER none %] depends
on [% "${terms.bug} ${change.blocker.id}"
FILTER bug_link(change.blocker, full_url => 1) FILTER none %],
which changed state.
</td>
</tr>
[% ELSE %]
<tr>
<td colspan="3">
[% INCLUDE global/user.html.tmpl who = change.who %]
changed:
</td>
</tr>
[% END %]
<tr>
<th>What</th>
<th>Removed</th>
<th>Added</th>
</tr>
[% END %]
[% PROCESS "email/bugmail-common.txt.html" %]
[% IF isnew %]
<tr>
<th>[% field_label FILTER html %]</th>
<td>[% new_value FILTER html %]</td>
</tr>
[% ELSE %]
<tr>
<td style="text-align:right;">[% field_label FILTER html %]</td>
<td>
[% IF old_value %]
[% old_value FILTER html %]
[% ELSE %]
&nbsp;
[% END%]
</td>
<td>
[% IF new_value %]
[% new_value FILTER html %]
[% ELSE %]
&nbsp;
[% END%]
</td>
</tr>
[% END %]
[% END %]
</table>
[% END %]
\ No newline at end of file
......@@ -17,6 +17,7 @@
#
# Contributor(s): André Batosti <batosti@async.com.br>
# Frédéric Buclin <LpSolit@gmail.com>
# Guy Pyrzak <guy.pyrzak@gmail.com>
#%]
[% PROCESS "global/field-descs.none.tmpl" %]
......@@ -24,28 +25,6 @@
[% isnew = bug.lastdiffed ? 0 : 1 %]
From: [% Param('mailfrom') %]
To: [% to_user.email %]
Subject: [[% terms.Bug %] [%+ bug.id %]] [% 'New: ' IF isnew %][%+ bug.short_desc %]
Date: [% delta_ts || bug.delta_ts FILTER time("%a, %d %b %Y %T %z", to_user.timezone) %]
X-Bugzilla-Reason: [% reasonsheader %]
X-Bugzilla-Type: [% isnew ? 'new' : 'changed' %]
X-Bugzilla-Watch-Reason: [% reasonswatchheader %]
[% IF Param('useclassification') %]
X-Bugzilla-Classification: [% bug.classification %]
[% END %]
X-Bugzilla-Product: [% bug.product %]
X-Bugzilla-Component: [% bug.component %]
X-Bugzilla-Keywords: [% bug.keywords %]
X-Bugzilla-Severity: [% bug.bug_severity %]
X-Bugzilla-Who: [% changer.login %]
X-Bugzilla-Status: [% bug.bug_status %]
X-Bugzilla-Priority: [% bug.priority %]
X-Bugzilla-Assigned-To: [% bug.assigned_to.login %]
X-Bugzilla-Target-Milestone: [% bug.target_milestone %]
X-Bugzilla-Changed-Fields: [% changedfields.join(" ") %]
[%+ threadingmarker %]
[%+ PROCESS generate_diffs -%]
[% FOREACH comment = new_comments %]
......@@ -74,42 +53,25 @@ Configure [% terms.bug %]mail: [% urlbase %]userprefs.cgi?tab=email
[%+ last_changer = "" %]
[% FOREACH change = diffs %]
[% IF !isnew && change.login_name != last_changer %]
[% last_changer = change.login_name %]
[% IF !isnew && change.who.login != last_changer %]
[% last_changer = change.who.login %]
[% IF change.blocker %]
[% terms.Bug %] [%+ bug.id %] depends on [% terms.bug %] [%+ change.blocker.id %], which changed state.
[%+ terms.Bug %] [%+ change.blocker.id %] Summary: [% change.blocker.short_desc %]
[%+ urlbase %]show_bug.cgi?id=[% change.blocker.id %]
[% ELSE %]
[%~ IF change.realname %]
[% change.realname _ " <" _ change.login_name _ ">" %]
[%~ IF change.who.name %]
[% change.who.name _ " <" _ change.who.login _ ">" %]
[% ELSE %]
[% change.login_name %]
[% change.who.login %]
[% END %] changed:
[% END %]
What |Removed |Added
----------------------------------------------------------------------------
[%+ END %][%# End of IF. This indentation is intentional! ~%]
[% field_label = field_descs.${change.field_name} %]
[% old_value = display_value(change.field_name, change.old) %]
[% new_value = display_value(change.field_name, change.new) %]
[%~ IF change.field_name == "estimated_time" || change.field_name == "remaining_time" %]
[% old_value = old_value FILTER format('%.2f') %]
[% new_value = new_value FILTER format('%.2f') %]
[% END %]
[%~ IF change.attach_id %]
[% field_label = field_label.replace('^(Attachment )?', "Attachment #${change.attach_id} ") %]
[% END %]
[%~ IF change.field_name == 'longdescs.isprivate' %]
[% field_label = field_label.replace('^(Comment )?', "Comment #${change.num} ") %]
[% END %]
[% PROCESS "email/bugmail-common.txt.html"%]
[%~ IF isnew %]
[% format_columns(2, field_label _ ":", new_value) -%]
[% ELSE %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment