2
# -*- Mode: perl; indent-tabs-mode: nil -*-
4
# The contents of this file are subject to the Mozilla Public
5
# License Version 1.1 (the "License"); you may not use this file
6
# except in compliance with the License. You may obtain a copy of
7
# the License at http://www.mozilla.org/MPL/
9
# Software distributed under the License is distributed on an "AS
10
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11
# implied. See the License for the specific language governing
12
# rights and limitations under the License.
14
# The Original Code is the Bugzilla Bug Tracking System.
16
# The Initial Developer of the Original Code is Netscape Communications
17
# Corporation. Portions created by Netscape are
18
# Copyright (C) 1998 Netscape Communications Corporation. All
21
# Contributor(s): Terry Weissman <terry@mozilla.org>
22
# Stephan Niemz <st.n@gmx.net>
23
# Christopher Aillon <christopher@aillon.com>
24
# Gervase Markham <gerv@gerv.net>
25
# Frédéric Buclin <LpSolit@gmail.com>
31
use Bugzilla::Constants;
36
use Bugzilla::Product;
38
use List::Util qw(min);
40
my $cgi = Bugzilla->cgi;
43
# If the action is show_bug, you need a bug_id.
44
# If the action is show_user, you can supply a userid to show the votes for
45
# another user, otherwise you see your own.
46
# If the action is vote, your votes are set to those encoded in the URL as
49
# If no action is defined, we default to show_bug if a bug_id is given,
50
# otherwise to show_user.
51
my $bug_id = $cgi->param('bug_id');
52
my $action = $cgi->param('action') || ($bug_id ? "show_bug" : "show_user");
54
if ($action eq "show_bug" ||
55
($action eq "show_user" && defined $cgi->param('user')))
60
Bugzilla->login(LOGIN_REQUIRED);
63
################################################################################
64
# Begin Data/Security Validation
65
################################################################################
67
# Make sure the bug ID is a positive integer representing an existing
68
# bug that the user is authorized to access.
70
ValidateBugID($bug_id) if defined $bug_id;
72
################################################################################
73
# End Data/Security Validation
74
################################################################################
76
if ($action eq "show_bug") {
79
elsif ($action eq "show_user") {
82
elsif ($action eq "vote") {
83
record_votes() if Bugzilla->params->{'usevotes'};
87
ThrowCodeError("unknown_action", {action => $action});
92
# Display the names of all the people voting for this one bug.
95
my $cgi = Bugzilla->cgi;
96
my $dbh = Bugzilla->dbh;
97
my $template = Bugzilla->template;
99
ThrowCodeError("missing_bug_id") unless defined $bug_id;
101
$vars->{'bug_id'} = $bug_id;
103
$dbh->selectall_arrayref('SELECT profiles.login_name, votes.vote_count
106
ON profiles.userid = votes.who
107
WHERE votes.bug_id = ?',
108
{'Slice' => {}}, $bug_id);
110
print $cgi->header();
111
$template->process("bug/votes/list-for-bug.html.tmpl", $vars)
112
|| ThrowTemplateError($template->error());
115
# Display all the votes for a particular user. If it's the user
116
# doing the viewing, give them the option to edit them too.
119
my $cgi = Bugzilla->cgi;
120
my $dbh = Bugzilla->dbh;
121
my $user = Bugzilla->user;
122
my $template = Bugzilla->template;
124
# If a bug_id is given, and we're editing, we'll add it to the votes list.
127
my $name = $cgi->param('user') || $user->login;
128
my $who = login_to_id($name, THROW_ERROR);
129
my $userid = $user->id;
131
my $canedit = (Bugzilla->params->{'usevotes'} && $userid == $who) ? 1 : 0;
133
$dbh->bz_lock_tables('bugs READ', 'products READ', 'votes WRITE',
134
'cc READ', 'bug_group_map READ', 'user_group_map READ',
135
'group_group_map READ', 'groups READ', 'group_control_map READ');
137
if ($canedit && $bug_id) {
138
# Make sure there is an entry for this bug
139
# in the vote table, just so that things display right.
140
my $has_votes = $dbh->selectrow_array('SELECT vote_count FROM votes
141
WHERE bug_id = ? AND who = ?',
142
undef, ($bug_id, $who));
144
$dbh->do('INSERT INTO votes (who, bug_id, vote_count)
145
VALUES (?, ?, 0)', undef, ($who, $bug_id));
150
my $products = $user->get_selectable_products;
151
# Read the votes data for this user for each product.
152
foreach my $product (@$products) {
153
next unless ($product->votes_per_user > 0);
160
$dbh->selectall_arrayref('SELECT votes.bug_id, votes.vote_count,
164
ON votes.bug_id = bugs.bug_id
166
AND bugs.product_id = ?
167
ORDER BY votes.bug_id',
168
undef, ($who, $product->id));
170
foreach (@$vote_list) {
171
my ($id, $count, $summary) = @$_;
174
# Next if user can't see this bug. So, the totals will be correct
175
# and they can see there are votes 'missing', but not on what bug
176
# they are. This seems a reasonable compromise; the alternative is
177
# to lie in the totals.
178
next if !$user->can_see_bug($id);
180
push (@bugs, { id => $id,
185
$onevoteonly = 1 if (min($product->votes_per_user,
186
$product->max_votes_per_bug) == 1);
188
# Only add the product for display if there are any bugs in it.
190
push (@products, { name => $product->name,
192
onevoteonly => $onevoteonly,
194
maxvotes => $product->votes_per_user,
195
maxperbug => $product->max_votes_per_bug });
199
$dbh->do('DELETE FROM votes WHERE vote_count <= 0');
200
$dbh->bz_unlock_tables();
202
$vars->{'canedit'} = $canedit;
203
$vars->{'voting_user'} = { "login" => $name };
204
$vars->{'products'} = \@products;
205
$vars->{'bug_id'} = $bug_id;
207
print $cgi->header();
208
$template->process("bug/votes/list-for-user.html.tmpl", $vars)
209
|| ThrowTemplateError($template->error());
212
# Update the user's votes in the database.
214
############################################################################
215
# Begin Data/Security Validation
216
############################################################################
218
my $cgi = Bugzilla->cgi;
219
my $dbh = Bugzilla->dbh;
220
my $template = Bugzilla->template;
222
# Build a list of bug IDs for which votes have been submitted. Votes
223
# are submitted in form fields in which the field names are the bug
224
# IDs and the field values are the number of votes.
226
my @buglist = grep {/^[1-9][0-9]*$/} $cgi->param();
228
# If no bugs are in the buglist, let's make sure the user gets notified
229
# that their votes will get nuked if they continue.
230
if (scalar(@buglist) == 0) {
231
if (!defined $cgi->param('delete_all_votes')) {
232
print $cgi->header();
233
$template->process("bug/votes/delete-all.html.tmpl", $vars)
234
|| ThrowTemplateError($template->error());
237
elsif ($cgi->param('delete_all_votes') == 0) {
238
print $cgi->redirect("votes.cgi");
243
# Call ValidateBugID on each bug ID to make sure it is a positive
244
# integer representing an existing bug that the user is authorized
245
# to access, and make sure the number of votes submitted is also
246
# a non-negative integer (a series of digits not preceded by a
249
foreach my $id (@buglist) {
251
$votes{$id} = $cgi->param($id);
252
detaint_natural($votes{$id})
253
|| ThrowUserError("votes_must_be_nonnegative");
256
############################################################################
257
# End Data/Security Validation
258
############################################################################
259
my $who = Bugzilla->user->id;
261
# If the user is voting for bugs, make sure they aren't overstuffing
263
if (scalar(@buglist)) {
266
# XXX - We really need a $bug->product() method.
267
foreach my $bug_id (@buglist) {
268
my $bug = new Bugzilla::Bug($bug_id);
269
my $prod = $bug->product;
270
$products{$prod} ||= new Bugzilla::Product({name => $prod});
271
$prodcount{$prod} ||= 0;
272
$prodcount{$prod} += $votes{$bug_id};
274
# Make sure we haven't broken the votes-per-bug limit
275
($votes{$bug_id} <= $products{$prod}->max_votes_per_bug)
276
|| ThrowUserError("too_many_votes_for_bug",
277
{max => $products{$prod}->max_votes_per_bug,
279
votes => $votes{$bug_id}});
282
# Make sure we haven't broken the votes-per-product limit
283
foreach my $prod (keys(%prodcount)) {
284
($prodcount{$prod} <= $products{$prod}->votes_per_user)
285
|| ThrowUserError("too_many_votes_for_product",
286
{max => $products{$prod}->votes_per_user,
288
votes => $prodcount{$prod}});
292
# Update the user's votes in the database. If the user did not submit
293
# any votes, they may be using a form with checkboxes to remove all their
294
# votes (checkboxes are not submitted along with other form data when
295
# they are not checked, and Bugzilla uses them to represent single votes
296
# for products that only allow one vote per bug). In that case, we still
297
# need to clear the user's votes from the database.
299
$dbh->bz_lock_tables('bugs WRITE', 'bugs_activity WRITE',
300
'votes WRITE', 'longdescs WRITE',
301
'products READ', 'fielddefs READ');
303
# Take note of, and delete the user's old votes from the database.
304
my $bug_list = $dbh->selectcol_arrayref('SELECT bug_id FROM votes
305
WHERE who = ?', undef, $who);
307
foreach my $id (@$bug_list) {
310
$dbh->do('DELETE FROM votes WHERE who = ?', undef, $who);
312
my $sth_insertVotes = $dbh->prepare('INSERT INTO votes (who, bug_id, vote_count)
314
# Insert the new values in their place
315
foreach my $id (@buglist) {
316
if ($votes{$id} > 0) {
317
$sth_insertVotes->execute($who, $id, $votes{$id});
322
# Update the cached values in the bugs table
323
print $cgi->header();
324
my @updated_bugs = ();
326
my $sth_getVotes = $dbh->prepare("SELECT SUM(vote_count) FROM votes
329
my $sth_updateVotes = $dbh->prepare("UPDATE bugs SET votes = ?
332
foreach my $id (keys %affected) {
333
$sth_getVotes->execute($id);
334
my $v = $sth_getVotes->fetchrow_array || 0;
335
$sth_updateVotes->execute($v, $id);
337
my $confirmed = CheckIfVotedConfirmed($id, $who);
338
push (@updated_bugs, $id) if $confirmed;
340
$dbh->bz_unlock_tables();
342
$vars->{'type'} = "votes";
343
$vars->{'mailrecipients'} = { 'changer' => Bugzilla->user->login };
344
$vars->{'title_tag'} = 'change_votes';
346
foreach my $bug_id (@updated_bugs) {
347
$vars->{'id'} = $bug_id;
348
$template->process("bug/process/results.html.tmpl", $vars)
349
|| ThrowTemplateError($template->error());
350
# Set header_done to 1 only after the first bug.
351
$vars->{'header_done'} = 1;
353
$vars->{'votes_recorded'} = 1;