summaryrefslogtreecommitdiffstats
path: root/extensions/Voting/Extension.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/Voting/Extension.pm')
-rw-r--r--extensions/Voting/Extension.pm1367
1 files changed, 710 insertions, 657 deletions
diff --git a/extensions/Voting/Extension.pm b/extensions/Voting/Extension.pm
index 198b065fa..523ee653d 100644
--- a/extensions/Voting/Extension.pm
+++ b/extensions/Voting/Extension.pm
@@ -23,15 +23,16 @@ use Bugzilla::Token;
use List::Util qw(min sum);
-use constant VERSION => BUGZILLA_VERSION;
+use constant VERSION => BUGZILLA_VERSION;
use constant DEFAULT_VOTES_PER_BUG => 1;
+
# These came from Bugzilla itself, so they maintain the old numbers
# they had before.
use constant CMT_POPULAR_VOTES => 3;
-use constant REL_VOTER => 4;
+use constant REL_VOTER => 4;
BEGIN {
- *Bugzilla::Bug::user_votes = \&_bug_user_votes;
+ *Bugzilla::Bug::user_votes = \&_bug_user_votes;
}
################
@@ -39,70 +40,71 @@ BEGIN {
################
BEGIN {
- *Bugzilla::Bug::votes = \&votes;
+ *Bugzilla::Bug::votes = \&votes;
}
sub votes {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- return $self->{votes} if exists $self->{votes};
+ return $self->{votes} if exists $self->{votes};
- $self->{votes} = $dbh->selectrow_array('SELECT votes FROM bugs WHERE bug_id = ?',
- undef, $self->id);
- return $self->{votes};
+ $self->{votes}
+ = $dbh->selectrow_array('SELECT votes FROM bugs WHERE bug_id = ?',
+ undef, $self->id);
+ return $self->{votes};
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'votes'} = {
- FIELDS => [
- who => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE'}},
- bug_id => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE'}},
- vote_count => {TYPE => 'INT2', NOTNULL => 1},
- ],
- INDEXES => [
- votes_who_idx => ['who'],
- votes_bug_id_idx => ['bug_id'],
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'votes'} = {
+ FIELDS => [
+ who => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}
+ },
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}
+ },
+ vote_count => {TYPE => 'INT2', NOTNULL => 1},
+ ],
+ INDEXES => [votes_who_idx => ['who'], votes_bug_id_idx => ['bug_id'],],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- # Note that before Bugzilla 4.0, voting was a built-in part of Bugzilla,
- # so updates to the columns for old versions of Bugzilla happen in
- # Bugzilla::Install::DB, and can't safely be moved to this extension.
-
- my $field = new Bugzilla::Field({ name => 'votes' });
- if (!$field) {
- Bugzilla::Field->create(
- { name => 'votes', description => 'Votes', buglist => 1 });
- }
-
- $dbh->bz_add_column('products', 'votesperuser',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
- $dbh->bz_add_column('products', 'maxvotesperbug',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
- $dbh->bz_add_column('products', 'votestoconfirm',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
-
- $dbh->bz_add_column('bugs', 'votes',
- {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0});
- $dbh->bz_add_index('bugs', 'bugs_votes_idx', ['votes']);
-
- # maxvotesperbug used to default to 10,000, which isn't very sensible.
- my $per_bug = $dbh->bz_column_info('products', 'maxvotesperbug');
- if ($per_bug->{DEFAULT} != DEFAULT_VOTES_PER_BUG) {
- $dbh->bz_alter_column('products', 'maxvotesperbug',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
- }
+ my $dbh = Bugzilla->dbh;
+
+ # Note that before Bugzilla 4.0, voting was a built-in part of Bugzilla,
+ # so updates to the columns for old versions of Bugzilla happen in
+ # Bugzilla::Install::DB, and can't safely be moved to this extension.
+
+ my $field = new Bugzilla::Field({name => 'votes'});
+ if (!$field) {
+ Bugzilla::Field->create(
+ {name => 'votes', description => 'Votes', buglist => 1});
+ }
+
+ $dbh->bz_add_column('products', 'votesperuser',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_column('products', 'maxvotesperbug',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
+ $dbh->bz_add_column('products', 'votestoconfirm',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+
+ $dbh->bz_add_column('bugs', 'votes',
+ {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_index('bugs', 'bugs_votes_idx', ['votes']);
+
+ # maxvotesperbug used to default to 10,000, which isn't very sensible.
+ my $per_bug = $dbh->bz_column_info('products', 'maxvotesperbug');
+ if ($per_bug->{DEFAULT} != DEFAULT_VOTES_PER_BUG) {
+ $dbh->bz_alter_column('products', 'maxvotesperbug',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
+ }
}
###########
@@ -110,102 +112,108 @@ sub install_update_db {
###########
sub _bug_user_votes {
- my ($self) = @_;
- return $self->{'user_votes'} if exists $self->{'user_votes'};
- $self->{'user_votes'} = Bugzilla->dbh->selectrow_array(
- "SELECT vote_count FROM votes WHERE bug_id = ? AND who = ?",
- undef, $self->id, Bugzilla->user->id);
- return $self->{'user_votes'};
+ my ($self) = @_;
+ return $self->{'user_votes'} if exists $self->{'user_votes'};
+ $self->{'user_votes'}
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT vote_count FROM votes WHERE bug_id = ? AND who = ?",
+ undef, $self->id, Bugzilla->user->id);
+ return $self->{'user_votes'};
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Bug')) {
- push(@$columns, 'votes');
- }
- elsif ($class->isa('Bugzilla::Product')) {
- push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Bug')) {
+ push(@$columns, 'votes');
+ }
+ elsif ($class->isa('Bugzilla::Product')) {
+ push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
+ }
}
sub bug_fields {
- my ($self, $args) = @_;
- my $fields = $args->{fields};
- push(@$fields, 'votes');
+ my ($self, $args) = @_;
+ my $fields = $args->{fields};
+ push(@$fields, 'votes');
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
- if ($object->isa('Bugzilla::Product')) {
- push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
- }
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
+ if ($object->isa('Bugzilla::Product')) {
+ push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
+ }
}
sub object_validators {
- my ($self, $args) = @_;
- my ($class, $validators) = @$args{qw(class validators)};
- if ($class->isa('Bugzilla::Product')) {
- $validators->{'votesperuser'} = \&_check_votesperuser;
- $validators->{'maxvotesperbug'} = \&_check_maxvotesperbug;
- $validators->{'votestoconfirm'} = \&_check_votestoconfirm;
- }
+ my ($self, $args) = @_;
+ my ($class, $validators) = @$args{qw(class validators)};
+ if ($class->isa('Bugzilla::Product')) {
+ $validators->{'votesperuser'} = \&_check_votesperuser;
+ $validators->{'maxvotesperbug'} = \&_check_maxvotesperbug;
+ $validators->{'votestoconfirm'} = \&_check_votestoconfirm;
+ }
}
sub object_before_create {
- my ($self, $args) = @_;
- my ($class, $params) = @$args{qw(class params)};
- if ($class->isa('Bugzilla::Bug')) {
- # Don't ever allow people to directly specify "votes" into the bugs
- # table.
- delete $params->{votes};
- }
- elsif ($class->isa('Bugzilla::Product')) {
- my $input = Bugzilla->input_params;
- $params->{votesperuser} = $input->{'votesperuser'};
- $params->{maxvotesperbug} = $input->{'maxvotesperbug'};
- $params->{votestoconfirm} = $input->{'votestoconfirm'};
- }
+ my ($self, $args) = @_;
+ my ($class, $params) = @$args{qw(class params)};
+ if ($class->isa('Bugzilla::Bug')) {
+
+ # Don't ever allow people to directly specify "votes" into the bugs
+ # table.
+ delete $params->{votes};
+ }
+ elsif ($class->isa('Bugzilla::Product')) {
+ my $input = Bugzilla->input_params;
+ $params->{votesperuser} = $input->{'votesperuser'};
+ $params->{maxvotesperbug} = $input->{'maxvotesperbug'};
+ $params->{votestoconfirm} = $input->{'votestoconfirm'};
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my ($object) = $args->{object};
- if ($object->isa('Bugzilla::Product')) {
- my $input = Bugzilla->input_params;
- $object->set('votesperuser', $input->{'votesperuser'});
- $object->set('maxvotesperbug', $input->{'maxvotesperbug'});
- $object->set('votestoconfirm', $input->{'votestoconfirm'});
- }
+ my ($self, $args) = @_;
+ my ($object) = $args->{object};
+ if ($object->isa('Bugzilla::Product')) {
+ my $input = Bugzilla->input_params;
+ $object->set('votesperuser', $input->{'votesperuser'});
+ $object->set('maxvotesperbug', $input->{'maxvotesperbug'});
+ $object->set('votestoconfirm', $input->{'votestoconfirm'});
+ }
}
sub object_end_of_update {
- my ($self, $args) = @_;
- my ($object, $changes) = @$args{qw(object changes)};
- if ( $object->isa('Bugzilla::Product')
- and ($changes->{maxvotesperbug} or $changes->{votesperuser}
- or $changes->{votestoconfirm}) )
- {
- _modify_bug_votes($object, $changes);
- }
+ my ($self, $args) = @_;
+ my ($object, $changes) = @$args{qw(object changes)};
+ if (
+ $object->isa('Bugzilla::Product')
+ and ($changes->{maxvotesperbug}
+ or $changes->{votesperuser}
+ or $changes->{votestoconfirm})
+ )
+ {
+ _modify_bug_votes($object, $changes);
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- my ($bug, $changes) = @$args{qw(bug changes)};
-
- if ($changes->{'product'}) {
- my @msgs;
- # If some votes have been removed, RemoveVotes() returns
- # a list of messages to send to voters.
- @msgs = _remove_votes($bug->id, 0, 'votes_bug_moved');
- _confirm_if_vote_confirmed($bug);
-
- foreach my $msg (@msgs) {
- MessageToMTA($msg);
- }
+ my ($self, $args) = @_;
+ my ($bug, $changes) = @$args{qw(bug changes)};
+
+ if ($changes->{'product'}) {
+ my @msgs;
+
+ # If some votes have been removed, RemoveVotes() returns
+ # a list of messages to send to voters.
+ @msgs = _remove_votes($bug->id, 0, 'votes_bug_moved');
+ _confirm_if_vote_confirmed($bug);
+
+ foreach my $msg (@msgs) {
+ MessageToMTA($msg);
}
+ }
}
#############
@@ -213,27 +221,28 @@ sub bug_end_of_update {
#############
sub template_before_create {
- my ($self, $args) = @_;
- my $config = $args->{config};
- my $constants = $config->{VARIABLES}{constants};
- $constants->{REL_VOTER} = REL_VOTER;
- $constants->{CMT_POPULAR_VOTES} = CMT_POPULAR_VOTES;
- $constants->{DEFAULT_VOTES_PER_BUG} = DEFAULT_VOTES_PER_BUG;
+ my ($self, $args) = @_;
+ my $config = $args->{config};
+ my $constants = $config->{VARIABLES}{constants};
+ $constants->{REL_VOTER} = REL_VOTER;
+ $constants->{CMT_POPULAR_VOTES} = CMT_POPULAR_VOTES;
+ $constants->{DEFAULT_VOTES_PER_BUG} = DEFAULT_VOTES_PER_BUG;
}
sub template_before_process {
- my ($self, $args) = @_;
- my ($vars, $file) = @$args{qw(vars file)};
- if ($file eq 'admin/users/confirm-delete.html.tmpl') {
- my $who = $vars->{otheruser};
- my $votes = Bugzilla->dbh->selectrow_array(
- 'SELECT COUNT(*) FROM votes WHERE who = ?', undef, $who->id);
- if ($votes) {
- $vars->{other_safe} = 1;
- $vars->{votes} = $votes;
- }
- }
+ my ($self, $args) = @_;
+ my ($vars, $file) = @$args{qw(vars file)};
+ if ($file eq 'admin/users/confirm-delete.html.tmpl') {
+ my $who = $vars->{otheruser};
+ my $votes
+ = Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM votes WHERE who = ?',
+ undef, $who->id);
+ if ($votes) {
+ $vars->{other_safe} = 1;
+ $vars->{votes} = $votes;
+ }
+ }
}
###########
@@ -241,19 +250,19 @@ sub template_before_process {
###########
sub bugmail_recipients {
- my ($self, $args) = @_;
- my ($bug, $recipients) = @$args{qw(bug recipients)};
- my $dbh = Bugzilla->dbh;
+ my ($self, $args) = @_;
+ my ($bug, $recipients) = @$args{qw(bug recipients)};
+ my $dbh = Bugzilla->dbh;
- my $voters = $dbh->selectcol_arrayref(
- "SELECT who FROM votes WHERE bug_id = ?", undef, $bug->id);
- $recipients->{$_}->{+REL_VOTER} = 1 foreach (@$voters);
+ my $voters = $dbh->selectcol_arrayref("SELECT who FROM votes WHERE bug_id = ?",
+ undef, $bug->id);
+ $recipients->{$_}->{+REL_VOTER} = 1 foreach (@$voters);
}
sub bugmail_relationships {
- my ($self, $args) = @_;
- my $relationships = $args->{relationships};
- $relationships->{+REL_VOTER} = 'Voter';
+ my ($self, $args) = @_;
+ my $relationships = $args->{relationships};
+ $relationships->{+REL_VOTER} = 'Voter';
}
###############
@@ -261,59 +270,59 @@ sub bugmail_relationships {
###############
sub sanitycheck_check {
- my ($self, $args) = @_;
- my $status = $args->{status};
-
- # Vote Cache
- $status->('voting_count_start');
- my $dbh = Bugzilla->dbh;
- my %cached_counts = @{ $dbh->selectcol_arrayref(
- 'SELECT bug_id, votes FROM bugs', {Columns=>[1,2]}) };
-
- my %real_counts = @{ $dbh->selectcol_arrayref(
- 'SELECT bug_id, SUM(vote_count) FROM votes '
- . $dbh->sql_group_by('bug_id'), {Columns=>[1,2]}) };
-
- my $needs_rebuild;
- foreach my $id (keys %cached_counts) {
- my $cached_count = $cached_counts{$id};
- my $real_count = $real_counts{$id} || 0;
- if ($cached_count < 0) {
- $status->('voting_count_alert', { id => $id }, 'alert');
- }
- elsif ($cached_count != $real_count) {
- $status->('voting_cache_alert', { id => $id }, 'alert');
- $needs_rebuild = 1;
- }
- }
-
- $status->('voting_cache_rebuild_fix') if $needs_rebuild;
+ my ($self, $args) = @_;
+ my $status = $args->{status};
+
+ # Vote Cache
+ $status->('voting_count_start');
+ my $dbh = Bugzilla->dbh;
+ my %cached_counts = @{$dbh->selectcol_arrayref('SELECT bug_id, votes FROM bugs',
+ {Columns => [1, 2]})};
+
+ my %real_counts = @{
+ $dbh->selectcol_arrayref(
+ 'SELECT bug_id, SUM(vote_count) FROM votes ' . $dbh->sql_group_by('bug_id'),
+ {Columns => [1, 2]})
+ };
+
+ my $needs_rebuild;
+ foreach my $id (keys %cached_counts) {
+ my $cached_count = $cached_counts{$id};
+ my $real_count = $real_counts{$id} || 0;
+ if ($cached_count < 0) {
+ $status->('voting_count_alert', {id => $id}, 'alert');
+ }
+ elsif ($cached_count != $real_count) {
+ $status->('voting_cache_alert', {id => $id}, 'alert');
+ $needs_rebuild = 1;
+ }
+ }
+
+ $status->('voting_cache_rebuild_fix') if $needs_rebuild;
}
sub sanitycheck_repair {
- my ($self, $args) = @_;
- my $status = $args->{status};
- my $input = Bugzilla->input_params;
- my $dbh = Bugzilla->dbh;
-
- return if !$input->{rebuild_vote_cache};
-
- $status->('voting_cache_rebuild_start');
- $dbh->bz_start_transaction();
- $dbh->do('UPDATE bugs SET votes = 0');
-
- my $sth = $dbh->prepare(
- 'SELECT bug_id, SUM(vote_count) FROM votes '
- . $dbh->sql_group_by('bug_id'));
- $sth->execute();
-
- my $sth_update = $dbh->prepare(
- 'UPDATE bugs SET votes = ? WHERE bug_id = ?');
- while (my ($id, $count) = $sth->fetchrow_array) {
- $sth_update->execute($count, $id);
- }
- $dbh->bz_commit_transaction();
- $status->('voting_cache_rebuild_end');
+ my ($self, $args) = @_;
+ my $status = $args->{status};
+ my $input = Bugzilla->input_params;
+ my $dbh = Bugzilla->dbh;
+
+ return if !$input->{rebuild_vote_cache};
+
+ $status->('voting_cache_rebuild_start');
+ $dbh->bz_start_transaction();
+ $dbh->do('UPDATE bugs SET votes = 0');
+
+ my $sth = $dbh->prepare(
+ 'SELECT bug_id, SUM(vote_count) FROM votes ' . $dbh->sql_group_by('bug_id'));
+ $sth->execute();
+
+ my $sth_update = $dbh->prepare('UPDATE bugs SET votes = ? WHERE bug_id = ?');
+ while (my ($id, $count) = $sth->fetchrow_array) {
+ $sth_update->execute($count, $id);
+ }
+ $dbh->bz_commit_transaction();
+ $status->('voting_cache_rebuild_end');
}
@@ -322,35 +331,36 @@ sub sanitycheck_repair {
##############
sub _check_votesperuser {
- return _check_votes(0, @_);
+ return _check_votes(0, @_);
}
sub _check_maxvotesperbug {
- return _check_votes(DEFAULT_VOTES_PER_BUG, @_);
+ return _check_votes(DEFAULT_VOTES_PER_BUG, @_);
}
sub _check_votestoconfirm {
- return _check_votes(0, @_);
+ return _check_votes(0, @_);
}
# This subroutine is only used internally by other _check_votes_* validators.
sub _check_votes {
- my ($default, $invocant, $votes, $field) = @_;
-
- detaint_natural($votes) if defined $votes;
- # On product creation, if the number of votes is not a valid integer,
- # we silently fall back to the given default value.
- # If the product already exists and the change is illegal, we complain.
- if (!defined $votes) {
- if (ref $invocant) {
- ThrowUserError('voting_product_illegal_votes',
- { field => $field, votes => $_[2] });
- }
- else {
- $votes = $default;
- }
+ my ($default, $invocant, $votes, $field) = @_;
+
+ detaint_natural($votes) if defined $votes;
+
+ # On product creation, if the number of votes is not a valid integer,
+ # we silently fall back to the given default value.
+ # If the product already exists and the change is illegal, we complain.
+ if (!defined $votes) {
+ if (ref $invocant) {
+ ThrowUserError('voting_product_illegal_votes',
+ {field => $field, votes => $_[2]});
}
- return $votes;
+ else {
+ $votes = $default;
+ }
+ }
+ return $votes;
}
#########
@@ -358,291 +368,322 @@ sub _check_votes {
#########
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{page_id};
- my $vars = $args->{vars};
-
- if ($page =~ m{^voting/bug\.}) {
- _page_bug($vars);
- }
- elsif ($page =~ m{^voting/user\.}) {
- _page_user($vars);
- }
+ my ($self, $args) = @_;
+ my $page = $args->{page_id};
+ my $vars = $args->{vars};
+
+ if ($page =~ m{^voting/bug\.}) {
+ _page_bug($vars);
+ }
+ elsif ($page =~ m{^voting/user\.}) {
+ _page_user($vars);
+ }
}
sub _page_bug {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $input = Bugzilla->input_params;
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
- my $bug_id = $input->{bug_id};
- my $bug = Bugzilla::Bug->check($bug_id);
+ my $bug_id = $input->{bug_id};
+ my $bug = Bugzilla::Bug->check($bug_id);
- $vars->{'bug'} = $bug;
- $vars->{'users'} =
- $dbh->selectall_arrayref('SELECT profiles.login_name,
+ $vars->{'bug'} = $bug;
+ $vars->{'users'} = $dbh->selectall_arrayref(
+ 'SELECT profiles.login_name,
profiles.userid AS id,
votes.vote_count
FROM votes
INNER JOIN profiles
ON profiles.userid = votes.who
- WHERE votes.bug_id = ?',
- {Slice=>{}}, $bug->id);
+ WHERE votes.bug_id = ?', {Slice => {}}, $bug->id
+ );
}
sub _page_user {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $input = Bugzilla->input_params;
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $input = Bugzilla->input_params;
- my $action = $input->{action};
- if ($action and $action eq 'vote') {
- _update_votes($vars);
- }
+ my $action = $input->{action};
+ if ($action and $action eq 'vote') {
+ _update_votes($vars);
+ }
- # If a bug_id is given, and we're editing, we'll add it to the votes list.
+ # If a bug_id is given, and we're editing, we'll add it to the votes list.
- my $bug_id = $input->{bug_id};
- $bug_id = $bug_id->[0] if ref($bug_id) eq 'ARRAY';
- my $bug = Bugzilla::Bug->check({ id => $bug_id, cache => 1 }) if $bug_id;
- my $who_id = $input->{user_id} || $user->id;
+ my $bug_id = $input->{bug_id};
+ $bug_id = $bug_id->[0] if ref($bug_id) eq 'ARRAY';
+ my $bug = Bugzilla::Bug->check({id => $bug_id, cache => 1}) if $bug_id;
+ my $who_id = $input->{user_id} || $user->id;
- # Logged-out users must specify a user_id.
- Bugzilla->login(LOGIN_REQUIRED) if !$who_id;
+ # Logged-out users must specify a user_id.
+ Bugzilla->login(LOGIN_REQUIRED) if !$who_id;
- my $who = Bugzilla::User->check({ id => $who_id });
+ my $who = Bugzilla::User->check({id => $who_id});
- my $canedit = $user->id == $who->id;
+ my $canedit = $user->id == $who->id;
- $dbh->bz_start_transaction();
+ $dbh->bz_start_transaction();
- if ($canedit && $bug) {
- # Make sure there is an entry for this bug
- # in the vote table, just so that things display right.
- my $has_votes = $dbh->selectrow_array('SELECT vote_count FROM votes
- WHERE bug_id = ? AND who = ?',
- undef, ($bug->id, $who->id));
- if (!$has_votes) {
- $dbh->do('INSERT INTO votes (who, bug_id, vote_count)
- VALUES (?, ?, 0)', undef, ($who->id, $bug->id));
- }
+ if ($canedit && $bug) {
+
+ # Make sure there is an entry for this bug
+ # in the vote table, just so that things display right.
+ my $has_votes = $dbh->selectrow_array(
+ 'SELECT vote_count FROM votes
+ WHERE bug_id = ? AND who = ?', undef,
+ ($bug->id, $who->id)
+ );
+ if (!$has_votes) {
+ $dbh->do(
+ 'INSERT INTO votes (who, bug_id, vote_count)
+ VALUES (?, ?, 0)', undef, ($who->id, $bug->id)
+ );
}
+ }
- my (@products, @all_bug_ids);
- # Read the votes data for this user for each product.
- foreach my $product (@{ $user->get_selectable_products }) {
- next unless ($product->{votesperuser} > 0);
+ my (@products, @all_bug_ids);
- my $vote_list =
- $dbh->selectall_arrayref('SELECT votes.bug_id, votes.vote_count
+ # Read the votes data for this user for each product.
+ foreach my $product (@{$user->get_selectable_products}) {
+ next unless ($product->{votesperuser} > 0);
+
+ my $vote_list = $dbh->selectall_arrayref(
+ 'SELECT votes.bug_id, votes.vote_count
FROM votes
INNER JOIN bugs
ON votes.bug_id = bugs.bug_id
WHERE votes.who = ?
- AND bugs.product_id = ?',
- undef, ($who->id, $product->id));
-
- my %votes = map { $_->[0] => $_->[1] } @$vote_list;
- my @bug_ids = sort keys %votes;
- # Exclude bugs that the user can no longer see.
- @bug_ids = @{ $user->visible_bugs(\@bug_ids) };
- next unless scalar @bug_ids;
-
- push(@all_bug_ids, @bug_ids);
- my @bugs = @{ Bugzilla::Bug->new_from_list(\@bug_ids) };
- $_->{count} = $votes{$_->id} foreach @bugs;
- # We include votes from bugs that the user can no longer see.
- my $total = sum(values %votes) || 0;
-
- my $onevoteonly = 0;
- $onevoteonly = 1 if (min($product->{votesperuser},
- $product->{maxvotesperbug}) == 1);
-
- push(@products, { name => $product->name,
- bugs => \@bugs,
- bug_ids => \@bug_ids,
- onevoteonly => $onevoteonly,
- total => $total,
- maxvotes => $product->{votesperuser},
- maxperbug => $product->{maxvotesperbug} });
- }
-
- if ($canedit && $bug) {
- $dbh->do('DELETE FROM votes WHERE vote_count = 0 AND who = ?',
- undef, $who->id);
- }
- $dbh->bz_commit_transaction();
-
- $vars->{'canedit'} = $canedit;
- $vars->{'voting_user'} = { "login" => $who->name };
- $vars->{'products'} = \@products;
- $vars->{'this_bug'} = $bug;
- $vars->{'all_bug_ids'} = \@all_bug_ids;
+ AND bugs.product_id = ?', undef,
+ ($who->id, $product->id)
+ );
+
+ my %votes = map { $_->[0] => $_->[1] } @$vote_list;
+ my @bug_ids = sort keys %votes;
+
+ # Exclude bugs that the user can no longer see.
+ @bug_ids = @{$user->visible_bugs(\@bug_ids)};
+ next unless scalar @bug_ids;
+
+ push(@all_bug_ids, @bug_ids);
+ my @bugs = @{Bugzilla::Bug->new_from_list(\@bug_ids)};
+ $_->{count} = $votes{$_->id} foreach @bugs;
+
+ # We include votes from bugs that the user can no longer see.
+ my $total = sum(values %votes) || 0;
+
+ my $onevoteonly = 0;
+ $onevoteonly = 1
+ if (min($product->{votesperuser}, $product->{maxvotesperbug}) == 1);
+
+ push(
+ @products,
+ {
+ name => $product->name,
+ bugs => \@bugs,
+ bug_ids => \@bug_ids,
+ onevoteonly => $onevoteonly,
+ total => $total,
+ maxvotes => $product->{votesperuser},
+ maxperbug => $product->{maxvotesperbug}
+ }
+ );
+ }
+
+ if ($canedit && $bug) {
+ $dbh->do('DELETE FROM votes WHERE vote_count = 0 AND who = ?', undef, $who->id);
+ }
+ $dbh->bz_commit_transaction();
+
+ $vars->{'canedit'} = $canedit;
+ $vars->{'voting_user'} = {"login" => $who->name};
+ $vars->{'products'} = \@products;
+ $vars->{'this_bug'} = $bug;
+ $vars->{'all_bug_ids'} = \@all_bug_ids;
}
sub _update_votes {
- my ($vars) = @_;
-
- ############################################################################
- # Begin Data/Security Validation
- ############################################################################
-
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- my $input = Bugzilla->input_params;
-
- # Build a list of bug IDs for which votes have been submitted. Votes
- # are submitted in form fields in which the field names are the bug
- # IDs and the field values are the number of votes.
-
- my @buglist = grep {/^\d+$/} keys %$input;
- my (%bugs, %votes);
-
- # If no bugs are in the buglist, let's make sure the user gets notified
- # that their votes will get nuked if they continue.
- if (scalar(@buglist) == 0) {
- if (!defined $cgi->param('delete_all_votes')) {
- print $cgi->header();
- $template->process("voting/delete-all.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
- elsif ($cgi->param('delete_all_votes') == 0) {
- print $cgi->redirect("page.cgi?id=voting/user.html");
- exit;
+ my ($vars) = @_;
+
+ ############################################################################
+ # Begin Data/Security Validation
+ ############################################################################
+
+ my $cgi = Bugzilla->cgi;
+ my $dbh = Bugzilla->dbh;
+ my $template = Bugzilla->template;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $input = Bugzilla->input_params;
+
+ # Build a list of bug IDs for which votes have been submitted. Votes
+ # are submitted in form fields in which the field names are the bug
+ # IDs and the field values are the number of votes.
+
+ my @buglist = grep {/^\d+$/} keys %$input;
+ my (%bugs, %votes);
+
+ # If no bugs are in the buglist, let's make sure the user gets notified
+ # that their votes will get nuked if they continue.
+ if (scalar(@buglist) == 0) {
+ if (!defined $cgi->param('delete_all_votes')) {
+ print $cgi->header();
+ $template->process("voting/delete-all.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+ elsif ($cgi->param('delete_all_votes') == 0) {
+ print $cgi->redirect("page.cgi?id=voting/user.html");
+ exit;
+ }
+ }
+ else {
+ $user->visible_bugs(\@buglist);
+ my $bugs_obj = Bugzilla::Bug->new_from_list(\@buglist);
+ $bugs{$_->id} = $_ foreach @$bugs_obj;
+ }
+
+ # Call check_is_visible() on each bug to make sure it is an existing bug
+ # that the user is authorized to access, and make sure the number of votes
+ # submitted is also an integer.
+ foreach my $id (@buglist) {
+ my $bug = $bugs{$id}
+ or ThrowUserError('bug_id_does_not_exist', {bug_id => $id});
+ $bug->check_is_visible;
+ $id = $bug->id;
+ $votes{$id} = $input->{$id};
+ detaint_natural($votes{$id}) || ThrowUserError("voting_must_be_nonnegative");
+ }
+
+ my $token = $cgi->param('token');
+ check_hash_token($token, ['vote']);
+
+ ############################################################################
+ # End Data/Security Validation
+ ############################################################################
+ my $who = $user->id;
+
+ # If the user is voting for bugs, make sure they aren't overstuffing
+ # the ballot box.
+ if (scalar @buglist) {
+ my (%prodcount, %products);
+ foreach my $bug_id (keys %bugs) {
+ my $bug = $bugs{$bug_id};
+ my $prod = $bug->product;
+ $products{$prod} ||= $bug->product_obj;
+ $prodcount{$prod} ||= 0;
+ $prodcount{$prod} += $votes{$bug_id};
+
+ # Make sure we haven't broken the votes-per-bug limit
+ ($votes{$bug_id} <= $products{$prod}->{maxvotesperbug}) || ThrowUserError(
+ "voting_too_many_votes_for_bug",
+ {
+ max => $products{$prod}->{maxvotesperbug},
+ product => $prod,
+ votes => $votes{$bug_id}
}
- }
- else {
- $user->visible_bugs(\@buglist);
- my $bugs_obj = Bugzilla::Bug->new_from_list(\@buglist);
- $bugs{$_->id} = $_ foreach @$bugs_obj;
+ );
}
- # Call check_is_visible() on each bug to make sure it is an existing bug
- # that the user is authorized to access, and make sure the number of votes
- # submitted is also an integer.
- foreach my $id (@buglist) {
- my $bug = $bugs{$id}
- or ThrowUserError('bug_id_does_not_exist', { bug_id => $id });
- $bug->check_is_visible;
- $id = $bug->id;
- $votes{$id} = $input->{$id};
- detaint_natural($votes{$id})
- || ThrowUserError("voting_must_be_nonnegative");
- }
-
- my $token = $cgi->param('token');
- check_hash_token($token, ['vote']);
-
- ############################################################################
- # End Data/Security Validation
- ############################################################################
- my $who = $user->id;
-
- # If the user is voting for bugs, make sure they aren't overstuffing
- # the ballot box.
- if (scalar @buglist) {
- my (%prodcount, %products);
- foreach my $bug_id (keys %bugs) {
- my $bug = $bugs{$bug_id};
- my $prod = $bug->product;
- $products{$prod} ||= $bug->product_obj;
- $prodcount{$prod} ||= 0;
- $prodcount{$prod} += $votes{$bug_id};
-
- # Make sure we haven't broken the votes-per-bug limit
- ($votes{$bug_id} <= $products{$prod}->{maxvotesperbug})
- || ThrowUserError("voting_too_many_votes_for_bug",
- {max => $products{$prod}->{maxvotesperbug},
- product => $prod,
- votes => $votes{$bug_id}});
- }
-
- # Make sure we haven't broken the votes-per-product limit
- foreach my $prod (keys(%prodcount)) {
- ($prodcount{$prod} <= $products{$prod}->{votesperuser})
- || ThrowUserError("voting_too_many_votes_for_product",
- {max => $products{$prod}->{votesperuser},
- product => $prod,
- votes => $prodcount{$prod}});
+ # Make sure we haven't broken the votes-per-product limit
+ foreach my $prod (keys(%prodcount)) {
+ ($prodcount{$prod} <= $products{$prod}->{votesperuser}) || ThrowUserError(
+ "voting_too_many_votes_for_product",
+ {
+ max => $products{$prod}->{votesperuser},
+ product => $prod,
+ votes => $prodcount{$prod}
}
+ );
}
+ }
- # Update the user's votes in the database.
- $dbh->bz_start_transaction();
+ # Update the user's votes in the database.
+ $dbh->bz_start_transaction();
- my $old_list = $dbh->selectall_arrayref('SELECT bug_id, vote_count FROM votes
- WHERE who = ?', undef, $who);
+ my $old_list = $dbh->selectall_arrayref(
+ 'SELECT bug_id, vote_count FROM votes
+ WHERE who = ?', undef, $who
+ );
- my %old_votes = map { $_->[0] => $_->[1] } @$old_list;
+ my %old_votes = map { $_->[0] => $_->[1] } @$old_list;
- my $sth_insertVotes = $dbh->prepare('INSERT INTO votes (who, bug_id, vote_count)
- VALUES (?, ?, ?)');
- my $sth_updateVotes = $dbh->prepare('UPDATE votes SET vote_count = ?
- WHERE bug_id = ? AND who = ?');
+ my $sth_insertVotes = $dbh->prepare(
+ 'INSERT INTO votes (who, bug_id, vote_count)
+ VALUES (?, ?, ?)'
+ );
+ my $sth_updateVotes = $dbh->prepare(
+ 'UPDATE votes SET vote_count = ?
+ WHERE bug_id = ? AND who = ?'
+ );
- my %affected = map { $_ => 1 } (@buglist, keys %old_votes);
- my @deleted_votes;
-
- foreach my $id (keys %affected) {
- if (!$votes{$id}) {
- push(@deleted_votes, $id);
- next;
- }
- if ($votes{$id} == ($old_votes{$id} || 0)) {
- delete $affected{$id};
- next;
- }
- # We use 'defined' in case 0 was accidentally stored in the DB.
- if (defined $old_votes{$id}) {
- $sth_updateVotes->execute($votes{$id}, $id, $who);
- }
- else {
- $sth_insertVotes->execute($who, $id, $votes{$id});
- }
- }
+ my %affected = map { $_ => 1 } (@buglist, keys %old_votes);
+ my @deleted_votes;
- if (@deleted_votes) {
- $dbh->do('DELETE FROM votes WHERE who = ? AND ' .
- $dbh->sql_in('bug_id', \@deleted_votes), undef, $who);
+ foreach my $id (keys %affected) {
+ if (!$votes{$id}) {
+ push(@deleted_votes, $id);
+ next;
}
-
- # Update the cached values in the bugs table
- my @updated_bugs = ();
-
- my $sth_getVotes = $dbh->prepare("SELECT SUM(vote_count) FROM votes
- WHERE bug_id = ?");
-
- $sth_updateVotes = $dbh->prepare('UPDATE bugs SET votes = ? WHERE bug_id = ?');
-
- foreach my $id (keys %affected) {
- $sth_getVotes->execute($id);
- my $v = $sth_getVotes->fetchrow_array || 0;
- $sth_updateVotes->execute($v, $id);
- $bugs{$id}->{votes} = $v if $bugs{$id};
- my $confirmed = _confirm_if_vote_confirmed($bugs{$id} || $id);
- push (@updated_bugs, $id) if $confirmed;
+ if ($votes{$id} == ($old_votes{$id} || 0)) {
+ delete $affected{$id};
+ next;
}
- $dbh->bz_commit_transaction();
-
- print $cgi->header() if scalar @updated_bugs;
- $vars->{'type'} = "votes";
- $vars->{'title_tag'} = 'change_votes';
- foreach my $bug_id (@updated_bugs) {
- $vars->{'id'} = $bug_id;
- $vars->{'sent_bugmail'} =
- Bugzilla::BugMail::Send($bug_id, { 'changer' => $user });
-
- $template->process("bug/process/results.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
- # Set header_done to 1 only after the first bug.
- $vars->{'header_done'} = 1;
+ # We use 'defined' in case 0 was accidentally stored in the DB.
+ if (defined $old_votes{$id}) {
+ $sth_updateVotes->execute($votes{$id}, $id, $who);
}
- $vars->{'votes_recorded'} = 1;
+ else {
+ $sth_insertVotes->execute($who, $id, $votes{$id});
+ }
+ }
+
+ if (@deleted_votes) {
+ $dbh->do(
+ 'DELETE FROM votes WHERE who = ? AND '
+ . $dbh->sql_in('bug_id', \@deleted_votes),
+ undef, $who
+ );
+ }
+
+ # Update the cached values in the bugs table
+ my @updated_bugs = ();
+
+ my $sth_getVotes = $dbh->prepare(
+ "SELECT SUM(vote_count) FROM votes
+ WHERE bug_id = ?"
+ );
+
+ $sth_updateVotes = $dbh->prepare('UPDATE bugs SET votes = ? WHERE bug_id = ?');
+
+ foreach my $id (keys %affected) {
+ $sth_getVotes->execute($id);
+ my $v = $sth_getVotes->fetchrow_array || 0;
+ $sth_updateVotes->execute($v, $id);
+ $bugs{$id}->{votes} = $v if $bugs{$id};
+ my $confirmed = _confirm_if_vote_confirmed($bugs{$id} || $id);
+ push(@updated_bugs, $id) if $confirmed;
+ }
+
+ $dbh->bz_commit_transaction();
+
+ print $cgi->header() if scalar @updated_bugs;
+ $vars->{'type'} = "votes";
+ $vars->{'title_tag'} = 'change_votes';
+ foreach my $bug_id (@updated_bugs) {
+ $vars->{'id'} = $bug_id;
+ $vars->{'sent_bugmail'}
+ = Bugzilla::BugMail::Send($bug_id, {'changer' => $user});
+
+ $template->process("bug/process/results.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+
+ # Set header_done to 1 only after the first bug.
+ $vars->{'header_done'} = 1;
+ }
+ $vars->{'votes_recorded'} = 1;
}
######################
@@ -650,234 +691,246 @@ sub _update_votes {
######################
sub _modify_bug_votes {
- my ($product, $changes) = @_;
- my $dbh = Bugzilla->dbh;
- my @msgs;
+ my ($product, $changes) = @_;
+ my $dbh = Bugzilla->dbh;
+ my @msgs;
- # 1. too many votes for a single user on a single bug.
- my @toomanyvotes_list;
- if ($product->{maxvotesperbug} < $product->{votesperuser}) {
- my $votes = $dbh->selectall_arrayref(
- 'SELECT votes.who, votes.bug_id
+ # 1. too many votes for a single user on a single bug.
+ my @toomanyvotes_list;
+ if ($product->{maxvotesperbug} < $product->{votesperuser}) {
+ my $votes = $dbh->selectall_arrayref(
+ 'SELECT votes.who, votes.bug_id
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
- AND votes.vote_count > ?',
- undef, ($product->id, $product->{maxvotesperbug}));
+ AND votes.vote_count > ?', undef,
+ ($product->id, $product->{maxvotesperbug})
+ );
+
+ foreach my $vote (@$votes) {
+ my ($who, $id) = (@$vote);
- foreach my $vote (@$votes) {
- my ($who, $id) = (@$vote);
- # If some votes are removed, _remove_votes() returns a list
- # of messages to send to voters.
- push(@msgs, _remove_votes($id, $who, 'votes_too_many_per_bug'));
- my $name = user_id_to_login($who);
+ # If some votes are removed, _remove_votes() returns a list
+ # of messages to send to voters.
+ push(@msgs, _remove_votes($id, $who, 'votes_too_many_per_bug'));
+ my $name = user_id_to_login($who);
- push(@toomanyvotes_list, {id => $id, name => $name});
- }
+ push(@toomanyvotes_list, {id => $id, name => $name});
}
+ }
- $changes->{'_too_many_votes'} = \@toomanyvotes_list;
+ $changes->{'_too_many_votes'} = \@toomanyvotes_list;
- # 2. too many total votes for a single user.
- # This part doesn't work in the general case because _remove_votes
- # doesn't enforce votesperuser (except per-bug when it's less
- # than maxvotesperbug). See _remove_votes().
+ # 2. too many total votes for a single user.
+ # This part doesn't work in the general case because _remove_votes
+ # doesn't enforce votesperuser (except per-bug when it's less
+ # than maxvotesperbug). See _remove_votes().
- my $votes = $dbh->selectall_arrayref(
- 'SELECT votes.who, votes.vote_count
+ my $votes = $dbh->selectall_arrayref(
+ 'SELECT votes.who, votes.vote_count
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
- WHERE bugs.product_id = ?',
- undef, $product->id);
+ WHERE bugs.product_id = ?', undef, $product->id
+ );
- my %counts;
- foreach my $vote (@$votes) {
- my ($who, $count) = @$vote;
- if (!defined $counts{$who}) {
- $counts{$who} = $count;
- } else {
- $counts{$who} += $count;
- }
+ my %counts;
+ foreach my $vote (@$votes) {
+ my ($who, $count) = @$vote;
+ if (!defined $counts{$who}) {
+ $counts{$who} = $count;
+ }
+ else {
+ $counts{$who} += $count;
}
+ }
- my @toomanytotalvotes_list;
- foreach my $who (keys(%counts)) {
- if ($counts{$who} > $product->{votesperuser}) {
- my $bug_ids = $dbh->selectcol_arrayref(
- 'SELECT votes.bug_id
+ my @toomanytotalvotes_list;
+ foreach my $who (keys(%counts)) {
+ if ($counts{$who} > $product->{votesperuser}) {
+ my $bug_ids = $dbh->selectcol_arrayref(
+ 'SELECT votes.bug_id
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
- AND votes.who = ?',
- undef, $product->id, $who);
-
- foreach my $bug_id (@$bug_ids) {
- # _remove_votes returns a list of messages to send
- # in case some voters had too many votes.
- push(@msgs, _remove_votes($bug_id, $who,
- 'votes_too_many_per_user'));
- my $name = user_id_to_login($who);
-
- push(@toomanytotalvotes_list, {id => $bug_id, name => $name});
- }
- }
- }
-
- $changes->{'_too_many_total_votes'} = \@toomanytotalvotes_list;
-
- # 3. enough votes to confirm
- my $bug_list = $dbh->selectcol_arrayref(
- 'SELECT bug_id FROM bugs
- WHERE product_id = ? AND bug_status = ? AND votes >= ?',
- undef, ($product->id, 'UNCONFIRMED', $product->{votestoconfirm}));
-
- my @updated_bugs;
- foreach my $bug_id (@$bug_list) {
- my $confirmed = _confirm_if_vote_confirmed($bug_id, $product);
- push (@updated_bugs, $bug_id) if $confirmed;
- }
- $changes->{'_confirmed_bugs'} = \@updated_bugs;
-
- # Now that changes are done, we can send emails to voters.
- foreach my $msg (@msgs) {
- MessageToMTA($msg);
- }
- # And send out emails about changed bugs
- foreach my $bug_id (@updated_bugs) {
- my $sent_bugmail = Bugzilla::BugMail::Send(
- $bug_id, { changer => Bugzilla->user });
- $changes->{'_confirmed_bugs_sent_bugmail'}->{$bug_id} = $sent_bugmail;
- }
+ AND votes.who = ?', undef, $product->id, $who
+ );
+
+ foreach my $bug_id (@$bug_ids) {
+
+ # _remove_votes returns a list of messages to send
+ # in case some voters had too many votes.
+ push(@msgs, _remove_votes($bug_id, $who, 'votes_too_many_per_user'));
+ my $name = user_id_to_login($who);
+
+ push(@toomanytotalvotes_list, {id => $bug_id, name => $name});
+ }
+ }
+ }
+
+ $changes->{'_too_many_total_votes'} = \@toomanytotalvotes_list;
+
+ # 3. enough votes to confirm
+ my $bug_list = $dbh->selectcol_arrayref(
+ 'SELECT bug_id FROM bugs
+ WHERE product_id = ? AND bug_status = ? AND votes >= ?', undef,
+ ($product->id, 'UNCONFIRMED', $product->{votestoconfirm})
+ );
+
+ my @updated_bugs;
+ foreach my $bug_id (@$bug_list) {
+ my $confirmed = _confirm_if_vote_confirmed($bug_id, $product);
+ push(@updated_bugs, $bug_id) if $confirmed;
+ }
+ $changes->{'_confirmed_bugs'} = \@updated_bugs;
+
+ # Now that changes are done, we can send emails to voters.
+ foreach my $msg (@msgs) {
+ MessageToMTA($msg);
+ }
+
+ # And send out emails about changed bugs
+ foreach my $bug_id (@updated_bugs) {
+ my $sent_bugmail
+ = Bugzilla::BugMail::Send($bug_id, {changer => Bugzilla->user});
+ $changes->{'_confirmed_bugs_sent_bugmail'}->{$bug_id} = $sent_bugmail;
+ }
}
# If a bug is moved to a product which allows less votes per bug
# compared to the previous product, extra votes need to be removed.
sub _remove_votes {
- my ($id, $who, $reason) = (@_);
- my $dbh = Bugzilla->dbh;
-
- my $whopart = ($who) ? " AND votes.who = $who" : "";
-
- my $sth = $dbh->prepare("SELECT profiles.login_name, " .
- "profiles.userid, votes.vote_count, " .
- "products.votesperuser, products.maxvotesperbug " .
- "FROM profiles " .
- "LEFT JOIN votes ON profiles.userid = votes.who " .
- "LEFT JOIN bugs ON votes.bug_id = bugs.bug_id " .
- "LEFT JOIN products ON products.id = bugs.product_id " .
- "WHERE votes.bug_id = ? " . $whopart);
- $sth->execute($id);
- my @list;
- while (my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = $sth->fetchrow_array()) {
- push(@list, [$name, $userid, $oldvotes, $votesperuser, $maxvotesperbug]);
- }
-
- # @messages stores all emails which have to be sent, if any.
- # This array is passed to the caller which will send these emails itself.
- my @messages = ();
-
- if (scalar(@list)) {
- foreach my $ref (@list) {
- my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref);
-
- $maxvotesperbug = min($votesperuser, $maxvotesperbug);
-
- # If this product allows voting and the user's votes are in
- # the acceptable range, then don't do anything.
- next if $votesperuser && $oldvotes <= $maxvotesperbug;
-
- # If the user has more votes on this bug than this product
- # allows, then reduce the number of votes so it fits
- my $newvotes = $maxvotesperbug;
-
- my $removedvotes = $oldvotes - $newvotes;
-
- if ($newvotes) {
- $dbh->do("UPDATE votes SET vote_count = ? " .
- "WHERE bug_id = ? AND who = ?",
- undef, ($newvotes, $id, $userid));
- } else {
- $dbh->do("DELETE FROM votes WHERE bug_id = ? AND who = ?",
- undef, ($id, $userid));
- }
-
- # Notice that we did not make sure that the user fit within the $votesperuser
- # range. This is considered to be an acceptable alternative to losing votes
- # during product moves. Then next time the user attempts to change their votes,
- # they will be forced to fit within the $votesperuser limit.
-
- # 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,
- };
-
- my $voter = new Bugzilla::User($userid);
- my $template = Bugzilla->template_inner($voter->setting('lang'));
-
- my $msg;
- $template->process("voting/votes-removed.txt.tmpl", $vars, \$msg);
- push(@messages, $msg);
- }
-
- my $votes = $dbh->selectrow_array("SELECT SUM(vote_count) " .
- "FROM votes WHERE bug_id = ?",
- undef, $id) || 0;
- $dbh->do("UPDATE bugs SET votes = ? WHERE bug_id = ?",
- undef, ($votes, $id));
- }
- # Now return the array containing emails to be sent.
- return @messages;
+ my ($id, $who, $reason) = (@_);
+ my $dbh = Bugzilla->dbh;
+
+ my $whopart = ($who) ? " AND votes.who = $who" : "";
+
+ my $sth
+ = $dbh->prepare("SELECT profiles.login_name, "
+ . "profiles.userid, votes.vote_count, "
+ . "products.votesperuser, products.maxvotesperbug "
+ . "FROM profiles "
+ . "LEFT JOIN votes ON profiles.userid = votes.who "
+ . "LEFT JOIN bugs ON votes.bug_id = bugs.bug_id "
+ . "LEFT JOIN products ON products.id = bugs.product_id "
+ . "WHERE votes.bug_id = ? "
+ . $whopart);
+ $sth->execute($id);
+ my @list;
+ while (my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug)
+ = $sth->fetchrow_array())
+ {
+ push(@list, [$name, $userid, $oldvotes, $votesperuser, $maxvotesperbug]);
+ }
+
+ # @messages stores all emails which have to be sent, if any.
+ # This array is passed to the caller which will send these emails itself.
+ my @messages = ();
+
+ if (scalar(@list)) {
+ foreach my $ref (@list) {
+ my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref);
+
+ $maxvotesperbug = min($votesperuser, $maxvotesperbug);
+
+ # If this product allows voting and the user's votes are in
+ # the acceptable range, then don't do anything.
+ next if $votesperuser && $oldvotes <= $maxvotesperbug;
+
+ # If the user has more votes on this bug than this product
+ # allows, then reduce the number of votes so it fits
+ my $newvotes = $maxvotesperbug;
+
+ my $removedvotes = $oldvotes - $newvotes;
+
+ if ($newvotes) {
+ $dbh->do("UPDATE votes SET vote_count = ? " . "WHERE bug_id = ? AND who = ?",
+ undef, ($newvotes, $id, $userid));
+ }
+ else {
+ $dbh->do("DELETE FROM votes WHERE bug_id = ? AND who = ?",
+ undef, ($id, $userid));
+ }
+
+ # Notice that we did not make sure that the user fit within the $votesperuser
+ # range. This is considered to be an acceptable alternative to losing votes
+ # during product moves. Then next time the user attempts to change their votes,
+ # they will be forced to fit within the $votesperuser limit.
+
+ # 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,
+ };
+
+ my $voter = new Bugzilla::User($userid);
+ my $template = Bugzilla->template_inner($voter->setting('lang'));
+
+ my $msg;
+ $template->process("voting/votes-removed.txt.tmpl", $vars, \$msg);
+ push(@messages, $msg);
+ }
+
+ my $votes
+ = $dbh->selectrow_array(
+ "SELECT SUM(vote_count) " . "FROM votes WHERE bug_id = ?",
+ undef, $id)
+ || 0;
+ $dbh->do("UPDATE bugs SET votes = ? WHERE bug_id = ?", undef, ($votes, $id));
+ }
+
+ # Now return the array containing emails to be sent.
+ return @messages;
}
# If a user votes for a bug, or the number of votes required to
# confirm a bug has been reduced, check if the bug is now confirmed.
sub _confirm_if_vote_confirmed {
- my ($id, $product) = @_;
- my $bug = ref $id ? $id : new Bugzilla::Bug({ id => $id, cache => 1 });
- $product ||= $bug->product_obj;
-
- my $ret = 0;
- if (!$bug->everconfirmed
- and $product->{votestoconfirm}
- and $bug->votes >= $product->{votestoconfirm})
- {
- $bug->add_comment('', { type => CMT_POPULAR_VOTES });
-
- if ($bug->bug_status eq 'UNCONFIRMED') {
- # Get a valid open state.
- my $new_status;
- foreach my $state (@{$bug->status->can_change_to}) {
- if ($state->is_open && $state->name ne 'UNCONFIRMED') {
- $new_status = $state->name;
- last;
- }
- }
- ThrowCodeError('voting_no_open_bug_status') unless $new_status;
-
- # We cannot call $bug->set_bug_status() here, because a user without
- # canconfirm privs should still be able to confirm a bug by
- # popular vote. We already know the new status is valid, so it's safe.
- $bug->{bug_status} = $new_status;
- $bug->{everconfirmed} = 1;
- delete $bug->{'status'}; # Contains the status object.
- }
- else {
- # If the bug is in a closed state, only set everconfirmed to 1.
- # Do not call $bug->_set_everconfirmed(), for the same reason as above.
- $bug->{everconfirmed} = 1;
+ my ($id, $product) = @_;
+ my $bug = ref $id ? $id : new Bugzilla::Bug({id => $id, cache => 1});
+ $product ||= $bug->product_obj;
+
+ my $ret = 0;
+ if ( !$bug->everconfirmed
+ and $product->{votestoconfirm}
+ and $bug->votes >= $product->{votestoconfirm})
+ {
+ $bug->add_comment('', {type => CMT_POPULAR_VOTES});
+
+ if ($bug->bug_status eq 'UNCONFIRMED') {
+
+ # Get a valid open state.
+ my $new_status;
+ foreach my $state (@{$bug->status->can_change_to}) {
+ if ($state->is_open && $state->name ne 'UNCONFIRMED') {
+ $new_status = $state->name;
+ last;
}
- $bug->update();
+ }
+ ThrowCodeError('voting_no_open_bug_status') unless $new_status;
- $ret = 1;
+ # We cannot call $bug->set_bug_status() here, because a user without
+ # canconfirm privs should still be able to confirm a bug by
+ # popular vote. We already know the new status is valid, so it's safe.
+ $bug->{bug_status} = $new_status;
+ $bug->{everconfirmed} = 1;
+ delete $bug->{'status'}; # Contains the status object.
}
- return $ret;
+ else {
+ # If the bug is in a closed state, only set everconfirmed to 1.
+ # Do not call $bug->_set_everconfirmed(), for the same reason as above.
+ $bug->{everconfirmed} = 1;
+ }
+ $bug->update();
+
+ $ret = 1;
+ }
+ return $ret;
}