diff --git a/CHANGES b/CHANGES
index 2c5f2994f4e6a3320a7d0d21bf876e83e6b46fd1..b71d84a8714a508cc34d7d7a49c441021d0e0c44 100644
--- a/CHANGES
+++ b/CHANGES
@@ -10,6 +10,13 @@ query the CVS tree.  For example,
 will tell you what has been changed in the last week.
 
 
+10/7/99 Added voting ability.  You must run the new script
+"makevotestable.sh".  You must also feed the following to mysql:
+
+	alter table products add column votesperuser smallint not null;
+
+
+
 9/15/99 Apparently, newer alphas of MySQL won't allow you to have
 "when" as a column name.  So, I have had to rename a column in the
 bugs_activity table.  You must feed the below to mysql or you won't
diff --git a/README b/README
index a335dfd8960767a36aa5fff4e3f68f86dbb3adc1..4030c3d1a5ececb3951b26e4ae7273def1dde2d4 100644
--- a/README
+++ b/README
@@ -282,6 +282,7 @@ command.  Order does not matter, but this one is fine:
 	./makeproducttable.sh
 	./makeprofilestable.sh
 	./makeversiontable.sh
+	./makevotestable.sh
 
 You may want to edit the scripts; once bugs are entered it gets very hard to
 make changes. Think carefully about how you want database users to describe bugs.  Here's one
diff --git a/bug_form.pl b/bug_form.pl
index c563daf06c4b085c7c50ad735c2b14d06e71f871..665d82f62b7eadb3dd50760c1b33e17f61afa623 100644
--- a/bug_form.pl
+++ b/bug_form.pl
@@ -111,7 +111,7 @@ my $id = $::FORM{'id'};
 
 my $query = "
 select
-        bug_id,
+        bugs.bug_id,
         product,
         version,
         rep_platform,
@@ -130,10 +130,12 @@ select
 	status_whiteboard,
         date_format(creation_ts,'Y-m-d'),
         groupset,
-	delta_ts
-from bugs
-where bug_id = $id
-and bugs.groupset & $::usergroupset = bugs.groupset";
+	delta_ts,
+	sum(votes.count)
+from bugs left join votes using(bug_id)
+where bugs.bug_id = $id
+and bugs.groupset & $::usergroupset = bugs.groupset
+group by bugs.bug_id";
 
 SendSQL($query);
 my %bug;
