if (scalar(@list)) {
foreach my $ref (@list) {
my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref);
- my $s;
$maxvotesperbug = min($votesperuser, $maxvotesperbug);
my $removedvotes = $oldvotes - $newvotes;
- $s = ($oldvotes == 1) ? "" : "s";
- my $oldvotestext = "You had $oldvotes vote$s on this bug.";
-
- $s = ($removedvotes == 1) ? "" : "s";
- my $removedvotestext = "You had $removedvotes vote$s removed from this bug.";
-
- my $newvotestext;
if ($newvotes) {
$dbh->do("UPDATE votes SET vote_count = ? " .
"WHERE bug_id = ? AND who = ?",
undef, ($newvotes, $id, $userid));
- $s = $newvotes == 1 ? "" : "s";
- $newvotestext = "You still have $newvotes vote$s on this bug."
} else {
$dbh->do("DELETE FROM votes WHERE bug_id = ? AND who = ?",
undef, ($id, $userid));
- $newvotestext = "You have no more votes remaining on this bug.";
}
# Notice that we did not make sure that the user fit within the $votesperuser
# Now lets send the e-mail to alert the user to the fact that their votes have
# been reduced or removed.
my $vars = {
-
'to' => $name . Bugzilla->params->{'emailsuffix'},
'bugid' => $id,
'reason' => $reason,
'votesremoved' => $removedvotes,
'votesold' => $oldvotes,
'votesnew' => $newvotes,
-
- 'votesremovedtext' => $removedvotestext,
- 'votesoldtext' => $oldvotestext,
- 'votesnewtext' => $newvotestext,
-
- 'count' => $removedvotes . "\n " . $newvotestext
};
+ my $voter = new Bugzilla::User($userid);
+ my $template = Bugzilla->template_inner($voter->settings->{'lang'}->{'value'});
+
my $msg;
- my $template = Bugzilla->template;
$template->process("email/votes-removed.txt.tmpl", $vars, \$msg);
push(@messages, $msg);
}
+ Bugzilla->template_inner("");
+
my $votes = $dbh->selectrow_array("SELECT SUM(vote_count) " .
"FROM votes WHERE bug_id = ?",
undef, $id) || 0;
sub notify {
my ($flag, $bug, $attachment) = @_;
- my $template = Bugzilla->template;
-
# There is nobody to notify.
return unless ($flag->{'addressee'} || $flag->type->cc_list);
- my $attachment_is_private = $attachment ? $attachment->isprivate : undef;
-
# If the target bug is restricted to one or more groups, then we need
# to make sure we don't send email about it to unauthorized users
# on the request type's CC: list, so we have to trawl the list for users
# not in those groups or email addresses that don't have an account.
my @bug_in_groups = grep {$_->{'ison'} || $_->{'mandatory'}} @{$bug->groups};
+ my $attachment_is_private = $attachment ? $attachment->isprivate : undef;
- if (scalar(@bug_in_groups) || $attachment_is_private) {
- my @new_cc_list;
- foreach my $cc (split(/[, ]+/, $flag->type->cc_list)) {
- my $ccuser = new Bugzilla::User({ name => $cc }) || next;
-
- next if (scalar(@bug_in_groups) && !$ccuser->can_see_bug($bug->bug_id));
- next if $attachment_is_private
- && Bugzilla->params->{"insidergroup"}
- && !$ccuser->in_group(Bugzilla->params->{"insidergroup"});
- push(@new_cc_list, $cc);
- }
- $flag->type->{'cc_list'} = join(", ", @new_cc_list);
+ my %recipients;
+ foreach my $cc (split(/[, ]+/, $flag->type->cc_list)) {
+ my $ccuser = new Bugzilla::User({ name => $cc });
+ next if (scalar(@bug_in_groups) && (!$ccuser || !$ccuser->can_see_bug($bug->bug_id)));
+ next if $attachment_is_private && (!$ccuser || !$ccuser->is_insider);
+ # Prevent duplicated entries due to case sensitivity.
+ $cc = $ccuser ? $ccuser->email : $cc;
+ $recipients{$cc} = $ccuser;
}
- # If there is nobody left to notify, return.
- return unless ($flag->{'addressee'} || $flag->type->cc_list);
-
- my @recipients = split(/[, ]+/, $flag->type->cc_list);
# Only notify if the addressee is allowed to receive the email.
if ($flag->{'addressee'} && $flag->{'addressee'}->email_enabled) {
- push @recipients, $flag->{'addressee'}->email;
+ $recipients{$flag->{'addressee'}->email} = $flag->{'addressee'};
}
- # Process and send notification for each recipient
- foreach my $to (@recipients)
- {
- next unless $to;
+ # Process and send notification for each recipient.
+ # If there are users in the CC list who don't have an account,
+ # use the default language for email notifications.
+ my $default_lang;
+ if (grep { !$_ } values %recipients) {
+ my $default_user = new Bugzilla::User();
+ $default_lang = $default_user->settings->{'lang'}->{'value'};
+ }
+
+ foreach my $to (keys %recipients) {
my $vars = { 'flag' => $flag,
'to' => $to,
'bug' => $bug,
'attachment' => $attachment};
+
+ my $lang = $recipients{$to} ?
+ $recipients{$to}->settings->{'lang'}->{'value'} : $default_lang;
+
+ my $template = Bugzilla->template_inner($lang);
my $message;
- my $rv = $template->process("request/email.txt.tmpl", $vars, \$message);
- if (!$rv) {
- Bugzilla->cgi->header();
- ThrowTemplateError($template->error());
- }
+ $template->process("request/email.txt.tmpl", $vars, \$message)
+ || ThrowTemplateError($template->error());
+ Bugzilla->template_inner("");
MessageToMTA($message);
}
}
use Bugzilla::Error;
use Bugzilla::Mailer;
use Bugzilla::Util;
+use Bugzilla::User;
use Date::Format;
use Date::Parse;
$template->process('account/email/request-new.txt.tmpl', $vars, \$message)
|| ThrowTemplateError($template->error());
+ # In 99% of cases, the user getting the confirmation email is the same one
+ # who made the request, and so it is reasonable to send the email in the same
+ # language used to view the "Create a New Account" page (we cannot use his
+ # user prefs as the user has no account yet!).
MessageToMTA($message);
}
sub IssueEmailChangeToken {
- my ($userid, $old_email, $new_email) = @_;
+ my ($user, $old_email, $new_email) = @_;
my $email_suffix = Bugzilla->params->{'emailsuffix'};
- my ($token, $token_ts) = _create_token($userid, 'emailold', $old_email . ":" . $new_email);
+ my ($token, $token_ts) = _create_token($user->id, 'emailold', $old_email . ":" . $new_email);
- my $newtoken = _create_token($userid, 'emailnew', $old_email . ":" . $new_email);
+ my $newtoken = _create_token($user->id, 'emailnew', $old_email . ":" . $new_email);
# Mail the user the token along with instructions for using it.
- my $template = Bugzilla->template;
+ my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
my $vars = {};
$vars->{'oldemailaddress'} = $old_email . $email_suffix;
$template->process("account/email/change-new.txt.tmpl", $vars, \$message)
|| ThrowTemplateError($template->error());
+ Bugzilla->template_inner("");
MessageToMTA($message);
}
# Generates a random token, adds it to the tokens table, and sends it
# to the user with instructions for using it to change their password.
sub IssuePasswordToken {
- my $loginname = shift;
+ my $user = shift;
my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
- my $vars = {};
- # Retrieve the user's ID from the database.
- trick_taint($loginname);
- my ($userid, $too_soon) =
- $dbh->selectrow_array('SELECT profiles.userid, tokens.issuedate
- FROM profiles
- LEFT JOIN tokens
- ON tokens.userid = profiles.userid
- AND tokens.tokentype = ?
- AND tokens.issuedate > NOW() - ' .
- $dbh->sql_interval(10, 'MINUTE') . '
- WHERE ' . $dbh->sql_istrcmp('login_name', '?'),
- undef, ('password', $loginname));
+ my $too_soon =
+ $dbh->selectrow_array('SELECT 1 FROM tokens
+ WHERE userid = ?
+ AND tokentype = ?
+ AND issuedate > NOW() - ' .
+ $dbh->sql_interval(10, 'MINUTE'),
+ undef, ($user->id, 'password'));
ThrowUserError('too_soon_for_new_token', {'type' => 'password'}) if $too_soon;
- my ($token, $token_ts) = _create_token($userid, 'password', $::ENV{'REMOTE_ADDR'});
+ my ($token, $token_ts) = _create_token($user->id, 'password', $::ENV{'REMOTE_ADDR'});
# Mail the user the token along with instructions for using it.
- $vars->{'token'} = $token;
- $vars->{'emailaddress'} = $loginname . Bugzilla->params->{'emailsuffix'};
+ my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
+ my $vars = {};
+ $vars->{'token'} = $token;
+ $vars->{'emailaddress'} = $user->email;
$vars->{'max_token_age'} = MAX_TOKEN_AGE;
$vars->{'token_ts'} = $token_ts;
$vars, \$message)
|| ThrowTemplateError($template->error());
+ Bugzilla->template_inner("");
MessageToMTA($message);
}
return $token;
}
-# Cancels a previously issued token and notifies the system administrator.
+# Cancels a previously issued token and notifies the user.
# This should only happen when the user accidentally makes a token request
# or when a malicious hacker makes a token request on behalf of a user.
sub Cancel {
my ($token, $cancelaction, $vars) = @_;
my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
$vars ||= {};
# Get information about the token being canceled.
trick_taint($token);
- my ($issuedate, $tokentype, $eventdata, $loginname) =
+ my ($issuedate, $tokentype, $eventdata, $userid) =
$dbh->selectrow_array('SELECT ' . $dbh->sql_date_format('issuedate') . ',
- tokentype, eventdata, login_name
+ tokentype, eventdata, userid
FROM tokens
- LEFT JOIN profiles
- ON tokens.userid = profiles.userid
WHERE token = ?',
undef, $token);
- # If we are cancelling the creation of a new user account, then there
+ # If we are canceling the creation of a new user account, then there
# is no entry in the 'profiles' table.
- $loginname ||= $eventdata;
- $vars->{'emailaddress'} = $loginname . Bugzilla->params->{'emailsuffix'};
- $vars->{'maintainer'} = Bugzilla->params->{'maintainer'};
+ my $user = new Bugzilla::User($userid);
+
+ $vars->{'emailaddress'} = $userid ? $user->email : $eventdata;
$vars->{'remoteaddress'} = $::ENV{'REMOTE_ADDR'};
$vars->{'token'} = $token;
$vars->{'tokentype'} = $tokentype;
$vars->{'cancelaction'} = $cancelaction;
# Notify the user via email about the cancellation.
+ my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
my $message;
$template->process("account/cancel-token.txt.tmpl", $vars, \$message)
|| ThrowTemplateError($template->error());
+ Bugzilla->template_inner("");
MessageToMTA($message);
# Delete the token from the database.
use Bugzilla::Token;
Bugzilla::Token::issue_new_user_account_token($login_name);
- Bugzilla::Token::IssueEmailChangeToken($user_id, $old_email, $new_email);
- Bugzilla::Token::IssuePasswordToken($login_name);
+ Bugzilla::Token::IssueEmailChangeToken($user, $old_email, $new_email);
+ Bugzilla::Token::IssuePasswordToken($user);
Bugzilla::Token::DeletePasswordTokens($user_id, $reason);
Bugzilla::Token::Cancel($token, $cancelaction, $vars);
Returns: Nothing. It throws an error if the same user made the same
request in the last few minutes.
-=item C<sub IssueEmailChangeToken($user_id, $old_email, $new_email)>
+=item C<sub IssueEmailChangeToken($user, $old_email, $new_email)>
Description: Sends two distinct tokens per email to the old and new email
addresses to confirm the email address change for the given
- user ID. These tokens remain valid for the next MAX_TOKEN_AGE days.
+ user. These tokens remain valid for the next MAX_TOKEN_AGE days.
- Params: $user_id - The user ID of the user account requesting a new
- email address.
+ Params: $user - User object of the user requesting a new
+ email address.
$old_email - The current (old) email address of the user.
$new_email - The new email address of the user.
Returns: Nothing.
-=item C<IssuePasswordToken($login_name)>
+=item C<IssuePasswordToken($user)>
- Description: Sends a token per email to the given login name. This token
+ Description: Sends a token per email to the given user. This token
can be used to change the password (e.g. in case the user
cannot remember his password and wishes to enter a new one).
- Params: $login_name - The login name of the user requesting a new password.
+ Params: $user - User object of the user requesting a new password.
Returns: Nothing. It throws an error if the same user made the same
request in the last few minutes.
# function call in Flag::notify. group_group_map is in here si
# Bugzilla::User can flatten groups.
'bugs WRITE', 'profiles READ', 'email_setting READ',
+ 'setting READ', 'profile_setting READ',
'cc READ', 'bug_group_map READ', 'user_group_map READ',
'group_group_map READ', 'groups READ', 'group_control_map READ');
my ($who, $id) = (@$vote);
# If some votes are removed, RemoveVotes() returns a list
# of messages to send to voters.
- my $msgs =
- RemoveVotes($id, $who, "The rules for voting on this product " .
- "has changed;\nyou had too many votes " .
- "for a single bug.");
+ my $msgs = RemoveVotes($id, $who, 'votes_too_many_per_bug');
foreach my $msg (@$msgs) {
MessageToMTA($msg);
}
foreach my $bug_id (@$bug_ids) {
# RemoveVotes() returns a list of messages to send
# in case some voters had too many votes.
- my $msgs =
- RemoveVotes($bug_id, $who, "The rules for voting on this " .
- "product has changed; you had " .
- "too many\ntotal votes, so all " .
- "votes have been removed.");
+ my $msgs = RemoveVotes($bug_id, $who, 'votes_too_many_per_user');
foreach my $msg (@$msgs) {
MessageToMTA($msg);
}
"keyworddefs READ", "groups READ", "attachments READ",
"group_control_map AS oldcontrolmap READ",
"group_control_map AS newcontrolmap READ",
- "group_control_map READ", "email_setting READ", "classifications READ");
+ "group_control_map READ", "email_setting READ", "classifications READ",
+ "setting READ", "profile_setting READ");
# It may sound crazy to set %formhash for each bug as $cgi->param()
# will not change, but %formhash is modified below and we prefer
# If some votes have been removed, RemoveVotes() returns
# a list of messages to send to voters.
# We delay the sending of these messages till tables are unlocked.
- $msgs = RemoveVotes($id, 0,
- "This bug has been moved to a different product");
-
+ $msgs = RemoveVotes($id, 0, 'votes_bug_moved');
CheckIfVotedConfirmed($id, $whoid);
}
# Go ahead and send out the message now
my $message;
- $template->process('email/sudo.txt.tmpl',
- { reason => $reason },
- \$message);
+ my $mail_template = Bugzilla->template_inner($target_user->settings->{'lang'}->{'value'});
+ $mail_template->process('email/sudo.txt.tmpl', { reason => $reason }, \$message);
+ Bugzilla->template_inner("");
MessageToMTA($message);
-
+
$vars->{'message'} = 'sudo_started';
$vars->{'target'} = $target_user->login;
$target = 'global/message.html.tmpl';
mistake or someone attempting to break into your [% terms.Bugzilla %] account.
Take a look at the information below and forward this email
-to [% maintainer %] if you suspect foul play.
+to [% Param('maintainer') %] if you suspect foul play.
Token: [% token %]
Token Type: [% tokentype %]
Some or all of your votes have been removed from [% terms.bug %] [%+ bugid %].
-[% votesoldtext %]
+You had [% votesold FILTER html %] [%+ IF votesold == 1 %]vote[% ELSE %]votes[% END
+%] on this [% terms.bug %], but [% votesremoved FILTER html %] have been removed.
-[% votesnewtext %]
+[% IF votesnew %]
+You still have [% votesnew FILTER html %] [%+ IF votesnew == 1 %]vote[% ELSE %]votes[% END %] on this [% terms.bug %].
+[% ELSE %]
+You have no more votes remaining on this [% terms.bug %].
+[% END %]
+
+Reason:
+[% IF reason == "votes_bug_moved" %]
+ This [% terms.bug %] has been moved to a different product.
+
+[% ELSIF reason == "votes_too_many_per_bug" %]
+ The rules for voting on this product has changed;
+ you had too many votes for a single [% terms.bug %].
+
+[% ELSIF reason == "votes_too_many_per_user" %]
+ The rules for voting on this product has changed; you had
+ too many total votes, so all votes have been removed.
+[% END %]
-Reason: [% reason %]
[% Param("urlbase") %]show_bug.cgi?id=[% bugid %]
# If the user is requesting a password change, make sure they submitted
# their login name and it exists in the database, and that the DB module is in
# the list of allowed verification methods.
-my $login_name;
+my $user_account;
if ( $::action eq 'reqpw' ) {
- $login_name = $cgi->param('loginname');
- defined $login_name
- || ThrowUserError("login_needed_for_password_change");
+ my $login_name = $cgi->param('loginname')
+ || ThrowUserError("login_needed_for_password_change");
# check verification methods
unless (Bugzilla->user->authorizer->can_change_password) {
validate_email_syntax($login_name)
|| ThrowUserError('illegal_email_address', {addr => $login_name});
- my ($user_id) = $dbh->selectrow_array('SELECT userid FROM profiles WHERE ' .
- $dbh->sql_istrcmp('login_name', '?'),
- undef, $login_name);
- $user_id || ThrowUserError("account_inexistent");
+ $user_account = new Bugzilla::User({name => $login_name});
+ $user_account || ThrowUserError("account_inexistent");
}
# If the user is changing their password, make sure they submitted a new
# that variable and runs the appropriate code.
if ($::action eq 'reqpw') {
- requestChangePassword($login_name);
+ requestChangePassword($user_account);
} elsif ($::action eq 'cfmpw') {
confirmChangePassword();
} elsif ($::action eq 'cxlpw') {
################################################################################
sub requestChangePassword {
- my ($login_name) = @_;
- Bugzilla::Token::IssuePasswordToken($login_name);
+ my ($user) = @_;
+ Bugzilla::Token::IssuePasswordToken($user);
$vars->{'message'} = "password_change_request";
is_available_username($new_login_name)
|| ThrowUserError("account_exists", {email => $new_login_name});
- Bugzilla::Token::IssueEmailChangeToken($user->id, $old_login_name,
+ Bugzilla::Token::IssueEmailChangeToken($user, $old_login_name,
$new_login_name);
$vars->{'email_changes_saved'} = 1;
#
sub mail {
my $args = shift;
+ my $addressee = $args->{recipient};
+ # Don't send mail to someone whose bugmail notification is disabled.
+ return if $addressee->email_disabled;
- # Don't send mail to someone on the nomail list.
- return if $args->{recipient}->email_disabled;
-
+ my $template = Bugzilla->template_inner($addressee->settings->{'lang'}->{'value'});
my $msg = ''; # it's a temporary variable to hold the template output
$args->{'alternatives'} ||= [];
$template->process("whine/multipart-mime.txt.tmpl", $args, \$msg)
or die($template->error());
+ Bugzilla->template_inner("");
MessageToMTA($msg);
delete $args->{'boundary'};
$vars->{'bugs'} = \@bugs;
my $msg;
- my $template = Bugzilla->template;
- $template->process("email/whine.txt.tmpl", $vars, \$msg);
+ my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
+ $template->process("email/whine.txt.tmpl", $vars, \$msg)
+ or die($template->error());
+ Bugzilla->template_inner("");
MessageToMTA($msg);
print "$email " . join(" ", @{$bugs{$email}}) . "\n";