Commit 32f3ff65 authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 297186: Send emails in the recipient's locale, not the current user's -…

Bug 297186: Send emails in the recipient's locale, not the current user's - Patch by Fré©ric Buclin <LpSolit@gmail.com> r=bkor a=justdave
parent 36540893
...@@ -146,14 +146,17 @@ init_page() if !$ENV{MOD_PERL}; ...@@ -146,14 +146,17 @@ init_page() if !$ENV{MOD_PERL};
sub template { sub template {
my $class = shift; my $class = shift;
request_cache()->{language} = "";
request_cache()->{template} ||= Bugzilla::Template->create(); request_cache()->{template} ||= Bugzilla::Template->create();
return request_cache()->{template}; return request_cache()->{template};
} }
sub template_inner { sub template_inner {
my $class = shift; my ($class, $lang) = @_;
$class->request_cache->{template_inner} ||= Bugzilla::Template->create(); $lang = defined($lang) ? $lang : (request_cache()->{language} || "");
return $class->request_cache->{template_inner}; request_cache()->{language} = $lang;
request_cache()->{"template_inner_$lang"} ||= Bugzilla::Template->create();
return request_cache()->{"template_inner_$lang"};
} }
sub cgi { sub cgi {
...@@ -497,7 +500,10 @@ The current C<Template> object, to be used for output ...@@ -497,7 +500,10 @@ The current C<Template> object, to be used for output
=item C<template_inner> =item C<template_inner>
If you ever need a L<Bugzilla::Template> object while you're already If you ever need a L<Bugzilla::Template> object while you're already
processing a template, use this. processing a template, use this. Also use it if you want to specify
the language to use. If no argument is passed, it uses the last
language set. If the argument is "" (empty string), the language is
reset to the current one (the one used by Bugzilla->template).
=item C<cgi> =item C<cgi>
......
...@@ -1405,7 +1405,7 @@ sub ValidateTime { ...@@ -1405,7 +1405,7 @@ sub ValidateTime {
} }
sub GetComments { sub GetComments {
my ($id, $comment_sort_order, $start, $end) = @_; my ($id, $comment_sort_order, $start, $end, $raw) = @_;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$comment_sort_order = $comment_sort_order || $comment_sort_order = $comment_sort_order ||
...@@ -1438,21 +1438,9 @@ sub GetComments { ...@@ -1438,21 +1438,9 @@ sub GetComments {
$comment{'email'} .= Bugzilla->params->{'emailsuffix'}; $comment{'email'} .= Bugzilla->params->{'emailsuffix'};
$comment{'name'} = $comment{'name'} || $comment{'email'}; $comment{'name'} = $comment{'name'} || $comment{'email'};
if ($comment{'type'} == CMT_DUPE_OF) {
$comment{'body'} .= "\n\n" . get_text('bug_duplicate_of', # If raw data is requested, do not format 'special' comments.
{ dupe_of => $comment{'extra_data'} }); $comment{'body'} = format_comment(\%comment) unless $raw;
}
elsif ($comment{'type'} == CMT_HAS_DUPE) {
$comment{'body'} = get_text('bug_has_duplicate',
{ dupe => $comment{'extra_data'} });
}
elsif ($comment{'type'} == CMT_POPULAR_VOTES) {
$comment{'body'} = get_text('bug_confirmed_by_votes');
}
elsif ($comment{'type'} == CMT_MOVED_TO) {
$comment{'body'} .= "\n\n" . get_text('bug_moved_to',
{ login => $comment{'extra_data'} });
}
push (@comments, \%comment); push (@comments, \%comment);
} }
...@@ -1464,6 +1452,32 @@ sub GetComments { ...@@ -1464,6 +1452,32 @@ sub GetComments {
return \@comments; return \@comments;
} }
# Format language specific comments. This routine must not update
# $comment{'body'} itself, see BugMail::prepare_comments().
sub format_comment {
my $comment = shift;
my $body;
if ($comment->{'type'} == CMT_DUPE_OF) {
$body = $comment->{'body'} . "\n\n" .
get_text('bug_duplicate_of', { dupe_of => $comment->{'extra_data'} });
}
elsif ($comment->{'type'} == CMT_HAS_DUPE) {
$body = get_text('bug_has_duplicate', { dupe => $comment->{'extra_data'} });
}
elsif ($comment->{'type'} == CMT_POPULAR_VOTES) {
$body = get_text('bug_confirmed_by_votes');
}
elsif ($comment->{'type'} == CMT_MOVED_TO) {
$body = $comment->{'body'} . "\n\n" .
get_text('bug_moved_to', { login => $comment->{'extra_data'} });
}
else {
$body = $comment->{'body'};
}
return $body;
}
# Get the activity of a bug, starting from $starttime (if given). # Get the activity of a bug, starting from $starttime (if given).
# This routine assumes ValidateBugID has been previously called. # This routine assumes ValidateBugID has been previously called.
sub GetBugActivity { sub GetBugActivity {
......
...@@ -313,7 +313,7 @@ sub Send { ...@@ -313,7 +313,7 @@ sub Send {
} }
my ($newcomments, $anyprivate) = get_comments_by_bug($id, $start, $end); my ($raw_comments, $anyprivate, $count) = get_comments_by_bug($id, $start, $end);
########################################################################### ###########################################################################
# Start of email filtering code # Start of email filtering code
...@@ -408,6 +408,9 @@ sub Send { ...@@ -408,6 +408,9 @@ sub Send {
my @sent; my @sent;
my @excluded; my @excluded;
# Some comments are language specific. We cache them here.
my %comments;
foreach my $user_id (keys %recipients) { foreach my $user_id (keys %recipients) {
my %rels_which_want; my %rels_which_want;
my $sent_mail = 0; my $sent_mail = 0;
...@@ -416,15 +419,24 @@ sub Send { ...@@ -416,15 +419,24 @@ sub Send {
# Deleted users must be excluded. # Deleted users must be excluded.
next unless $user; next unless $user;
if ($user->can_see_bug($id)) # What's the language chosen by this user for email?
{ my $lang = $user->settings->{'lang'}->{'value'};
if ($user->can_see_bug($id)) {
# It's time to format language specific comments.
unless (exists $comments{$lang}) {
Bugzilla->template_inner($lang);
$comments{$lang} = prepare_comments($raw_comments, $count);
Bugzilla->template_inner("");
}
# Go through each role the user has and see if they want mail in # Go through each role the user has and see if they want mail in
# that role. # that role.
foreach my $relationship (keys %{$recipients{$user_id}}) { foreach my $relationship (keys %{$recipients{$user_id}}) {
if ($user->wants_bug_mail($id, if ($user->wants_bug_mail($id,
$relationship, $relationship,
$diffs, $diffs,
$newcomments, $comments{$lang},
$changer, $changer,
!$start)) !$start))
{ {
...@@ -471,7 +483,7 @@ sub Send { ...@@ -471,7 +483,7 @@ sub Send {
\%defmailhead, \%defmailhead,
\%fielddescription, \%fielddescription,
\@diffparts, \@diffparts,
$newcomments, $comments{$lang},
$anyprivate, $anyprivate,
$start, $start,
$id, $id,
...@@ -634,16 +646,17 @@ sub sendMail { ...@@ -634,16 +646,17 @@ sub sendMail {
}; };
my $msg; my $msg;
my $template = Bugzilla->template_inner; my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
$template->process("email/newchangedmail.txt.tmpl", $vars, \$msg) $template->process("email/newchangedmail.txt.tmpl", $vars, \$msg)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
Bugzilla->template_inner("");
MessageToMTA($msg); MessageToMTA($msg);
return 1; return 1;
} }
# Get bug comments for the given period and format them to be used in emails. # Get bug comments for the given period.
sub get_comments_by_bug { sub get_comments_by_bug {
my ($id, $start, $end) = @_; my ($id, $start, $end) = @_;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
...@@ -661,22 +674,35 @@ sub get_comments_by_bug { ...@@ -661,22 +674,35 @@ sub get_comments_by_bug {
undef, ($id, $start)); undef, ($id, $start));
} }
my $comments = Bugzilla::Bug::GetComments($id, "oldest_to_newest", $start, $end); my $raw = 1; # Do not format comments which are not of type CMT_NORMAL.
my $comments = Bugzilla::Bug::GetComments($id, "oldest_to_newest", $start, $end, $raw);
if (Bugzilla->params->{'insidergroup'}) {
$anyprivate = 1 if scalar(grep {$_->{'isprivate'} > 0} @$comments);
}
return ($comments, $anyprivate, $count);
}
# Prepare comments for the given language.
sub prepare_comments {
my ($raw_comments, $count) = @_;
foreach my $comment (@$comments) { my $result = "";
foreach my $comment (@$raw_comments) {
if ($count) { if ($count) {
$result .= "\n\n--- Comment #$count from " . $comment->{'name'} . " <" . $result .= "\n\n--- Comment #$count from " . $comment->{'name'} . " <" .
$comment->{'email'} . Bugzilla->params->{'emailsuffix'} . "> " . $comment->{'email'} . Bugzilla->params->{'emailsuffix'} . "> " .
format_time($comment->{'time'}) . " ---\n"; format_time($comment->{'time'}) . " ---\n";
} }
if ($comment->{'isprivate'} > 0 && Bugzilla->params->{'insidergroup'}) { # Format language specific comments. We don't update $comment->{'body'}
$anyprivate = 1; # directly, otherwise it would grow everytime you call format_comment()
} # with a different language as some text may be appended to the existing one.
$result .= ($comment->{'already_wrapped'} ? $comment->{'body'} my $body = Bugzilla::Bug::format_comment($comment);
: wrap_comment($comment->{'body'})); $result .= ($comment->{'already_wrapped'} ? $body : wrap_comment($body));
$count++; $count++;
} }
return ($result, $anyprivate); return $result;
} }
1; 1;
...@@ -35,7 +35,8 @@ use Bugzilla::User::Setting; ...@@ -35,7 +35,8 @@ use Bugzilla::User::Setting;
use Bugzilla::Util qw(get_text); use Bugzilla::Util qw(get_text);
use Bugzilla::Version; use Bugzilla::Version;
use constant SETTINGS => { sub SETTINGS {
return {
# 2005-03-03 travis@sedsystems.ca -- Bug 41972 # 2005-03-03 travis@sedsystems.ca -- Bug 41972
display_quips => { options => ["on", "off"], default => "on" }, display_quips => { options => ["on", "off"], default => "on" },
# 2005-03-10 travis@sedsystems.ca -- Bug 199048 # 2005-03-10 travis@sedsystems.ca -- Bug 199048
...@@ -56,7 +57,10 @@ use constant SETTINGS => { ...@@ -56,7 +57,10 @@ use constant SETTINGS => {
default => 'cc_unless_role' }, default => 'cc_unless_role' },
# 2006-08-04 wurblzap@gmail.com -- Bug 322693 # 2006-08-04 wurblzap@gmail.com -- Bug 322693
skin => { subclass => 'Skin', default => 'standard' }, skin => { subclass => 'Skin', default => 'standard' },
# 2006-12-10 LpSolit@gmail.com -- Bug 297186
lang => { options => [split(/[\s,]+/, Bugzilla->params->{'languages'})],
default => Bugzilla->params->{'defaultlanguage'} }
}
}; };
use constant SYSTEM_GROUPS => ( use constant SYSTEM_GROUPS => (
......
...@@ -108,9 +108,10 @@ sub sortAcceptLanguage { ...@@ -108,9 +108,10 @@ sub sortAcceptLanguage {
# If no Accept-Language is present it uses the defined default # If no Accept-Language is present it uses the defined default
# Templates may also be found in the extensions/ tree # Templates may also be found in the extensions/ tree
sub getTemplateIncludePath { sub getTemplateIncludePath {
my $lang = Bugzilla->request_cache->{'language'} || "";
# Return cached value if available # Return cached value if available
my $include_path = Bugzilla->request_cache->{template_include_path}; my $include_path = Bugzilla->request_cache->{"template_include_path_$lang"};
return $include_path if $include_path; return $include_path if $include_path;
my $templatedir = bz_locations()->{'templatedir'}; my $templatedir = bz_locations()->{'templatedir'};
...@@ -132,15 +133,16 @@ sub getTemplateIncludePath { ...@@ -132,15 +133,16 @@ sub getTemplateIncludePath {
} }
} }
my @languages = sortAcceptLanguage($languages); my @languages = sortAcceptLanguage($languages);
my @accept_language = sortAcceptLanguage($ENV{'HTTP_ACCEPT_LANGUAGE'} || "" ); # If $lang is specified, only consider this language.
my @accept_language = ($lang) || sortAcceptLanguage($ENV{'HTTP_ACCEPT_LANGUAGE'} || "");
my @usedlanguages; my @usedlanguages;
foreach my $lang (@accept_language) { foreach my $language (@accept_language) {
# Per RFC 1766 and RFC 2616 any language tag matches also its # Per RFC 1766 and RFC 2616 any language tag matches also its
# primary tag. That is 'en' (accept language) matches 'en-us', # primary tag. That is 'en' (accept language) matches 'en-us',
# 'en-uk' etc. but not the otherway round. (This is unfortunately # 'en-uk' etc. but not the otherway round. (This is unfortunately
# not very clearly stated in those RFC; see comment just over 14.5 # not very clearly stated in those RFC; see comment just over 14.5
# in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4) # in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
if(my @found = grep /^\Q$lang\E(-.+)?$/i, @languages) { if(my @found = grep /^\Q$language\E(-.+)?$/i, @languages) {
push (@usedlanguages, @found); push (@usedlanguages, @found);
} }
} }
...@@ -180,9 +182,9 @@ sub getTemplateIncludePath { ...@@ -180,9 +182,9 @@ sub getTemplateIncludePath {
foreach my $dir (@$include_path) { foreach my $dir (@$include_path) {
push(@dirs, $dir) unless grep ($dir eq $_, @dirs); push(@dirs, $dir) unless grep ($dir eq $_, @dirs);
} }
Bugzilla->request_cache->{template_include_path} = \@dirs; Bugzilla->request_cache->{"template_include_path_$lang"} = \@dirs;
return Bugzilla->request_cache->{template_include_path}; return Bugzilla->request_cache->{"template_include_path_$lang"};
} }
sub put_header { sub put_header {
...@@ -527,7 +529,7 @@ sub create { ...@@ -527,7 +529,7 @@ sub create {
# We need a possibility to reset the cache, so that no files from # We need a possibility to reset the cache, so that no files from
# the previous language pollute the action. # the previous language pollute the action.
if ($opts{'clean_cache'}) { if ($opts{'clean_cache'}) {
delete Bugzilla->request_cache->{template_include_path}; delete Bugzilla->request_cache->{template_include_path_};
} }
# IMPORTANT - If you make any configuration changes here, make sure to # IMPORTANT - If you make any configuration changes here, make sure to
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
# Contributor(s): Shane H. W. Travis <travis@sedsystems.ca> # Contributor(s): Shane H. W. Travis <travis@sedsystems.ca>
# Max Kanat-Alexander <mkanat@bugzilla.org> # Max Kanat-Alexander <mkanat@bugzilla.org>
# Marc Schumann <wurblzap@gmail.com> # Marc Schumann <wurblzap@gmail.com>
# # Frédéric Buclin <LpSolit@gmail.com>
package Bugzilla::User::Setting; package Bugzilla::User::Setting;
...@@ -128,15 +128,28 @@ sub new { ...@@ -128,15 +128,28 @@ sub new {
############################### ###############################
sub add_setting { sub add_setting {
my ($name, $values, $default_value, $subclass) = @_; my ($name, $values, $default_value, $subclass, $force_check) = @_;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
return if _setting_exists($name); my $exists = _setting_exists($name);
return if ($exists && !$force_check);
($name && $default_value) ($name && $default_value)
|| ThrowCodeError("setting_info_invalid"); || ThrowCodeError("setting_info_invalid");
if ($exists) {
# If this setting exists, we delete it and regenerate it.
$dbh->do('DELETE FROM setting_value WHERE name = ?', undef, $name);
$dbh->do('DELETE FROM setting WHERE name = ?', undef, $name);
# Remove obsolete user preferences for this setting.
my $list = join(', ', map {$dbh->quote($_)} @$values);
$dbh->do("DELETE FROM profile_setting
WHERE setting_name = ? AND setting_value NOT IN ($list)",
undef, $name);
}
else {
print get_text('install_setting_new', { name => $name }) . "\n"; print get_text('install_setting_new', { name => $name }) . "\n";
}
$dbh->do(q{INSERT INTO setting (name, default_value, is_enabled, subclass) $dbh->do(q{INSERT INTO setting (name, default_value, is_enabled, subclass)
VALUES (?, ?, 1, ?)}, VALUES (?, ?, 1, ?)},
undef, ($name, $default_value, $subclass)); undef, ($name, $default_value, $subclass));
......
...@@ -33,6 +33,7 @@ use Bugzilla::Util; ...@@ -33,6 +33,7 @@ use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Token; use Bugzilla::Token;
use Bugzilla::User; use Bugzilla::User;
use Bugzilla::User::Setting;
my $user = Bugzilla->login(LOGIN_REQUIRED); my $user = Bugzilla->login(LOGIN_REQUIRED);
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
...@@ -73,6 +74,7 @@ if ($action eq 'save' && $current_module) { ...@@ -73,6 +74,7 @@ if ($action eq 'save' && $current_module) {
my @changes = (); my @changes = ();
my @module_param_list = "Bugzilla::Config::${current_module}"->get_param_list(1); my @module_param_list = "Bugzilla::Config::${current_module}"->get_param_list(1);
my $update_lang_user_pref = 0;
foreach my $i (@module_param_list) { foreach my $i (@module_param_list) {
my $name = $i->{'name'}; my $name = $i->{'name'};
my $value = $cgi->param($name); my $value = $cgi->param($name);
...@@ -132,8 +134,20 @@ if ($action eq 'save' && $current_module) { ...@@ -132,8 +134,20 @@ if ($action eq 'save' && $current_module) {
if (($name eq "shutdownhtml") && ($value ne "")) { if (($name eq "shutdownhtml") && ($value ne "")) {
$vars->{'shutdown_is_active'} = 1; $vars->{'shutdown_is_active'} = 1;
} }
if ($name eq 'languages') {
$update_lang_user_pref = 1;
} }
} }
}
if ($update_lang_user_pref) {
# We have to update the list of languages users can choose.
# If some users have selected a language which is no longer available,
# then we delete it (the user pref is reset to the default one).
my @languages = split(/[\s,]+/, Bugzilla->params->{'languages'});
map {trick_taint($_)} @languages;
add_setting('lang', \@languages, Bugzilla->params->{'defaultlanguage'}, undef, 1);
}
$vars->{'message'} = 'parameters_updated'; $vars->{'message'} = 'parameters_updated';
$vars->{'param_changed'} = \@changes; $vars->{'param_changed'} = \@changes;
......
...@@ -40,5 +40,6 @@ ...@@ -40,5 +40,6 @@
"always" => "Always", "always" => "Always",
"never" => "Never", "never" => "Never",
"cc_unless_role" => "Only if I have no role on them", "cc_unless_role" => "Only if I have no role on them",
"lang" => "Language used in email",
} }
%] %]
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