@@ -145,7 +147,7 @@ if (@row = FetchSQLData()) {
 		       "bug_severity", "component", "assigned_to", "reporter",
 		       "bug_file_loc", "short_desc", "target_milestone",
                        "qa_contact", "status_whiteboard", "creation_ts",
-                       "groupset", "delta_ts") {
+                       "groupset", "delta_ts", "votes") {
 	$bug{$field} = shift @row;
 	if (!defined $bug{$field}) {
 	    $bug{$field} = "";
@@ -368,6 +370,16 @@ if (Param("usedependencies")) {
     print "</tr></table>\n";
 }
 
+if ($::prodmaxvotes{$bug{'product'}}) {
+    print qq{
+<table><tr>
+<th><a href="votehelp.html">Votes</a> for bug $id:</th><td>
+<a href="showvotes.cgi?bug_id=$id">$bug{'votes'}</a>
+&nbsp;&nbsp;&nbsp;<a href="showvotes.cgi?voteon=$id">Vote for this bug</a>
+</td></tr></table>
+};
+}
+
 print "
 <br>
 <B>Additional Comments:</B>
diff --git a/buglist.cgi b/buglist.cgi
index d780e2af2d188adaa67ac56e4b1096ebca8cca07..6c8e6ef1693c51694c6fdcf1bfe27c60890f1d12 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -173,6 +173,7 @@ DefCol("version", "substring(bugs.version, 1, 5)", "Vers", "bugs.version");
 DefCol("os", "substring(bugs.op_sys, 1, 4)", "OS", "bugs.op_sys");
 DefCol("target_milestone", "bugs.target_milestone", "TargetM",
        "bugs.target_milestone");
+DefCol("votes", "sum(votes.count)", "Votes", "sum(votes.count)");
 
 my @collist;
 if (defined $::COOKIE{'COLUMNLIST'}) {
@@ -208,11 +209,12 @@ bugs.bug_status";
 
 
 $query .= "
-from   bugs,
+from   bugs left join votes using(bug_id),
        profiles assign,
        profiles report
        left join profiles qacont on bugs.qa_contact = qacont.userid,
        versions projector
+
 where  bugs.assigned_to = assign.userid 
 and    bugs.reporter = report.userid
 and    bugs.product = projector.program
@@ -226,7 +228,7 @@ if ((defined $::FORM{'emailcc1'} && $::FORM{'emailcc1'}) ||
     # We need to poke into the CC table.  Do weird SQL left join stuff so that
     # we can look in the CC table, but won't reject any bugs that don't have
     # any CC fields.
-    $query =~ s/bugs,/bugs left join cc using (bug_id) left join profiles ccname on cc.who = ccname.userid,/;
+    $query =~ s/bugs left join,/bugs left join cc using (bug_id) left join profiles ccname on cc.who = ccname.userid left join,/;
 }
 
 if (defined $::FORM{'sql'}) {
@@ -431,6 +433,8 @@ foreach my $f ("short_desc", "long_desc", "bug_file_loc",
 }
 
 
+$query .= "group by bugs.bug_id\n";
+
 if (defined $::FORM{'order'} && $::FORM{'order'} ne "") {
     $query .= "order by ";
     ORDER: for ($::FORM{'order'}) {
diff --git a/changepassword.cgi b/changepassword.cgi
index 068180fbddd8b71254c299ceb9fad36fd4c55d39..c6d408562e813415140066e7f22cf846d5c2c507 100755
--- a/changepassword.cgi
+++ b/changepassword.cgi
@@ -83,6 +83,8 @@ On which of these bugs would you like email notification of changes?</td>
 <input type=submit value=Submit>
 </form>
 <hr>
+<a href=\"showvotes.cgi\">Review your votes</a>
+<hr>
 ";
     navigation_header();
     exit;
diff --git a/colchange.cgi b/colchange.cgi
index 2dd1b350b8c26e53591d0703312d81dcf180ce01..eb3eafd39dff5ea83ad565419f40f989fb96a031 100755
--- a/colchange.cgi
+++ b/colchange.cgi
@@ -31,7 +31,7 @@ print "Content-type: text/html\n";
 
 my @masterlist = ("opendate", "changeddate", "severity", "priority",
                   "platform", "owner", "reporter", "status", "resolution",
-                  "component", "product", "version", "project", "os");
+                  "component", "product", "version", "project", "os", "votes");
 
 if (Param("usetargetmilestone")) {
     push(@masterlist, "target_milestone");
diff --git a/defparams.pl b/defparams.pl
index a7c4ba06074fc85371d6b4cfa9c80f0a060abe97..7efdc40539525e7a3658f55d05b45f262d85db8f 100644
--- a/defparams.pl
+++ b/defparams.pl
@@ -339,5 +339,20 @@ DefParam("emailsuffix",
          "");
 
 
+DefParam("voteremovedmail",
+q{This is a mail message to send to anyone who gets a vote removed from a bug for any reason.  %to% gets replaced by a comma-separated list of people who used to be voting for this bug.  %bugid% gets replaced by the bug number.  %reason% gets replaced by a short reason describing why the vote was removed.  %<i>anythingelse</i>% gets replaced by the definition of thatparameter (as defined on this page).},
+         "l",
+"From: bugzilla-daemon
+To: %to%
+Subject: [Bug %bugid%] Your vote has been removed from this bug
+
+You used to have a vote on bug %bugid%, but it has been removed.
+
+Reason: %reason%
+
+%urlbase%show_bug.cgi?id=%bugid%
+");
+         
+
 1;
 
diff --git a/doeditcomponents.cgi b/doeditcomponents.cgi
index bfe09e93df9fee95dbfc71d2cfa48cecf85740c8..7f138f285d014adf5aa7ea600f28ce24a0be840a 100755
--- a/doeditcomponents.cgi
+++ b/doeditcomponents.cgi
@@ -96,12 +96,12 @@ GetVersionTable();
 my $prodcode = "P000";
 
 foreach my $product (@::legal_product) {
-    SendSQL("select description, milestoneurl, disallownew from products where product='$product'");
+    SendSQL("select description, milestoneurl, disallownew, votesperuser from products where product='$product'");
     my @row = FetchSQLData();
     if (!@row) {
         next;
     }
-    my ($description, $milestoneurl, $disallownew) = (@row);
+    my ($description, $milestoneurl, $disallownew, $votesperuser) = (@row);
     $prodcode++;
     Check($product, $::FORM{"prodcode-$prodcode"});
 
@@ -111,6 +111,7 @@ foreach my $product (@::legal_product) {
         DoOne($milestoneurl, "$prodcode-milestoneurl", $where);
     }
     DoOne($disallownew, "$prodcode-disallownew", $where);
+    DoOne($votesperuser, "$prodcode-votesperuser", $where);
 
     SendSQL("select value, initialowner, initialqacontact, description from components where program=" . SqlQuote($product) . " order by value");
     my $c = 0;
diff --git a/doeditvotes.cgi b/doeditvotes.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..03c4c1d88e0ed60a33cb5387e5826a26d873edca
--- /dev/null
+++ b/doeditvotes.cgi
@@ -0,0 +1,102 @@
+#!/usr/bonsaitools/bin/perl -w
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Terry Weissman <terry@mozilla.org>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+confirm_login();
+
+print "Content-type: text/html\n\n";
+
+ConnectToDatabase();
+GetVersionTable();
+
+my $who = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'});
+
+if ($who ne $::FORM{'who'}) {
+    PutHeader("Wrong login.");
+    print "The login info got confused.  If you want to adjust the votes\n";
+    print "for <tt>$::COOKIE{'Bugzilla_login'}</tt>, then please\n";
+    print "<a href=showvotes.cgi?user=$who>click here</a>.<hr>\n";
+    navigation_header();
+    exit();
+}
+
+my @buglist = grep {/^\d+$/} keys(%::FORM);
+
+if (0 == @buglist) {
+    PutHeader("Oops?");
+    print "Something got confused.  Please click <b>Back</b> and try again.";
+    navigation_header();
+    exit();
+}
+
+foreach my $id (@buglist) {
+    $::FORM{$id} = trim($::FORM{$id});
+    if ($::FORM{$id} !~ /\d+/ || $::FORM{$id} < 0) {
+        PutHeader("Numbers only, please");
+        print "Only use numeric values for your bug votes.\n";
+        print "Please click <b>Back</b> and try again.<hr>\n";
+        navigation_header();
+        exit();
+    }
+}
+
+SendSQL("select bug_id, product from bugs where bug_id = " .
+        join(" or bug_id = ", @buglist));
+
+my %prodcount;
+
+while (MoreSQLData()) {
+    my ($id, $prod) = (FetchSQLData());
+    if (!defined $prodcount{$prod}) {
+        $prodcount{$prod} = 0;
+    }
+    $prodcount{$prod} += $::FORM{$id};
+}
+
+foreach my $prod (keys(%prodcount)) {
+    if ($prodcount{$prod} > $::prodmaxvotes{$prod}) {
+        PutHeader("Don't overstuff!", "Illegal vote");
+        print "You may only use $::prodmaxvotes{$prod} votes for bugs in the\n";
+        print "<tt>$prod</tt> product, but you are using $prodcount{$prod}.\n";
+        print "Please click <b>Back</b> and try again.<hr>\n";
+        navigation_header();
+        exit();
+    }
+}
+
+SendSQL("delete from votes where who = $who");
+foreach my $id (@buglist) {
+    if ($::FORM{$id} > 0) {
+        SendSQL("insert into votes (who, bug_id, count) values ($who, $id, $::FORM{$id})");
+    }
+}
+
+PutHeader("Voting tabulated", "Voting tabulated", $::COOKIE{'Bugzilla_login'});
+print "Your votes have been recorded.\n";
+print qq{<p><a href="showvotes.cgi?user=$who">Review your votes</a><hr>\n};
+navigation_header();
+exit();
+    
+
diff --git a/editcomponents.cgi b/editcomponents.cgi
index a7e1e660edf6c985951db4ee09336764a4fa13f4..e4b6a23dea804ef02af6e89fcc10ec5fecb8fb07 100755
--- a/editcomponents.cgi
+++ b/editcomponents.cgi
@@ -59,12 +59,12 @@ GetVersionTable();
 my $prodcode = "P000";
 
 foreach my $product (@::legal_product) {
-    SendSQL("select description, milestoneurl, disallownew from products where product='$product'");
+    SendSQL("select description, milestoneurl, disallownew, votesperuser from products where product='$product'");
     my @row = FetchSQLData();
     if (!@row) {
         next;
     }
-    my ($description, $milestoneurl, $disallownew) = (@row);
+    my ($description, $milestoneurl, $disallownew, $votesperuser) = (@row);
     $prodcode++;
     print "<input type=hidden name=prodcode-$prodcode value=\"" .
         value_quote($product) . "\">\n";
@@ -77,6 +77,9 @@ foreach my $product (@::legal_product) {
         print "<td><input size=80 name=$prodcode-milestoneurl value=\"" .
             value_quote($milestoneurl) . "\"></td></tr>\n";
     }
+    print qq{<tr><th align=right>Maximum votes per user:</th><td>\n};
+    print qq{<input size=10 name=$prodcode-votesperuser value=$votesperuser>};
+    print qq{</td></tr>\n};
     my $check0 = !$disallownew ? " SELECTED" : "";
     my $check1 = $disallownew ? " SELECTED" : "";
     print "<tr><td colspan=2><select name=$prodcode-disallownew>\n";
diff --git a/globals.pl b/globals.pl
index 4f5b9a2267e15024bbb590d5a571831938276d55..ccb0e81674c8b6303756c7dcf43539597aa916dd 100644
--- a/globals.pl
+++ b/globals.pl
@@ -236,9 +236,9 @@ sub GenerateVersionTable {
     my $dotargetmilestone = Param("usetargetmilestone");
 
     my $mpart = $dotargetmilestone ? ", milestoneurl" : "";
-    SendSQL("select product, description, disallownew$mpart from products");
+    SendSQL("select product, description, votesperuser, disallownew$mpart from products");
     while (@line = FetchSQLData()) {
-        my ($p, $d, $dis, $u) = (@line);
+        my ($p, $d, $votesperuser, $dis, $u) = (@line);
         $::proddesc{$p} = $d;
         if ($dis) {
             # Special hack.  Stomp on the description and make it "0" if we're
@@ -249,6 +249,7 @@ sub GenerateVersionTable {
         if ($dotargetmilestone) {
             $::milestoneurl{$p} = $u;
         }
+        $::prodmaxvotes{$p} = $votesperuser;
     }
             
 
@@ -300,6 +301,7 @@ sub GenerateVersionTable {
         print FID GenerateCode('@::legal_' . $i);
     }
     print FID GenerateCode('%::proddesc');
+    print FID GenerateCode('%::prodmaxvotes');
 
     if ($dotargetmilestone) {
         my $last = Param("nummilestones");
@@ -515,6 +517,29 @@ sub UserInGroup {
 }
 
 
+sub RemoveVotes {
+    my ($id, $reason) = (@_);
+    ConnectToDatabase();
+    SendSQL("select profiles.login_name from votes, profiles where votes.bug_id = $id and profiles.userid = votes.who");
+    my @list;
+    while (MoreSQLData()) {
+        push(@list, FetchOneColumn());
+    }
+    if (0 < @list) {
+        if (open(SENDMAIL, "|/usr/lib/sendmail -t")) {
+            my %substs;
+            $substs{"to"} = join(',', @list);
+            $substs{"bugid"} = $id;
+            $substs{"reason"} = $reason;
+            print SENDMAIL PerformSubsts(Param("voteremovedmail"), \%substs);
+            close SENDMAIL;
+        }
+        SendSQL("delete from votes where bug_id = $id");
+    }
+}
+
+
+
 sub Param {
     my ($value) = (@_);
     if (defined $::param{$value}) {
diff --git a/makeproducttable.sh b/makeproducttable.sh
index ce5dae79c113f794a91cd00791e75a407fed07f9..806c69627f53c221de42c612faf16606a024e28e 100755
--- a/makeproducttable.sh
+++ b/makeproducttable.sh
@@ -31,7 +31,8 @@ create table products (
 product tinytext,
 description mediumtext,
 milestoneurl tinytext not null,
-disallownew tinyint not null
+disallownew tinyint not null,
+votesperuser smallint not null
 );
 
 
diff --git a/makevotestable.sh b/makevotestable.sh
new file mode 100755
index 0000000000000000000000000000000000000000..bc2e163c1bf68219e0c090470a78266bfc381f92
--- /dev/null
+++ b/makevotestable.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Terry Weissman <terry@mozilla.org>
+
+mysql > /dev/null 2>/dev/null << OK_ALL_DONE
+
+use bugs;
+
+drop table votes;
+OK_ALL_DONE
+
+mysql << OK_ALL_DONE
+use bugs;
+create table votes (
+who mediumint not null,
+bug_id mediumint not null,
+count smallint not null,
+
+index(who),
+index(bug_id)
+);
+
diff --git a/process_bug.cgi b/process_bug.cgi
index aa168bd55d88c47ad63f6a5f0602174ba22f747f..2bd10e890195b809e19ee49beb2a411dd7b48e99 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -303,7 +303,7 @@ sub LogDependencyActivity {
 
 foreach my $id (@idlist) {
     my %dependencychanged;
-    SendSQL("lock tables bugs write, bugs_activity write, cc write, profiles write, dependencies write");
+    SendSQL("lock tables bugs write, bugs_activity write, cc write, profiles write, dependencies write, votes write");
     my @oldvalues = SnapShotBug($id);
 
     if (defined $::FORM{'delta_ts'} && $::FORM{'delta_ts'} ne $delta_ts) {
@@ -490,6 +490,10 @@ The changes made were:
                 $old = DBID_to_name($old) if $old != 0;
                 $new = DBID_to_name($new) if $new != 0;
             }
+            if ($col eq 'product') {
+                RemoveVotes($id,
+                            "This bug has been moved to a different product");
+            }
             $col = SqlQuote($col);
             $old = SqlQuote($old);
             $new = SqlQuote($new);
diff --git a/processmail b/processmail
index 04c064df345df6d3ae974959a67153210aa746c1..58d53e51f7cf3946ba6e45bd9e801075f56425a6 100755
--- a/processmail
+++ b/processmail
@@ -190,6 +190,10 @@ sub GetBugText {
 
     my @cclist;
     @cclist = split(/,/, ShowCcList($id));
+    SendSQL("select profiles.login_name from votes, profiles where votes.bug_id = $id and profiles.userid = votes.who");
+    while (MoreSQLData()) {
+        push(@cclist, FetchOneColumn());
+    }
     $::bug{'cclist'} = \@cclist;
 
 
diff --git a/showvotes.cgi b/showvotes.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..1d03ae19107d64faba0108c20968ef7878964f90
--- /dev/null
+++ b/showvotes.cgi
@@ -0,0 +1,120 @@
+#!/usr/bonsaitools/bin/perl -w
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Terry Weissman <terry@mozilla.org>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+if (defined $::FORM{'voteon'} || (!defined $::FORM{'bug_id'} &&
+                                  !defined $::FORM{'user'})) {
+    confirm_login();
+    ConnectToDatabase();
+    $::FORM{'user'} = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'});
+}
+
+print "Content-type: text/html\n\n";
+
+if (defined $::FORM{'bug_id'}) {
+    my $id = $::FORM{'bug_id'};
+    my $linkedid = qq{<a href="show_bug.cgi?id=$id">$id</a>};
+    PutHeader("Show votes", "Show votes", "Bug $linkedid");
+    ConnectToDatabase();
+    SendSQL("select profiles.login_name, votes.who, votes.count from votes, profiles where votes.bug_id = " . SqlQuote($id) . " and profiles.userid = votes.who");
+    print "<table>\n";
+    print "<tr><th>Who</th><th>Number of votes</th></tr>\n";
+    my $sum = 0;
+    while (MoreSQLData()) {
+        my ($name, $userid, $count) = (FetchSQLData());
+        print qq{<tr><td><a href="showvotes.cgi?user=$userid">$name</a></td><td align=right>$count</td></tr>\n};
+        $sum += $count;
+    }
+    print "</table>";
+    print "<p>Total votes: $sum<p>\n";
+} elsif (defined $::FORM{'user'}) {
+    ConnectToDatabase();
+    quietly_check_login();
+    GetVersionTable();
+    my $who = $::FORM{'user'};
+    my $name = DBID_to_name($who);
+    PutHeader("Show votes", "Show votes", $name);
+    print qq{<form action="doeditvotes.cgi">\n};
+    print "<table><tr><td></td><th>Bug \#</th><th>Summary</th><th>Votes</th></tr>\n";
+    SendSQL("lock tables bugs read, votes write");
+    if (defined($::FORM{'voteon'})) {
+        # Oh, boy, what a hack.  Make sure there is an entry for this bug
+        # in the vote table, just so that things display right.
+        # Yuck yuck yuck.###
+        SendSQL("select votes.count from votes where votes.bug_id = $::FORM{'voteon'} and votes.who = $who");
+        if (!MoreSQLData()) {
+            SendSQL("insert into votes (who, bug_id, count) values ($who, $::FORM{'voteon'}, 0)");
+        }
+    }
+    my $canedit = (defined $::COOKIE{'Bugzilla_login'} &&
+                   $::COOKIE{'Bugzilla_login'} eq $name);
+    foreach my $product (sort(keys(%::prodmaxvotes))) {
+        if ($::prodmaxvotes{$product} <= 0) {
+            next;
+        }
+        my $qprod = value_quote($product);
+        SendSQL("select votes.bug_id, votes.count, bugs.short_desc, bugs.bug_status from votes, bugs where votes.who = $who and votes.bug_id = bugs.bug_id and bugs.product = " . SqlQuote($product) . "order by votes.bug_id");
+        my $sum = 0;
+        print "<tr><th>$product</th></tr>";
+        while (MoreSQLData()) {
+            my ($id, $count, $summary, $status) = (FetchSQLData());
+            my $opened = ($status eq "NEW" || $status eq "ASSIGNED" ||
+                          $status eq "REOPENED");
+            my $strike = $opened ? "" : "<strike>";
+            my $endstrike = $opened ? "" : "</strike>";
+            $summary = html_quote($summary);
+            $sum += $count;
+            if ($canedit) {
+                $count = "<input name=$id value=$count size=5>";
+            }
+            print qq{
+<tr>
+<td></td>
+<td><a href="showvotes.cgi?bug_id=$id">$id</a></td>
+<td><a href="show_bug.cgi?id=$id">$strike$summary$endstrike</a></td>
+<td align=right>$count</td>
+</tr>
+};
+        }
+        my $plural = (($sum == 1) ? "" : "s");
+        print "<td colspan=3>$sum vote$plural used out of\n";
+        print "$::prodmaxvotes{$product} allowed.</td>\n";
+    }
+    print "</table>\n";
+    if ($canedit) {
+        print qq{<input type=submit value="Submit">\n};
+        print "<br>To change your votes, type in new numbers (using zero to\n";
+        print "mean no votes), and then click <b>Submit</b>.\n";
+    }
+    print "<input type=hidden name=who value=$who>";
+    print "</form>\n";
+    SendSQL("delete from votes where count <= 0");
+    SendSQL("unlock tables");
+}
+    
+print qq{<a href="votehelp.html">Help!  I don't understand this voting stuff</a>};
+
+navigation_header();
+    
diff --git a/votehelp.html b/votehelp.html
index 46b7de744f0a8ce8101713c9b96d35532b25de78..da76fa91efbfe1c016307564ecb9fa871c7a7d5c 100644
--- a/votehelp.html
+++ b/votehelp.html
@@ -48,5 +48,10 @@ To vote for a bug:
        may rebalance your votes as necessary.
 </ul>
 
+You will automatically get email notifying you of any changes that
+occur on bugs you vote for.
+
+<p>
+
 You may review your votes at any time by clicking on the "Change your
 password or preferences" link at the bottom of the query page.