summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorterry%mozilla.org <>2000-02-17 14:15:20 +0100
committerterry%mozilla.org <>2000-02-17 14:15:20 +0100
commite9a32920f47ce268e3835b12abccc9fb2e1dd8c6 (patch)
tree8f1154745b807d4dee480e7b5c22d3ccb4b27f07
parent3c0ea11d42d7942f36e1704afefc55655811db5d (diff)
downloadbugzilla-e9a32920f47ce268e3835b12abccc9fb2e1dd8c6.tar.gz
bugzilla-e9a32920f47ce268e3835b12abccc9fb2e1dd8c6.tar.xz
Major spankage. Added a new state, UNCONFIRMED. Added new groups,
"editbugs" and "canconfirm". People without these states are now much more limited in what they can do. For backwards compatability, by default all users will have the editbugs and canconfirm bits on them. Installing this changes as is should only have one major visible effect -- an UNCONFIRMED state will appear in the query page. But no bugs will become in that state, until you tweak some of the new voting-related parameters you'll find when editing products.
-rw-r--r--CGI.pl52
-rw-r--r--bug_form.pl100
-rwxr-xr-xbug_status.html16
-rwxr-xr-xbuglist.cgi19
-rwxr-xr-xchecksetup.pl82
-rw-r--r--confirmhelp.html154
-rw-r--r--defparams.pl9
-rwxr-xr-xdoeditvotes.cgi22
-rwxr-xr-xeditproducts.cgi178
-rwxr-xr-xeditusers.cgi222
-rwxr-xr-xenter_bug.cgi21
-rw-r--r--globals.pl45
-rwxr-xr-xpost_bug.cgi20
-rwxr-xr-xprocess_bug.cgi139
-rwxr-xr-xshowvotes.cgi3
-rwxr-xr-xuserprefs.cgi46
16 files changed, 942 insertions, 186 deletions
diff --git a/CGI.pl b/CGI.pl
index 157f7a9ff..af597f8bc 100644
--- a/CGI.pl
+++ b/CGI.pl
@@ -490,6 +490,9 @@ sub BuildPulldown {
if ($tag eq $default) {
$selectpart = " SELECTED";
}
+ if (!defined $desc) {
+ $desc = $tag;
+ }
$entry .= qq{<OPTION$selectpart VALUE="$tag">$desc\n};
}
$entry .= qq{</SELECT>};
@@ -515,28 +518,31 @@ sub quietly_check_login() {
$::usergroupset = '0';
my $loginok = 0;
$::disabledreason = '';
+ $::userid = 0;
if (defined $::COOKIE{"Bugzilla_login"} &&
defined $::COOKIE{"Bugzilla_logincookie"}) {
ConnectToDatabase();
if (!defined $ENV{'REMOTE_HOST'}) {
$ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
}
- SendSQL("select profiles.groupset, profiles.login_name, " .
+ SendSQL("SELECT profiles.userid, profiles.groupset, " .
+ "profiles.login_name, " .
"profiles.login_name = " .
SqlQuote($::COOKIE{"Bugzilla_login"}) .
- " and profiles.cryptpassword = logincookies.cryptpassword " .
- "and logincookies.hostname = " .
+ " AND profiles.cryptpassword = logincookies.cryptpassword " .
+ "AND logincookies.hostname = " .
SqlQuote($ENV{"REMOTE_HOST"}) .
", profiles.disabledtext " .
- " from profiles,logincookies where logincookies.cookie = " .
+ " FROM profiles, logincookies WHERE logincookies.cookie = " .
SqlQuote($::COOKIE{"Bugzilla_logincookie"}) .
- " and profiles.userid = logincookies.userid");
+ " AND profiles.userid = logincookies.userid");
my @row;
if (@row = FetchSQLData()) {
- my ($groupset, $loginname, $ok, $disabledtext) = (@row);
+ my ($userid, $groupset, $loginname, $ok, $disabledtext) = (@row);
if ($ok) {
if ($disabledtext eq '') {
$loginok = 1;
+ $::userid = $userid;
$::usergroupset = $groupset;
$::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case
# is in
@@ -730,6 +736,7 @@ name=PleaseMailAPassword>
# Update the timestamp on our logincookie, so it'll keep on working.
SendSQL("update logincookies set lastused = null where cookie = $::COOKIE{'Bugzilla_logincookie'}");
+ return $::userid;
}
@@ -785,6 +792,37 @@ sub PutFooter {
}
+sub CheckIfVotedConfirmed {
+ my ($id, $who) = (@_);
+ SendSQL("SELECT bugs.votes, bugs.bug_status, products.votestoconfirm, " .
+ " bugs.everconfirmed " .
+ "FROM bugs, products " .
+ "WHERE bugs.bug_id = $id AND products.product = bugs.product");
+ my ($votes, $status, $votestoconfirm, $everconfirmed) = (FetchSQLData());
+ if ($votes >= $votestoconfirm && $status eq $::unconfirmedstate) {
+ SendSQL("UPDATE bugs SET bug_status = 'NEW', everconfirmed = 1 " .
+ "WHERE bug_id = $id");
+ my $fieldid = GetFieldID("bug_status");
+ SendSQL("INSERT INTO bugs_activity " .
+ "(bug_id,who,bug_when,fieldid,oldvalue,newvalue) VALUES " .
+ "($id,$who,now(),$fieldid,'$::unconfirmedstate','NEW')");
+ if (!$everconfirmed) {
+ $fieldid = GetFieldID("everconfirmed");
+ SendSQL("INSERT INTO bugs_activity " .
+ "(bug_id,who,bug_when,fieldid,oldvalue,newvalue) VALUES " .
+ "($id,$who,now(),$fieldid,'0','1')");
+ }
+ AppendComment($id, DBID_to_name($who),
+ "*** This bug has been confirmed by popular vote. ***");
+ print "<TABLE BORDER=1><TD><H2>Bug $id has been confirmed by votes.</H2>\n";
+ system("./processmail", $id);
+ print "<TD><A HREF=\"show_bug.cgi?id=$id\">Go To BUG# $id</A></TABLE>\n";
+ }
+
+}
+
+
+
sub DumpBugActivity {
my ($id, $starttime) = (@_);
my $datepart = "";
@@ -885,7 +923,7 @@ sub GetCommandMenu {
$html .= ", <a href=editparams.cgi>parameters</a>";
$html .= ", <a href=sanitycheck.cgi><NOBR>sanity check</NOBR></a>";
}
- if (UserInGroup("editusers")) {
+ if (UserInGroup("editusers") || UserInGroup("editgroupmembers")) {
$html .= ", <a href=editusers.cgi>users</a>";
}
if (UserInGroup("editcomponents")) {
diff --git a/bug_form.pl b/bug_form.pl
index c5cfd96e2..520949a36 100644
--- a/bug_form.pl
+++ b/bug_form.pl
@@ -31,13 +31,13 @@ sub bug_form_pl_sillyness {
$zz = %::components;
$zz = %::prodmaxvotes;
$zz = %::versions;
+ $zz = @::legal_keywords;
$zz = @::legal_opsys;
$zz = @::legal_platform;
$zz = @::legal_product;
$zz = @::legal_priority;
$zz = @::legal_resolution_no_dup;
$zz = @::legal_severity;
- $zz = @::keywordsbyname;
}
my %knownattachments;
@@ -194,14 +194,27 @@ if (@row = FetchSQLData()) {
exit;
}
+my $assignedtoid = $bug{'assigned_to'};
+my $reporterid = $bug{'reporter'};
+my $qacontactid = $bug{'qa_contact'};
+
$bug{'assigned_to'} = DBID_to_name($bug{'assigned_to'});
$bug{'reporter'} = DBID_to_name($bug{'reporter'});
+
+print qq{<FORM NAME="changeform" METHOD="POST" ACTION="process_bug.cgi">\n};
+
+# foreach my $i (sort(keys(%bug))) {
+# my $q = value_quote($bug{$i});
+# print qq{<INPUT TYPE="HIDDEN" NAME="orig-$i" VALUE="$q">\n};
+# }
+
$bug{'long_desc'} = GetLongDescription($id);
my $longdesclength = length($bug{'long_desc'});
-
GetVersionTable();
+
+
#
# These should be read from the database ...
#
@@ -229,11 +242,9 @@ if (defined $URL && $URL ne "none" && $URL ne "NULL" && $URL ne "") {
}
print "
-<FORM NAME=changeform METHOD=POST ACTION=\"process_bug.cgi\">
<INPUT TYPE=HIDDEN NAME=\"delta_ts\" VALUE=\"$bug{'delta_ts'}\">
<INPUT TYPE=HIDDEN NAME=\"longdesclength\" VALUE=\"$longdesclength\">
<INPUT TYPE=HIDDEN NAME=\"id\" VALUE=$id>
-<INPUT TYPE=HIDDEN NAME=\"was_assigned_to\" VALUE=\"$bug{'assigned_to'}\">
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0><TR>
<TD ALIGN=RIGHT><B>Bug#:</B></TD><TD><A HREF=\"show_bug.cgi?id=$bug{'bug_id'}\">$bug{'bug_id'}</A></TD>
<TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#rep_platform\">Platform:</A></B></TD>
@@ -449,50 +460,73 @@ my $knum = 1;
my $status = $bug{'bug_status'};
-if ($status eq "NEW" || $status eq "ASSIGNED" || $status eq "REOPENED") {
- if ($status ne "ASSIGNED") {
- print "<INPUT TYPE=radio NAME=knob VALUE=accept>";
- print "Accept bug (change status to <b>ASSIGNED</b>)<br>";
- $knum++;
- }
- if ($bug{'resolution'} ne "") {
- print "<INPUT TYPE=radio NAME=knob VALUE=clearresolution>\n";
- print "Clear the resolution (remove the current resolution of\n";
- print "<b>$bug{'resolution'}</b>)<br>\n";
+my $canedit = UserInGroup("editbugs");
+my $canconfirm;
+
+if ($status eq $::unconfirmedstate) {
+ $canconfirm = UserInGroup("canconfirm");
+ if ($canedit || $canconfirm) {
+ print "<INPUT TYPE=radio NAME=knob VALUE=confirm>";
+ print "Confirm bug (change status to <b>NEW</b>)<br>";
$knum++;
}
- print "<INPUT TYPE=radio NAME=knob VALUE=resolve>
+}
+
+
+if ($::userid && ($canedit || $::userid == $assignedtoid ||
+ $::userid == $reporterid || $::userid == $qacontactid)) {
+ if (IsOpenedState($status)) {
+ if ($status ne "ASSIGNED") {
+ print "<INPUT TYPE=radio NAME=knob VALUE=accept>";
+ my $extra = "";
+ if ($status eq $::unconfirmedstate && ($canconfirm || $canedit)) {
+ $extra = "confirm bug, ";
+ }
+ print "Accept bug (${extra}change status to <b>ASSIGNED</b>)<br>";
+ $knum++;
+ }
+ if ($bug{'resolution'} ne "") {
+ print "<INPUT TYPE=radio NAME=knob VALUE=clearresolution>\n";
+ print "Clear the resolution (remove the current resolution of\n";
+ print "<b>$bug{'resolution'}</b>)<br>\n";
+ $knum++;
+ }
+ print "<INPUT TYPE=radio NAME=knob VALUE=resolve>
Resolve bug, changing <A HREF=\"bug_status.html\">resolution</A> to
<SELECT NAME=resolution
ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\">
$resolution_popup</SELECT><br>\n";
- $knum++;
- print "<INPUT TYPE=radio NAME=knob VALUE=duplicate>
+ $knum++;
+ print "<INPUT TYPE=radio NAME=knob VALUE=duplicate>
Resolve bug, mark it as duplicate of bug #
<INPUT NAME=dup_id SIZE=6 ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\"><br>\n";
- $knum++;
- my $assign_element = "<INPUT NAME=\"assigned_to\" SIZE=32 ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\" VALUE=\"$bug{'assigned_to'}\">";
+ $knum++;
+ my $assign_element = "<INPUT NAME=\"assigned_to\" SIZE=32 ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\" VALUE=\"$bug{'assigned_to'}\">";
- print "<INPUT TYPE=radio NAME=knob VALUE=reassign>
+ print "<INPUT TYPE=radio NAME=knob VALUE=reassign>
<A HREF=\"bug_status.html#assigned_to\">Reassign</A> bug to
$assign_element
<br>\n";
- $knum++;
- print "<INPUT TYPE=radio NAME=knob VALUE=reassignbycomponent>
+ if ($status eq $::unconfirmedstate && ($canconfirm || $canedit)) {
+ print "&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE=checkbox NAME=andconfirm> and confirm bug (change status to <b>NEW</b>)<BR>";
+ }
+ $knum++;
+ print "<INPUT TYPE=radio NAME=knob VALUE=reassignbycomponent>
Reassign bug to owner of selected component<br>\n";
- $knum++;
-} else {
- print "<INPUT TYPE=radio NAME=knob VALUE=reopen> Reopen bug<br>\n";
- $knum++;
- if ($status eq "RESOLVED") {
- print "<INPUT TYPE=radio NAME=knob VALUE=verify>
- Mark bug as <b>VERIFIED</b><br>\n";
$knum++;
- }
- if ($status ne "CLOSED") {
- print "<INPUT TYPE=radio NAME=knob VALUE=close>
- Mark bug as <b>CLOSED</b><br>\n";
+ } else {
+ print "<INPUT TYPE=radio NAME=knob VALUE=reopen> Reopen bug<br>\n";
$knum++;
+ if ($status eq "RESOLVED") {
+ print "<INPUT TYPE=radio NAME=knob VALUE=verify>
+ Mark bug as <b>VERIFIED</b><br>\n";
+ $knum++;
+ }
+ if ($status ne "CLOSED") {
+ print "<INPUT TYPE=radio NAME=knob VALUE=close>
+ Mark bug as <b>CLOSED</b><br>\n";
+ $knum++;
+ }
}
}
diff --git a/bug_status.html b/bug_status.html
index c9629ff38..6e9f044e1 100755
--- a/bug_status.html
+++ b/bug_status.html
@@ -30,7 +30,9 @@
The <B>status</B> and <B>resolution</B> field define and track the
life cycle of a bug.
+<a name="status">
<p>
+</a>
<TABLE BORDER=1 CELLPADDING=4>
<TR ALIGN=CENTER VALIGN=TOP>
@@ -42,7 +44,12 @@ certain status transitions are allowed.
<TD>The <b>resolution</b> field indicates what happened to this bug.
<TR VALIGN=TOP><TD>
-<DL><DT><B>NEW</B>
+<DL><DT><B>UNCONFIRMED</B>
+<DD> This bug has recently been added to the database. Nobody has
+ validated that this bug is true. Users who have the "canconfirm"
+ permission set may confirm this bug, changing its state to NEW.
+ Or, it may be directly resolved and marked RESOLVED.
+<DT><B>NEW</B>
<DD> This bug has recently been added to the assignee's list of bugs
and must be processed. Bugs in this state may be accepted, and
become <B>ASSIGNED</B>, passed on to someone else, and remain
@@ -60,8 +67,8 @@ certain status transitions are allowed.
</DL>
<TD>
<DL>
-<DD> No resolution yet. All bugs which are <B>NEW</B> or
- <B>ASSIGNED</B> have the resolution set to blank. All other bugs
+<DD> No resolution yet. All bugs which are in one of these "open" states
+ have the resolution set to blank. All other bugs
will be marked with one of the following resolutions.
</DL>
@@ -188,8 +195,7 @@ searching for bugs that have been resolved or verified, remember to set the
status field appropriately.
<hr>
-<address><a href="http://home.netscape.com/people/terry/">Terry Weissman &lt;terry@netscape.com&gt;</a></address>
<!-- hhmts start -->
-Last modified: Thu Jan 13 16:22:39 2000
+Last modified: Wed Feb 16 20:41:24 2000
<!-- hhmts end -->
</body> </html>
diff --git a/buglist.cgi b/buglist.cgi
index 8af1e9433..096508211 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -33,6 +33,7 @@ use Date::Parse;
sub sillyness {
my $zz;
$zz = $::defaultqueryname;
+ $zz = $::unconfirmedstate;
$zz = @::components;
$zz = @::default_column_list;
$zz = @::keywordsbyname;
@@ -824,6 +825,14 @@ my $dotweak = defined $::FORM{'tweak'};
if ($dotweak) {
confirm_login();
+ if (!UserInGroup("canedit")) {
+ print qq{
+Sorry; you do not have sufficient priviledges to edit a bunch of bugs
+at once.
+};
+ PutFooter();
+ exit();
+ }
} else {
quietly_check_login();
}
@@ -979,7 +988,7 @@ if ($splitheader) {
$tablestart .= "</TR>\n<TR><TD></TD>";
}
for (my $i=1-$pass ; $i<@th ; $i += 2) {
- my $h = @th[$i];
+ my $h = $th[$i];
$h =~ s/TH/TH COLSPAN="2" ALIGN="left"/;
$tablestart .= $h;
}
@@ -1287,6 +1296,12 @@ if ($::usergroupset ne '0' && $buggroupset =~ /^\d+$/) {
<INPUT TYPE=radio NAME=knob VALUE=none CHECKED>
Do nothing else<br>";
$knum++;
+ if ($statushash{$::unconfirmedstate} && 1 == scaler(keys(%statushash))) {
+ print "
+<INPUT TYPE=radio NAME=knob VALUE=confirm>
+ Confirm bugs (change status to <b>NEW</b>)<br>";
+ }
+ $knum++;
print "
<INPUT TYPE=radio NAME=knob VALUE=accept>
Accept bugs (change status to <b>ASSIGNED</b>)<br>";
@@ -1363,7 +1378,7 @@ if ($count > 0) {
<NOBR><A HREF=\"enter_bug.cgi\">Enter New Bug</A></NOBR>
&nbsp;&nbsp;
<NOBR><A HREF=\"colchange.cgi?$::buffer\">Change columns</A></NOBR>";
- if (!$dotweak && $count > 1) {
+ if (!$dotweak && $count > 1 && UserInGroup("canedit")) {
print "&nbsp;&nbsp;\n";
print "<NOBR><A HREF=\"buglist.cgi?$fields$orderpart&tweak=1\">";
print "Change several bugs at once</A></NOBR>\n";
diff --git a/checksetup.pl b/checksetup.pl
index 05bcf31ac..16f2a9c19 100755
--- a/checksetup.pl
+++ b/checksetup.pl
@@ -547,7 +547,7 @@ $table{bugs} =
assigned_to mediumint not null, # This is a comment.
bug_file_loc text,
bug_severity enum($severities) not null,
- bug_status enum("NEW", "ASSIGNED", "REOPENED", "RESOLVED", "VERIFIED", "CLOSED") not null,
+ bug_status enum("UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED", "RESOLVED", "VERIFIED", "CLOSED") not null,
creation_ts datetime not null,
delta_ts timestamp,
short_desc mediumtext,
@@ -567,6 +567,7 @@ $table{bugs} =
# the real data comes from the keywords table.
. '
lastdiffed datetime not null,
+ everconfirmed tinyint not null,
index (assigned_to),
index (creation_ts),
@@ -656,7 +657,10 @@ $table{products} =
description mediumtext,
milestoneurl tinytext not null,
disallownew tinyint not null,
- votesperuser smallint not null';
+ votesperuser smallint not null,
+ maxvotesperbug smallint not null default 10000,
+ votestoconfirm smallint not null
+';
$table{profiles} =
@@ -670,10 +674,25 @@ $table{profiles} =
disabledtext mediumtext not null,
newemailtech tinyint not null,
mybugslink tinyint not null default 1,
+ blessgroupset bigint not null,
+
unique(login_name)';
+$table{profiles_activity} =
+ 'userid mediumint not null,
+ who mediumint not null,
+ profiles_when datetime not null,
+ fieldid mediumint not null,
+ oldvalue tinytext,
+ newvalue tinytext,
+
+ index (userid),
+ index (profiles_when),
+ index (fieldid)';
+
+
$table{namedqueries} =
'userid mediumint not null,
name varchar(64) not null,
@@ -775,22 +794,31 @@ while (my ($tabname, $fielddef) = each %table) {
# Populate groups table
###########################################################################
+sub GroupExists ($)
+{
+ my ($name) = @_;
+ my $sth = $dbh->prepare("SELECT name FROM groups WHERE name='$name'");
+ $sth->execute;
+ if ($sth->rows) {
+ return 1;
+ }
+ return 0;
+}
+
+
#
# This subroutine checks if a group exist. If not, it will be automatically
# created with the next available bit set
#
-sub AddGroup ($$)
-{
- my ($name, $desc) = @_;
+sub AddGroup {
+ my ($name, $desc, $userregexp) = @_;
+ $userregexp ||= "";
- # does the group exist?
- my $sth = $dbh->prepare("SELECT name FROM groups WHERE name='$name'");
- $sth->execute;
- return if $sth->rows;
+ return if GroupExists($name);
# get highest bit number
- $sth = $dbh->prepare("SELECT bit FROM groups ORDER BY bit DESC");
+ my $sth = $dbh->prepare("SELECT bit FROM groups ORDER BY bit DESC");
$sth->execute;
my @row = $sth->fetchrow_array;
@@ -807,21 +835,31 @@ sub AddGroup ($$)
$sth = $dbh->prepare('INSERT INTO groups
(bit, name, description, userregexp)
VALUES (?, ?, ?, ?)');
- $sth->execute($bit, $name, $desc, "");
+ $sth->execute($bit, $name, $desc, $userregexp);
+ return $bit;
}
#
-# BugZilla uses --GROUPS-- to assign various rights to it's users.
+# BugZilla uses --GROUPS-- to assign various rights to its users.
#
AddGroup 'tweakparams', 'Can tweak operating parameters';
AddGroup 'editusers', 'Can edit or disable users';
-AddGroup 'editgroupmembers', 'Can put people in and out of groups that they are members of.';
AddGroup 'creategroups', 'Can create and destroy groups.';
AddGroup 'editcomponents', 'Can create, destroy, and edit components.';
AddGroup 'editkeywords', 'Can create, destroy, and edit keywords.';
+if (!GroupExists("editbugs")) {
+ my $id = AddGroup('editbugs', 'Can edit all aspects of any bug.', ".*");
+ $dbh->do("UPDATE profiles SET groupset = groupset | $id");
+}
+
+if (!GroupExists("canconfirm")) {
+ my $id = AddGroup('canconfirm', 'Can confirm a bug.', ".*");
+ $dbh->do("UPDATE profiles SET groupset = groupset | $id");
+}
+
@@ -1427,6 +1465,24 @@ AddField('profiles', 'mybugslink', 'tinyint not null default 1');
AddField('namedqueries', 'linkinfooter', 'tinyint not null');
+# 2000-02-12 Added a new state to bugs, UNCONFIRMED. Added ability to confirm
+# a vote via bugs. Added user bits to control which users can confirm bugs
+# by themselves, and which users can edit bugs without their names on them.
+# Added a user field which controls which groups a user can put other users
+# into.
+
+my @states = ("UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED", "RESOLVED",
+ "VERIFIED", "CLOSED");
+CheckEnumField('bugs', 'bug_status', @states);
+if (!GetFieldDef('bugs', 'everconfirmed')) {
+ AddField('bugs', 'everconfirmed', 'tinyint not null');
+ $dbh->do("UPDATE bugs SET everconfirmed = 1");
+}
+AddField('products', 'maxvotesperbug', 'smallint not null default 10000');
+AddField('products', 'votestoconfirm', 'smallint not null');
+AddField('profiles', 'blessgroupset', 'bigint not null');
+
+
#
# If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment.
diff --git a/confirmhelp.html b/confirmhelp.html
new file mode 100644
index 000000000..5c8e64434
--- /dev/null
+++ b/confirmhelp.html
@@ -0,0 +1,154 @@
+<HTML><head>
+
+<!--
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (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) 2000 Netscape Communications Corporation. All
+ Rights Reserved.
+
+ Contributor(s): Terry Weissman <terry@mozilla.org>
+-->
+
+<title>Understanding the UNCONFIRMED state, and other recent changes</title>
+</head>
+
+<body>
+<h1>Understanding the UNCONFIRMED state, and other recent changes</h1>
+
+[This document is aimed primarily at people who have used Bugzilla
+before the UNCONFIRMED state was implemented. It might be helpful for
+newer users as well.]
+
+<p>
+
+New bugs in some products will now show up in a new state,
+UNCONFIRMED. This means that we have nobody has confirmed that the
+bug is real. Very busy engineers will probably generally ignore
+UNCONFIRMED that have been assigned to them, until they have been
+confirmed in one way or another. (Engineers with more time will
+hopefully glance over their UNCONFIRMED bugs regularly.)
+
+<p>
+
+The <a href="bug_status.html">page describing bug fields</a> has been
+updated to include UNCONFIRMED.
+<p>
+
+There are two basic ways that a bug can become confirmed (and enter
+the NEW) state.
+
+<ul>
+ <li> A user with the appropriate permissions (see below for more on
+ permissions) decides that the bug is a valid one, and confirms
+ it. We hope to gather a small army of responsible volunteers
+ to regularly go through bugs for us.
+ <li> The bug gathers a certain number of votes. <b>Any</b> valid
+ Bugzilla user may vote for bugs (each user gets a certain
+ number of bugs); any UNCONFIRMED bug which enough bugs becomes
+ automatically confirmed, and enters the NEW state.
+</ul>
+
+One implication of this is that it is worth your time to search the
+bug system for duplicates of your bug to vote on them, before
+submitting your own bug. If we can spread around knowledge of this
+fact, it ought to help cut down the number of duplicate bugs in the
+system.
+
+<h2>Permissions.</h2>
+
+Users now have a certain set of permissions. To see your permissions,
+check out the
+<a href="userprefs.cgi?bank=permissions">user preferences</a> page.
+
+<p>
+
+If you have the "Can confirm a bug" permission, then you will be able
+to move UNCONFIRMED bugs into the NEW state.
+
+<p>
+
+If you have the "Can edit all aspects of any bug" permission, then you
+can tweak anything about any bug. If not, you may only edit those
+bugs that you have submitted, or that you have assigned to you (or
+qa-assigned to you). However, anyone may add a comment to any bug.
+
+<p>
+
+Some people (initially, the initial owners and initial qa-contacts for
+components in the system) have the ability to give the above two
+permissions to other people. So, if you really feel that you ought to
+have one of these permissions, a good person to ask (via private
+email, please!) is the person who is assigned a relevant bug.
+
+<h2>Other details.</h2>
+
+An initial stab was taken to decide who would be given which of the
+above permissions. This was determined by some simple heurstics of
+who was assigned bugs, and who the default owners of bugs were, and a
+look at people who seem to have submitted several bugs that appear to
+have been interesting and valid. Inevitably, we have failed to give
+someone the permissions they deserve. Please don't take it
+personally; just bear with us as we shake out the new system.
+
+<p>
+
+
+People with one of the two bits above can easily confirm their own
+bugs, so bugs they submit will actually start out in the NEW state.
+They can override this when submitting a bug.
+
+<p>
+
+People can ACCEPT or RESOLVE a bug assigned to them, even if they
+aren't allowed to confirm it. However, the system remembers, and if
+the bug gets REOPENED or reassigned to someone else, it will revert
+back to the UNCONFIRMED state. If the bug has ever been confirmed,
+then REOPENing or reassigning will cause it to go to the NEW or
+REOPENED state.
+
+<p>
+
+Note that only some products support the UNCONFIRMED state. In other
+products, all new bugs will automatically start in the NEW state.
+
+<h2>Things still to be done.</h2>
+
+There probably ought to be a way to get a bug back into the
+UNCONFIRMED state, but there isn't yet.
+
+<p>
+
+If a person has submitted several bugs that get confirmed, then this
+is probably a person who understands the system well, and deserves the
+"Can confirm a bug" permission. This kind of person should be
+detected and promoted automatically.
+
+<p>
+
+There should also be a way to automatically promote people to get the
+"Can edit all aspects of any bug" permission.
+
+<p>
+
+The "enter a new bug" page needs to be revamped with easy ways for new
+people to educate themselves on the benefit of searching for a bug
+like the one they're about to submit and voting on it, rather than
+adding a new useless duplicate.
+
+<hr>
+<!-- hhmts start -->
+Last modified: Wed Feb 16 21:04:56 2000
+<!-- hhmts end -->
+</body> </html>
diff --git a/defparams.pl b/defparams.pl
index 742ca3746..c5cf400e0 100644
--- a/defparams.pl
+++ b/defparams.pl
@@ -428,7 +428,7 @@ DefParam("expectbigqueries",
0);
DefParam("emailregexp",
- 'This defines the regexp to use for legal email addresses. The default tries to match fully qualified email addresses. Another popular value to put here is <tt>^[^@, ]$</tt>, which means "local usernames, no @ allowed.',
+ 'This defines the regexp to use for legal email addresses. The default tries to match fully qualified email addresses. Another popular value to put here is <tt>^[^@, ]*$</tt>, which means "local usernames, no @ allowed.',
"t",
q:^[^@, ]*@[^@, ]*\\.[^@, ]*$:);
@@ -444,7 +444,7 @@ 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).},
+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. %count% is how many votes got removed.%<i>anythingelse</i>% gets replaced by the definition of that parameter (as defined on this page).},
"l",
"From: bugzilla-daemon
To: %to%
@@ -454,6 +454,8 @@ You used to have a vote on bug %bugid%, but it has been removed.
Reason: %reason%
+Votes removed: %count%
+
%urlbase%show_bug.cgi?id=%bugid%
");
@@ -488,6 +490,9 @@ DefParam("commentonaccept",
DefParam("commentonclearresolution",
"If this option is on, the user needs to enter a short comment if the bugs resolution is cleared",
"b", 0 );
+DefParam("commentonconfirm",
+ "If this option is on, the user needs to enter a short comment when confirming a bug",
+ "b", 0 );
DefParam("commentonresolve",
"If this option is on, the user needs to enter a short comment if the bug is resolved",
"b", 0 );
diff --git a/doeditvotes.cgi b/doeditvotes.cgi
index eef6381d8..3902f9118 100755
--- a/doeditvotes.cgi
+++ b/doeditvotes.cgi
@@ -63,17 +63,27 @@ foreach my $id (@buglist) {
}
}
-SendSQL("select bug_id, product from bugs where bug_id = " .
- join(" or bug_id = ", @buglist));
+SendSQL("SELECT bugs.bug_id, bugs.product, products.maxvotesperbug " .
+ "FROM bugs, products " .
+ "WHERE products.product = bugs.product ".
+ " AND bugs.bug_id IN (" . join(", ", @buglist) . ")");
my %prodcount;
while (MoreSQLData()) {
- my ($id, $prod) = (FetchSQLData());
+ my ($id, $prod, $max) = (FetchSQLData());
if (!defined $prodcount{$prod}) {
$prodcount{$prod} = 0;
}
$prodcount{$prod} += $::FORM{$id};
+ if ($::FORM{$id} > $max) {
+ PutHeader("Don't overstuff!", "Illegal vote");
+ print "You may only use at most $max votes for a single bug in the\n";
+ print "<tt>$prod</tt> product, but you are using $::FORM{$id}.\n";
+ print "<P>Please click <b>Back</b> and try again.<hr>\n";
+ PutFooter();
+ exit();
+ }
}
foreach my $prod (keys(%prodcount)) {
@@ -81,7 +91,7 @@ foreach my $prod (keys(%prodcount)) {
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";
+ print "<P>Please click <b>Back</b> and try again.<hr>\n";
PutFooter();
exit();
}
@@ -110,10 +120,12 @@ foreach my $id (keys %affected) {
SendSQL("unlock tables");
-
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};
+foreach my $id (keys %affected) {
+ CheckIfVotedConfirmed($id, $who);
+}
PutFooter();
exit();
diff --git a/editproducts.cgi b/editproducts.cgi
index e9c5f9f35..d224077b3 100755
--- a/editproducts.cgi
+++ b/editproducts.cgi
@@ -32,7 +32,13 @@ use strict;
require "CGI.pl";
require "globals.pl";
+# Shut up misguided -w warnings about "used only once". "use vars" just
+# doesn't work for me.
+sub sillyness {
+ my $zz;
+ $zz = $::unconfirmedstate;
+}
# TestProduct: just returns if the specified product does exists
@@ -72,10 +78,10 @@ sub CheckProduct ($)
# Displays the form to edit a products parameters
#
-sub EmitFormElements ($$$$$)
+sub EmitFormElements ($$$$$$$)
{
my ($product, $description, $milestoneurl, $disallownew,
- $votesperuser) = @_;
+ $votesperuser, $maxvotesperbug, $votestoconfirm) = @_;
$product = value_quote($product);
$description = value_quote($description);
@@ -102,6 +108,14 @@ sub EmitFormElements ($$$$$)
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Maximum votes per person:</TH>\n";
print " <TD><INPUT SIZE=5 MAXLENGTH=5 NAME=\"votesperuser\" VALUE=\"$votesperuser\"></TD>\n";
+
+ print "</TR><TR>\n";
+ print " <TH ALIGN=\"right\">Maximum votes a person can put on a single bug:</TH>\n";
+ print " <TD><INPUT SIZE=5 MAXLENGTH=5 NAME=\"maxvotesperbug\" VALUE=\"$maxvotesperbug\"></TD>\n";
+
+ print "</TR><TR>\n";
+ print " <TH ALIGN=\"right\">Number of votes a bug in this product needs to automatically get out of the <A HREF=\"bug_status.html#status\">UNCONFIRMED</A> state:</TH>\n";
+ print " <TD><INPUT SIZE=5 MAXLENGTH=5 NAME=\"votestoconfirm\" VALUE=\"$votestoconfirm\"></TD>\n";
}
@@ -173,7 +187,7 @@ unless ($action) {
PutHeader("Select product");
SendSQL("SELECT products.product,description,disallownew,
- votesperuser,COUNT(bug_id)
+ votesperuser,maxvotesperbug,votestoconfirm,COUNT(bug_id)
FROM products LEFT JOIN bugs
ON products.product=bugs.product
GROUP BY products.product
@@ -183,12 +197,14 @@ unless ($action) {
print " <TH ALIGN=\"left\">Description</TH>\n";
print " <TH ALIGN=\"left\">Status</TH>\n";
print " <TH ALIGN=\"left\">Votes<br>per<br>user</TH>\n";
+ print " <TH ALIGN=\"left\">Max<br>Votes<br>per<br>bug</TH>\n";
+ print " <TH ALIGN=\"left\">Votes<br>to<br>confirm</TH>\n";
print " <TH ALIGN=\"left\">Bugs</TH>\n";
print " <TH ALIGN=\"left\">Action</TH>\n";
print "</TR>";
while ( MoreSQLData() ) {
my ($product, $description, $disallownew, $votesperuser,
- $bugs) = FetchSQLData();
+ $maxvotesperbug, $votestoconfirm, $bugs) = FetchSQLData();
$description ||= "<FONT COLOR=\"red\">missing</FONT>";
$disallownew = $disallownew ? 'closed' : 'open';
$bugs ||= 'none';
@@ -197,6 +213,8 @@ unless ($action) {
print " <TD VALIGN=\"top\">$description</TD>\n";
print " <TD VALIGN=\"top\">$disallownew</TD>\n";
print " <TD VALIGN=\"top\" ALIGN=\"right\">$votesperuser</TD>\n";
+ print " <TD VALIGN=\"top\" ALIGN=\"right\">$maxvotesperbug</TD>\n";
+ print " <TD VALIGN=\"top\" ALIGN=\"right\">$votestoconfirm</TD>\n";
print " <TD VALIGN=\"top\" ALIGN=\"right\">$bugs</TD>\n";
print " <TD VALIGN=\"top\"><A HREF=\"editproducts.cgi?action=del&product=", url_quote($product), "\">Delete</A></TD>\n";
print "</TR>";
@@ -227,7 +245,7 @@ if ($action eq 'add') {
print "<FORM METHOD=POST ACTION=editproducts.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
- EmitFormElements('', '', '', 0, 0);
+ EmitFormElements('', '', '', 0, 0, 10000, 0);
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Version:</TH>\n";
@@ -283,16 +301,21 @@ if ($action eq 'new') {
$disallownew = 1 if $::FORM{disallownew};
my $votesperuser = $::FORM{votesperuser};
$votesperuser ||= 0;
+ my $maxvotesperbug = $::FORM{maxvotesperbug};
+ $maxvotesperbug = 10000 if !defined $maxvotesperbug;
+ my $votestoconfirm = $::FORM{votestoconfirm};
+ $votestoconfirm ||= 0;
# Add the new product.
SendSQL("INSERT INTO products ( " .
- "product, description, milestoneurl, disallownew, votesperuser" .
+ "product, description, milestoneurl, disallownew, votesperuser, " .
+ "maxvotesperbug, votestoconfirm" .
" ) VALUES ( " .
SqlQuote($product) . "," .
SqlQuote($description) . "," .
SqlQuote($milestoneurl) . "," .
$disallownew . "," .
- SqlQuote($votesperuser) . ")" );
+ "$votesperuser, $maxvotesperbug, $votestoconfirm)");
SendSQL("INSERT INTO versions ( " .
"value, program" .
" ) VALUES ( " .
@@ -513,17 +536,19 @@ if ($action eq 'edit') {
CheckProduct($product);
# get data of product
- SendSQL("SELECT description,milestoneurl,disallownew,votesperuser
+ SendSQL("SELECT description,milestoneurl,disallownew,
+ votesperuser,maxvotesperbug,votestoconfirm
FROM products
WHERE product=" . SqlQuote($product));
- my ($description, $milestoneurl, $disallownew, $votesperuser) =
+ my ($description, $milestoneurl, $disallownew,
+ $votesperuser, $maxvotesperbug, $votestoconfirm) =
FetchSQLData();
print "<FORM METHOD=POST ACTION=editproducts.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements($product, $description, $milestoneurl, $disallownew,
- $votesperuser);
+ $votesperuser, $maxvotesperbug, $votestoconfirm);
print "</TR><TR VALIGN=top>\n";
print " <TH ALIGN=\"right\"><A HREF=\"editcomponents.cgi?product=", url_quote($product), "\">Edit components:</A></TH>\n";
@@ -586,6 +611,8 @@ if ($action eq 'edit') {
value_quote($milestoneurl) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"disallownewold\" VALUE=\"$disallownew\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"votesperuserold\" VALUE=\"$votesperuser\">\n";
+ print "<INPUT TYPE=HIDDEN NAME=\"maxvotesperbugold\" VALUE=\"$maxvotesperbug\">\n";
+ print "<INPUT TYPE=HIDDEN NAME=\"votestoconfirmold\" VALUE=\"$votestoconfirm\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"update\">\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Update\">\n";
@@ -606,18 +633,30 @@ if ($action eq 'edit') {
if ($action eq 'update') {
PutHeader("Update product");
- my $productold = trim($::FORM{productold} || '');
- my $description = trim($::FORM{description} || '');
- my $descriptionold = trim($::FORM{descriptionold} || '');
- my $disallownew = trim($::FORM{disallownew} || '');
- my $disallownewold = trim($::FORM{disallownewold} || '');
- my $milestoneurl = trim($::FORM{milestoneurl} || '');
- my $milestoneurlold = trim($::FORM{milestoneurlold} || '');
- my $votesperuser = trim($::FORM{votesperuser} || 0);
- my $votesperuserold = trim($::FORM{votesperuserold} || '');
+ my $productold = trim($::FORM{productold} || '');
+ my $description = trim($::FORM{description} || '');
+ my $descriptionold = trim($::FORM{descriptionold} || '');
+ my $disallownew = trim($::FORM{disallownew} || '');
+ my $disallownewold = trim($::FORM{disallownewold} || '');
+ my $milestoneurl = trim($::FORM{milestoneurl} || '');
+ my $milestoneurlold = trim($::FORM{milestoneurlold} || '');
+ my $votesperuser = trim($::FORM{votesperuser} || 0);
+ my $votesperuserold = trim($::FORM{votesperuserold} || '');
+ my $maxvotesperbug = trim($::FORM{maxvotesperbug} || 0);
+ my $maxvotesperbugold = trim($::FORM{maxvotesperbugold} || '');
+ my $votestoconfirm = trim($::FORM{votestoconfirm} || 0);
+ my $votestoconfirmold = trim($::FORM{votestoconfirmold} || '');
+
+ my $checkvotes = 0;
CheckProduct($productold);
+ if ($maxvotesperbug !~ /^\d+$/ || $maxvotesperbug <= 0) {
+ print "Sorry, the max votes per bug must be a positive integer.";
+ PutTrailer($localtrailer);
+ exit;
+ }
+
# Note that the order of this tests is important. If you change
# them, be sure to test for WHERE='$product' or WHERE='$productold'
@@ -659,9 +698,31 @@ if ($action eq 'update') {
SET votesperuser=$votesperuser
WHERE product=" . SqlQuote($productold));
print "Update votes per user.<BR>\n";
+ $checkvotes = 1;
}
+ if ($maxvotesperbug ne $maxvotesperbugold) {
+ SendSQL("UPDATE products
+ SET maxvotesperbug=$maxvotesperbug
+ WHERE product=" . SqlQuote($productold));
+ print "Update max votes per bug.<BR>\n";
+ $checkvotes = 1;
+ }
+
+
+ if ($votestoconfirm ne $votestoconfirmold) {
+ SendSQL("UPDATE products
+ SET votestoconfirm=$votestoconfirm
+ WHERE product=" . SqlQuote($productold));
+ print "Update votes to confirm.<BR>\n";
+ $checkvotes = 1;
+ }
+
+
+ my $qp = SqlQuote($product);
+ my $qpold = SqlQuote($productold);
+
if ($product ne $productold) {
unless ($product) {
print "Sorry, I can't delete the product name.";
@@ -676,24 +737,79 @@ if ($action eq 'update') {
exit;
}
- SendSQL("UPDATE bugs
- SET product=" . SqlQuote($product) . "
- WHERE product=" . SqlQuote($productold));
- SendSQL("UPDATE components
- SET program=" . SqlQuote($product) . "
- WHERE program=" . SqlQuote($productold));
- SendSQL("UPDATE products
- SET product=" . SqlQuote($product) . "
- WHERE product=" . SqlQuote($productold));
- SendSQL("UPDATE versions
- SET program='$product'
- WHERE program=" . SqlQuote($productold));
+ SendSQL("UPDATE bugs SET product=$qp WHERE product=$qpold");
+ SendSQL("UPDATE components SET program=$qp WHERE program=$qpold");
+ SendSQL("UPDATE products SET product=$qp WHERE product=$qpold");
+ SendSQL("UPDATE versions SET program=$qp WHERE program=$qpold");
print "Updated product name.<BR>\n";
}
unlink "data/versioncache";
SendSQL("UNLOCK TABLES");
+ if ($checkvotes) {
+ print "Checking existing votes in this product for anybody who now has too many votes.";
+ if ($maxvotesperbug < $votesperuser) {
+ SendSQL("SELECT votes.who, votes.bug_id " .
+ "FROM votes, bugs " .
+ "WHERE bugs.bug_id = votes.bug_id " .
+ " AND bugs.product = $qp " .
+ " AND votes.count > $maxvotesperbug");
+ my @list;
+ while (MoreSQLData()) {
+ my ($who, $id) = (FetchSQLData());
+ push(@list, [$who, $id]);
+ }
+ foreach my $ref (@list) {
+ my ($who, $id) = (@$ref);
+ RemoveVotes($id, $who, "The rules for voting on this product has changed;\nyou had too many votes for a single bug.");
+ my $name = DBID_to_name($who);
+ print qq{<br>Removed votes for bug <A HREF="show_bug.cgi?id=$id">$id</A> from $name\n};
+ }
+ }
+ SendSQL("SELECT votes.who, votes.count FROM votes, bugs " .
+ "WHERE bugs.bug_id = votes.bug_id " .
+ " AND bugs.product = $qp");
+ my %counts;
+ while (MoreSQLData()) {
+ my ($who, $count) = (FetchSQLData());
+ if (!defined $counts{$who}) {
+ $counts{$who} = $count;
+ } else {
+ $counts{$who} += $count;
+ }
+ }
+ foreach my $who (keys(%counts)) {
+ if ($counts{$who} > $votesperuser) {
+ SendSQL("SELECT votes.bug_id FROM votes, bugs " .
+ "WHERE bugs.bug_id = votes.bug_id " .
+ " AND bugs.product = $qp " .
+ " AND votes.who = $who");
+ while (MoreSQLData()) {
+ my $id = FetchSQLData();
+ RemoveVotes($id, $who,
+ "The rules for voting on this product has changed; you had too many\ntotal votes, so all votes have been removed.");
+ my $name = DBID_to_name($who);
+ print qq{<br>Removed votes for bug <A HREF="show_bug.cgi?id=$id">$id</A> from $name\n};
+ }
+ }
+ }
+ SendSQL("SELECT bug_id FROM bugs " .
+ "WHERE product = $qp " .
+ " AND bug_status = '$::unconfirmedstate' " .
+ " AND votes >= $votestoconfirm");
+ my @list;
+ while (MoreSQLData()) {
+ push(@list, FetchOneColumn());
+ }
+ foreach my $id (@list) {
+ SendSQL("SELECT who FROM votes WHERE bug_id = $id");
+ my $who = FetchOneColumn();
+ CheckIfVotedConfirmed($id, $who);
+ }
+
+ }
+
PutTrailer($localtrailer);
exit;
}
diff --git a/editusers.cgi b/editusers.cgi
index f4a6c4dfb..03819ec35 100755
--- a/editusers.cgi
+++ b/editusers.cgi
@@ -31,7 +31,16 @@ use strict;
require "CGI.pl";
require "globals.pl";
+# Shut up misguided -w warnings about "used only once". "use vars" just
+# doesn't work for me.
+sub sillyness {
+ my $zz;
+ $zz = $::userid;
+}
+
+my $editall;
+my $opblessgroupset = '9223372036854775807'; # This is all 64 bits.
@@ -69,59 +78,81 @@ sub CheckUser ($)
+sub EmitElement ($$)
+{
+ my ($name, $value) = (@_);
+ $value = value_quote($value);
+ if ($editall) {
+ print qq{<TD><INPUT SIZE=64 MAXLENGTH=255 NAME="$name" VALUE="$value"></TD>\n};
+ } else {
+ print qq{<TD>$value</TD>\n};
+ }
+}
+
+
#
# Displays the form to edit a user parameters
#
-sub EmitFormElements ($$$$$$)
+sub EmitFormElements ($$$$$$$)
{
- my ($user, $password, $realname, $groupset, $emailnotification,
- $disabledtext) = @_;
+ my ($user, $password, $realname, $groupset, $blessgroupset,
+ $emailnotification, $disabledtext) = @_;
print " <TH ALIGN=\"right\">Login name:</TH>\n";
- print " <TD><INPUT SIZE=64 MAXLENGTH=255 NAME=\"user\" VALUE=\"$user\"></TD>\n";
+ EmitElement("user", $user);
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Real name:</TH>\n";
- print " <TD><INPUT SIZE=64 MAXLENGTH=255 NAME=\"realname\" VALUE=\"$realname\"></TD>\n";
-
- print "</TR><TR>\n";
- print " <TH ALIGN=\"right\">Password:</TH>\n";
- print " <TD><INPUT SIZE=16 MAXLENGTH=16 NAME=\"password\" VALUE=\"$password\"></TD>\n";
-
- print "</TR><TR>\n";
- print " <TH ALIGN=\"right\">Email notification:</TH>\n";
- print qq{<TD><SELECT NAME="emailnotification">};
- foreach my $i (["ExcludeSelfChanges", "All qualifying bugs except those which I change"],
- ["CConly", "Only those bugs which I am listed on the CC line"],
- ["All", "All qualifying bugs"]) {
- my ($tag, $desc) = (@$i);
- my $selectpart = "";
- if ($tag eq $emailnotification) {
- $selectpart = " SELECTED";
+ EmitElement("realname", $realname);
+
+ if ($editall) {
+ print "</TR><TR>\n";
+ print " <TH ALIGN=\"right\">Password:</TH>\n";
+ print " <TD><INPUT SIZE=16 MAXLENGTH=16 NAME=\"password\" VALUE=\"$password\"></TD>\n";
+
+ print "</TR><TR>\n";
+ print " <TH ALIGN=\"right\">Email notification:</TH>\n";
+ print qq{<TD><SELECT NAME="emailnotification">};
+ foreach my $i (["ExcludeSelfChanges", "All qualifying bugs except those which I change"],
+ ["CConly", "Only those bugs which I am listed on the CC line"],
+ ["All", "All qualifying bugs"]) {
+ my ($tag, $desc) = (@$i);
+ my $selectpart = "";
+ if ($tag eq $emailnotification) {
+ $selectpart = " SELECTED";
+ }
+ print qq{<OPTION$selectpart VALUE="$tag">$desc\n};
}
- print qq{<OPTION$selectpart VALUE="$tag">$desc\n};
+ print "</SELECT></TD>\n";
+ print "</TR><TR>\n";
+ print " <TH ALIGN=\"right\">Disable text:</TH>\n";
+ print " <TD ROWSPAN=2><TEXTAREA NAME=\"disabledtext\" ROWS=10 COLS=60>" .
+ value_quote($disabledtext) . "</TEXTAREA>\n";
+ print " </TD>\n";
+ print "</TR><TR>\n";
+ print " <TD VALIGN=\"top\">If non-empty, then the account will\n";
+ print "be disabled, and this text should explain why.</TD>\n";
}
- print "</SELECT></TD>\n";
- print "</TR><TR>\n";
- print " <TH ALIGN=\"right\">Disable text:</TH>\n";
- print " <TD ROWSPAN=2><TEXTAREA NAME=\"disabledtext\" ROWS=10 COLS=60>" .
- value_quote($disabledtext) . "</TEXTAREA>\n";
- print " </TD>\n";
- print "</TR><TR>\n";
- print " <TD VALIGN=\"top\">If non-empty, then the account will\n";
- print "be disabled, and this text should explain why.</TD>\n";
-
-
- SendSQL("SELECT bit,name,description,bit & $groupset != 0
- FROM groups
- ORDER BY name");
+
+
+ SendSQL("SELECT bit,name,description,bit & $groupset != 0, " .
+ " bit & $blessgroupset " .
+ "FROM groups " .
+ "WHERE bit & $opblessgroupset != 0 " .
+ "ORDER BY name");
while (MoreSQLData()) {
- my ($bit,$name,$description,$checked) = FetchSQLData();
+ my ($bit,$name,$description,$checked,$blchecked) = FetchSQLData();
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">", ucfirst($name), ":</TH>\n";
$checked = ($checked) ? "CHECKED" : "";
print " <TD><INPUT TYPE=CHECKBOX NAME=\"bit_$name\" $checked VALUE=\"$bit\"> $description</TD>\n";
+ if ($editall) {
+ print "</TR><TR>\n";
+ print "<TH></TH>";
+ $blchecked = ($blchecked) ? "CHECKED" : "";
+ print "<TD><INPUT TYPE=CHECKBOX NAME=\"blbit_$name\" $blchecked VALUE=\"$bit\"> Can turn this bit on for other users</TD>\n";
+ }
}
}
@@ -165,12 +196,19 @@ confirm_login();
print "Content-type: text/html\n\n";
-unless (UserInGroup("editusers")) {
- PutHeader("Not allowed");
- print "Sorry, you aren't a member of the 'editusers' group.\n";
- print "And so, you aren't allowed to add, modify or delete users.\n";
- PutTrailer();
- exit;
+$editall = UserInGroup("editusers");
+
+if (!$editall) {
+ SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $::userid");
+ $opblessgroupset = FetchOneColumn();
+ if (!$opblessgroupset) {
+ PutHeader("Not allowed");
+ print "Sorry, you aren't a member of the 'editusers' group, and you\n";
+ print "don't have permissions to put people in or out of any group.\n";
+ print "And so, you aren't allowed to add, modify or delete users.\n";
+ PutTrailer();
+ exit;
+ }
}
@@ -198,8 +236,8 @@ List users with login name matching:
<INPUT SIZE=32 NAME="matchstr">
<SELECT NAME="matchtype">
<OPTION VALUE="substr" SELECTED>case-insensitive substring
-<OPTION VALUE="regexp" SELECTED>case-sensitive regexp
-<OPTION VALUE="notregexp" SELECTED>not (case-sensitive regexp)
+<OPTION VALUE="regexp">case-sensitive regexp
+<OPTION VALUE="notregexp">not (case-sensitive regexp)
</SELECT>
<BR>
<INPUT TYPE=SUBMIT VALUE="Submit">
@@ -261,14 +299,17 @@ if ($action eq 'list') {
}
print "</TR>";
}
- print "<TR>\n";
- my $span = $candelete ? 3 : 2;
- print qq{
+ if ($editall) {
+ print "<TR>\n";
+ my $span = $candelete ? 3 : 2;
+ print qq{
<TD VALIGN="top" COLSPAN=$span ALIGN="right">
<A HREF=\"editusers.cgi?action=add\">Add a new user</A>
</TD>
};
- print "</TR></TABLE>\n";
+ print "</TR>";
+ }
+ print "</TABLE>\n";
print "$count users found.\n";
PutTrailer($localtrailer);
@@ -286,11 +327,16 @@ if ($action eq 'list') {
if ($action eq 'add') {
PutHeader("Add user");
+ if (!$editall) {
+ print "Sorry, you don't have permissions to add new users.";
+ PutTrailer();
+ exit;
+ }
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
- EmitFormElements('', '', '', 0, 'ExcludeSelfChanges', '');
+ EmitFormElements('', '', '', 0, 0, 'ExcludeSelfChanges', '');
print "</TR></TABLE>\n<HR>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Add\">\n";
@@ -312,6 +358,12 @@ if ($action eq 'add') {
if ($action eq 'new') {
PutHeader("Adding new user");
+ if (!$editall) {
+ print "Sorry, you don't have permissions to add new users.";
+ PutTrailer();
+ exit;
+ }
+
# Cleanups and valididy checks
my $realname = trim($::FORM{realname} || '');
my $password = trim($::FORM{password} || '');
@@ -386,6 +438,11 @@ if ($action eq 'del') {
print "Sorry, deleting users isn't allowed.";
PutTrailer();
}
+ if (!$editall) {
+ print "Sorry, you don't have permissions to delete users.";
+ PutTrailer();
+ exit;
+ }
CheckUser($user);
# display some data about the user
@@ -515,6 +572,11 @@ if ($action eq 'delete') {
print "Sorry, deleting users isn't allowed.";
PutTrailer();
}
+ if (!$editall) {
+ print "Sorry, you don't have permissions to delete users.";
+ PutTrailer();
+ exit;
+ }
CheckUser($user);
SendSQL("SELECT userid
@@ -545,25 +607,28 @@ if ($action eq 'edit') {
CheckUser($user);
# get data of user
- SendSQL("SELECT password, realname, groupset, emailnotification,
- disabledtext
+ SendSQL("SELECT password, realname, groupset, blessgroupset,
+ emailnotification, disabledtext
FROM profiles
WHERE login_name=" . SqlQuote($user));
- my ($password, $realname, $groupset, $emailnotification,
+ my ($password, $realname, $groupset, $blessgroupset, $emailnotification,
$disabledtext) = FetchSQLData();
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
- EmitFormElements($user, $password, $realname, $groupset,
+ EmitFormElements($user, $password, $realname, $groupset, $blessgroupset,
$emailnotification, $disabledtext);
print "</TR></TABLE>\n";
print "<INPUT TYPE=HIDDEN NAME=\"userold\" VALUE=\"$user\">\n";
- print "<INPUT TYPE=HIDDEN NAME=\"passwordold\" VALUE=\"$password\">\n";
+ if ($editall) {
+ print "<INPUT TYPE=HIDDEN NAME=\"passwordold\" VALUE=\"$password\">\n";
+ }
print "<INPUT TYPE=HIDDEN NAME=\"realnameold\" VALUE=\"$realname\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"groupsetold\" VALUE=\"$groupset\">\n";
+ print "<INPUT TYPE=HIDDEN NAME=\"blessgroupsetold\" VALUE=\"$blessgroupset\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"emailnotificationold\" VALUE=\"$emailnotification\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"disabledtextold\" VALUE=\"" .
value_quote($disabledtext) . "\">\n";
@@ -583,7 +648,7 @@ if ($action eq 'edit') {
#
if ($action eq 'update') {
- PutHeader("Update User");
+ PutHeader("Updated user");
my $userold = trim($::FORM{userold} || '');
my $realname = trim($::FORM{realname} || '');
@@ -595,12 +660,19 @@ if ($action eq 'update') {
my $disabledtext = trim($::FORM{disabledtext} || '');
my $disabledtextold = trim($::FORM{disabledtextold} || '');
my $groupsetold = trim($::FORM{groupsetold} || '');
+ my $blessgroupsetold = trim($::FORM{blessgroupsetold} || '');
my $groupset = "0";
foreach (keys %::FORM) {
next unless /^bit_/;
#print "$_=$::FORM{$_}<br>\n";
- $groupset .= "+ $::FORM{$_}";
+ $groupset .= " + $::FORM{$_}";
+ }
+ my $blessgroupset = "0";
+ foreach (keys %::FORM) {
+ next unless /^blbit_/;
+ #print "$_=$::FORM{$_}<br>\n";
+ $blessgroupset .= " + $::FORM{$_}";
}
CheckUser($userold);
@@ -608,34 +680,58 @@ if ($action eq 'update') {
# Note that the order of this tests is important. If you change
# them, be sure to test for WHERE='$product' or WHERE='$productold'
- if ($groupset != $groupsetold) {
+ if ($groupset ne $groupsetold) {
SendSQL("UPDATE profiles
- SET groupset=" . $groupset . "
+ SET groupset =
+ groupset - (groupset & $opblessgroupset) + $groupset
WHERE login_name=" . SqlQuote($userold));
+
+ # I'm paranoid that someone who I give the ability to bless people
+ # will start misusing it. Let's log who blesses who (even though
+ # nothing actually uses this log right now).
+ my $fieldid = GetFieldID("groupset");
+ SendSQL("SELECT userid, groupset FROM profiles WHERE login_name=" .
+ SqlQuote($userold));
+ my $u;
+ ($u, $groupset) = (FetchSQLData());
+ if ($groupset ne $groupsetold) {
+ SendSQL("INSERT INTO profiles_activity " .
+ "(userid,who,profiles_when,fieldid,oldvalue,newvalue)" .
+ "VALUES " .
+ "($u, $::userid, now(), $fieldid, " .
+ " $groupsetold, $groupset)");
+ }
print "Updated permissions.\n";
}
- if ($emailnotification ne $emailnotificationold) {
+ if ($editall && $blessgroupset ne $blessgroupsetold) {
+ SendSQL("UPDATE profiles
+ SET blessgroupset=" . $blessgroupset . "
+ WHERE login_name=" . SqlQuote($userold));
+ print "Updated ability to tweak permissions of other users.\n";
+ }
+
+ if ($editall && $emailnotification ne $emailnotificationold) {
SendSQL("UPDATE profiles
SET emailnotification=" . SqlQuote($emailnotification) . "
WHERE login_name=" . SqlQuote($userold));
print "Updated email notification.<BR>\n";
}
- if ($password ne $passwordold) {
+ if ($editall && $password ne $passwordold) {
my $q = SqlQuote($password);
SendSQL("UPDATE profiles
SET password= $q, cryptpassword = ENCRYPT($q)
WHERE login_name=" . SqlQuote($userold));
print "Updated password.<BR>\n";
}
- if ($realname ne $realnameold) {
+ if ($editall && $realname ne $realnameold) {
SendSQL("UPDATE profiles
SET realname=" . SqlQuote($realname) . "
WHERE login_name=" . SqlQuote($userold));
print "Updated real name.<BR>\n";
}
- if ($disabledtext ne $disabledtextold) {
+ if ($editall && $disabledtext ne $disabledtextold) {
SendSQL("UPDATE profiles
SET disabledtext=" . SqlQuote($disabledtext) . "
WHERE login_name=" . SqlQuote($userold));
@@ -647,7 +743,7 @@ if ($action eq 'update') {
WHERE userid=" . $userid);
print "Updated disabled text.<BR>\n";
}
- if ($user ne $userold) {
+ if ($editall && $user ne $userold) {
unless ($user) {
print "Sorry, I can't delete the user's name.";
PutTrailer($localtrailer);
diff --git a/enter_bug.cgi b/enter_bug.cgi
index 57d9d0de3..45ca19de5 100755
--- a/enter_bug.cgi
+++ b/enter_bug.cgi
@@ -204,7 +204,6 @@ PutHeader ("Enter Bug","Enter Bug","This page lets you enter a new bug into Bugz
print "
<FORM METHOD=POST ACTION=\"post_bug.cgi\">
-<INPUT TYPE=HIDDEN NAME=bug_status VALUE=NEW>
<INPUT TYPE=HIDDEN NAME=reporter VALUE=\"$::COOKIE{'Bugzilla_login'}\">
<INPUT TYPE=HIDDEN NAME=product VALUE=\"" . value_quote($product) . "\">
<TABLE CELLSPACING=2 CELLPADDING=0 BORDER=0>";
@@ -260,6 +259,26 @@ print "
<td></td>
</TR>
<tr><td>&nbsp<td> <td> <td> <td> <td> </tr>
+";
+
+if (UserInGroup("canedit") || UserInGroup("canconfirm")) {
+ SendSQL("SELECT votestoconfirm FROM products WHERE product = " .
+ SqlQuote($product));
+ if (FetchOneColumn()) {
+ print qq{
+ <TR>
+ <TD ALIGN="right"><B><A HREF="bug_status.html#status">Initial state:</B></A></TD>
+ <TD COLSPAN="5">
+};
+ print BuildPulldown("bug_status",
+ [[$::unconfirmedstate], ["NEW"]],
+ "NEW");
+ print "</TD></TR>";
+ }
+}
+
+
+print "
<tr>
<TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#assigned_to\">Assigned To:</A></B></TD>
<TD colspan=5>$assign_element
diff --git a/globals.pl b/globals.pl
index 2f88d41a8..cbfc87357 100644
--- a/globals.pl
+++ b/globals.pl
@@ -73,6 +73,7 @@ $::param{'version'} = '2.9';
$::dontchange = "--do_not_change--";
$::chooseone = "--Choose_one:--";
$::defaultqueryname = "(Default query)";
+$::unconfirmedstate = "UNCONFIRMED";
sub ConnectToDatabase {
if (!defined $::db) {
@@ -644,7 +645,7 @@ sub UserInGroup {
sub IsOpenedState {
my ($state) = (@_);
- if ($state =~ /^(NEW|REOPENED|ASSIGNED)$/) {
+ if ($state =~ /^(NEW|REOPENED|ASSIGNED)$/ || $state eq $::unconfirmedstate) {
return 1;
}
return 0;
@@ -652,24 +653,42 @@ sub IsOpenedState {
sub RemoveVotes {
- my ($id, $reason) = (@_);
+ my ($id, $who, $reason) = (@_);
ConnectToDatabase();
- SendSQL("select profiles.login_name from votes, profiles where votes.bug_id = $id and profiles.userid = votes.who");
+ my $whopart = "";
+ if ($who) {
+ $whopart = " AND votes.who = $who";
+ }
+ SendSQL("SELECT profiles.login_name, votes.count " .
+ "FROM votes, profiles " .
+ "WHERE votes.bug_id = $id " .
+ "AND profiles.userid = votes.who" .
+ $whopart);
my @list;
while (MoreSQLData()) {
- push(@list, FetchOneColumn());
+ my ($name, $count) = (@_);
+ push(@list, [$name, $count]);
}
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;
+ foreach my $ref (@list) {
+ my ($name, $count) = (@$ref);
+ if (open(SENDMAIL, "|/usr/lib/sendmail -t")) {
+ my %substs;
+ $substs{"to"} = $name;
+ $substs{"bugid"} = $id;
+ $substs{"reason"} = $reason;
+ $substs{"count"} = $count;
+ print SENDMAIL PerformSubsts(Param("voteremovedmail"),
+ \%substs);
+ close SENDMAIL;
+ }
}
- SendSQL("delete from votes where bug_id = $id");
- SendSQL("update bugs set votes = 0, delta_ts=delta_ts where bug_id = $id");
+ SendSQL("DELETE FROM votes WHERE bug_id = $id" . $whopart);
+ SendSQL("SELECT SUM(count) FROM votes WHERE bug_id = $id");
+ my $v = FetchOneColumn();
+ $v ||= 0;
+ SendSQL("UPDATE bugs SET votes = $v, delta_ts = delta_ts " .
+ "WHERE bug_id = $id");
}
}
diff --git a/post_bug.cgi b/post_bug.cgi
index c3be5c67b..18b579119 100755
--- a/post_bug.cgi
+++ b/post_bug.cgi
@@ -120,12 +120,32 @@ if (Param("useqacontact")) {
+if (exists $::FORM{'bug_status'}) {
+ if (!UserInGroup("canedit") && !UserInGroup("canconfirm")) {
+ delete $::FORM{'bug_status'};
+ }
+}
+
+if (!exists $::FORM{'bug_status'}) {
+ $::FORM{'bug_status'} = $::unconfirmedstate;
+ SendSQL("SELECT votestoconfirm FROM products WHERE product = " .
+ SqlQuote($::FORM{'product'}));
+ if (!FetchOneColumn()) {
+ $::FORM{'bug_status'} = "NEW";
+ }
+}
+
+
my @used_fields;
foreach my $f (@bug_fields) {
if (exists $::FORM{$f}) {
push (@used_fields, $f);
}
}
+if (exists $::FORM{'bug_status'} && $::FORM{'bug_status'} ne $::unconfirmedstate) {
+ push(@used_fields, "everconfirmed");
+ $::FORM{'everconfirmed'} = 1;
+}
my $query = "insert into bugs (\n" . join(",\n", @used_fields) . ",
creation_ts )
diff --git a/process_bug.cgi b/process_bug.cgi
index 9eb32e129..52fc5b423 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -24,6 +24,9 @@
use diagnostics;
use strict;
+my $UserInEditGroupSet = -1;
+my $UserInCanConfirmGroupSet = -1;
+
require "CGI.pl";
# Shut up misguided -w warnings about "used only once":
@@ -38,7 +41,7 @@ use vars %::versions,
%::legal_priority,
%::legal_severity;
-confirm_login();
+my $whoid = confirm_login();
print "Content-type: text/html\n\n";
@@ -103,6 +106,97 @@ if ($::FORM{'product'} ne $::dontchange) {
}
+# Checks that the user is allowed to change the given field. Actually, right
+# now, the rules are pretty simple, and don't look at the field itself very
+# much, but that could be enhanced.
+
+my $lastbugid = 0;
+my $ownerid;
+my $reporterid;
+my $qacontactid;
+
+sub CheckCanChangeField {
+ my ($f, $bugid, $oldvalue, $newvalue) = (@_);
+ if ($f eq "assigned_to" || $f eq "reporter" || $f eq "qa_contact") {
+ if ($oldvalue =~ /^\d+$/) {
+ if ($oldvalue == 0) {
+ $oldvalue = "";
+ } else {
+ $oldvalue = DBID_to_name($oldvalue);
+ }
+ }
+ }
+ if ($oldvalue eq $newvalue) {
+ return 1;
+ }
+ if ($f =~ /^longdesc/) {
+ return 1;
+ }
+ if ($UserInEditGroupSet < 0) {
+ $UserInEditGroupSet = UserInGroup("editbugs");
+ }
+ if ($UserInEditGroupSet) {
+ return 1;
+ }
+ if ($lastbugid != $bugid) {
+ SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs " .
+ "WHERE bug_id = $bugid");
+ ($reporterid, $ownerid, $qacontactid) = (FetchSQLData());
+ }
+ if ($reporterid eq $whoid || $ownerid eq $whoid || $qacontactid eq $whoid) {
+ if ($f ne "bug_status") {
+ return 1;
+ }
+ if ($newvalue eq $::unconfirmedstate || !IsOpenedState($newvalue)) {
+ return 1;
+ }
+
+ # Hmm. They are trying to set this bug to some opened state
+ # that isn't the UNCONFIRMED state. Are they in the right
+ # group? Or, has it ever been confirmed? If not, then this
+ # isn't legal.
+
+ if ($UserInCanConfirmGroupSet < 0) {
+ $UserInCanConfirmGroupSet = UserInGroup("canconfirm");
+ }
+ if ($UserInCanConfirmGroupSet) {
+ return 1;
+ }
+ my $fieldid = GetFieldID("bug_status");
+ SendSQL("SELECT newvalue FROM bugs_activity " .
+ "WHERE fieldid = $fieldid " .
+ " AND oldvalue = '$::unconfirmedstate'");
+ while (MoreSQLData()) {
+ my $n = FetchOneColumn();
+ if (IsOpenedState($n) && $n ne $::unconfirmedstate) {
+ return 1;
+ }
+ }
+ }
+ SendSQL("UNLOCK TABLES");
+ $oldvalue = value_quote($oldvalue);
+ $newvalue = value_quote($newvalue);
+ print qq{
+<H1>Sorry, not allowed.</H1>
+Only the owner or submitter of the bug, or a sufficiently
+empowered user, may make that change to the $f field.
+<TABLE>
+<TR><TH ALIGN="right">Old value:</TH><TD>$oldvalue</TD></TR>
+<TR><TH ALIGN="right">New value:</TH><TD>$newvalue</TD></TR>
+</TABLE>
+<pre>($reporterid eq $whoid || $ownerid eq $whoid || $qacontactid eq $whoid)</PRE>
+
+<P>Click <B>Back</B> and try again.
+};
+ PutFooter();
+ exit();
+}
+
+
+
+
+
+
my @idlist;
if (defined $::FORM{'id'}) {
@@ -160,11 +254,29 @@ sub DoComma {
$::comma = ",";
}
+sub DoConfirm {
+ if ($UserInEditGroupSet < 0) {
+ $UserInEditGroupSet = UserInGroup("editbugs");
+ }
+ if ($UserInCanConfirmGroupSet < 0) {
+ $UserInCanConfirmGroupSet = UserInGroup("canconfirm");
+ }
+ if ($UserInEditGroupSet || $UserInCanConfirmGroupSet) {
+ DoComma();
+ $::query .= "everconfirmed = 1";
+ }
+}
+
+
sub ChangeStatus {
my ($str) = (@_);
if ($str ne $::dontchange) {
DoComma();
- $::query .= "bug_status = '$str'";
+ if (IsOpenedState($str)) {
+ $::query .= "bug_status = IF(everconfirmed = 1, '$str', '$::unconfirmedstate')";
+ } else {
+ $::query .= "bug_status = '$str'";
+ }
}
}
@@ -192,7 +304,7 @@ sub CheckonComment( $ ) {
if( $ret ) {
if (!defined $::FORM{'comment'} || $::FORM{'comment'} =~ /^\s*$/) {
- # No commet - sorry, action not allowed !
+ # No comment - sorry, action not allowed !
warnBanner("You have to specify a <b>comment</b> on this change." .
"<p>" .
"Please press <b>Back</b> and give some words " .
@@ -275,11 +387,17 @@ SWITCH: for ($::FORM{'knob'}) {
/^none$/ && do {
last SWITCH;
};
+ /^confirm$/ && CheckonComment( "confirm" ) && do {
+ DoConfirm();
+ ChangeStatus('NEW');
+ last SWITCH;
+ };
/^accept$/ && CheckonComment( "accept" ) && do {
+ DoConfirm();
ChangeStatus('ASSIGNED');
last SWITCH;
};
- /^clearresolution$/ && CheckonComment( "clearresolution" ) &&do {
+ /^clearresolution$/ && CheckonComment( "clearresolution" ) && do {
ChangeResolution('');
last SWITCH;
};
@@ -289,6 +407,9 @@ SWITCH: for ($::FORM{'knob'}) {
last SWITCH;
};
/^reassign$/ && CheckonComment( "reassign" ) && do {
+ if ($::FORM{'andconfirm'}) {
+ DoConfirm();
+ }
ChangeStatus('NEW');
DoComma();
if ( Param("strictvaluechecks") ) {
@@ -460,7 +581,6 @@ sub SnapShotDeps {
}
-my $whoid = DBNameToIdAndCheck($::FORM{'who'});
my $timestamp;
sub LogDependencyActivity {
@@ -489,6 +609,13 @@ foreach my $id (@idlist) {
"keywords $write, longdescs $write, fielddefs $write, " .
"keyworddefs READ, groups READ");
my @oldvalues = SnapShotBug($id);
+ my $i = 0;
+ foreach my $col (@::log_columns) {
+ if (exists $::FORM{$col}) {
+ CheckCanChangeField($col, $id, $oldvalues[$i], $::FORM{$col});
+ }
+ $i++;
+ }
if (defined $::FORM{'delta_ts'} && $::FORM{'delta_ts'} ne $delta_ts) {
print "
@@ -730,7 +857,7 @@ The changes made were:
# updates about this bug.
}
if ($col eq 'product') {
- RemoveVotes($id,
+ RemoveVotes($id, 0,
"This bug has been moved to a different product");
}
$col = GetFieldID($col);
diff --git a/showvotes.cgi b/showvotes.cgi
index 429c71545..894baafbb 100755
--- a/showvotes.cgi
+++ b/showvotes.cgi
@@ -84,8 +84,7 @@ if (defined $::FORM{'bug_id'}) {
if (!defined $status) {
next;
}
- my $opened = ($status eq "NEW" || $status eq "ASSIGNED" ||
- $status eq "REOPENED");
+ my $opened = IsOpenedState($status);
my $strike = $opened ? "" : "<strike>";
my $endstrike = $opened ? "" : "</strike>";
$summary = html_quote($summary);
diff --git a/userprefs.cgi b/userprefs.cgi
index 0ee7d576f..88f032192 100755
--- a/userprefs.cgi
+++ b/userprefs.cgi
@@ -215,6 +215,42 @@ sub SaveFooter {
+sub ShowPermissions {
+ print "You have the following permission bits set on your account:\n";
+ print "<P><UL>\n";
+ my $found = 0;
+ SendSQL("SELECT description FROM groups " .
+ "WHERE bit & $::usergroupset != 0 " .
+ "ORDER BY bit");
+ while (MoreSQLData()) {
+ my ($description) = (FetchSQLData());
+ print "<LI>$description\n";
+ $found = 1;
+ }
+ if ($found == 0) {
+ print "<LI>(No extra permission bits have been set).\n";
+ }
+ print "</UL>\n";
+ SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $userid");
+ my $blessgroupset = FetchOneColumn();
+ if ($blessgroupset) {
+ print "And you can turn on or off the following bits for\n";
+ print qq{<A HREF="editusers.cgi">other users</A>:\n};
+ print "<P><UL>\n";
+ SendSQL("SELECT description FROM groups " .
+ "WHERE bit & $blessgroupset != 0 " .
+ "ORDER BY bit");
+ while (MoreSQLData()) {
+ my ($description) = (FetchSQLData());
+ print "<LI>$description\n";
+ }
+ print "</UL>\n";
+ }
+}
+
+
+
+
######################################################################
################# Live code (not sub defs) starts here ###############
@@ -239,7 +275,9 @@ my @banklist = (
["diffs", "Email settings",
\&ShowDiffs, \&SaveDiffs],
["footer", "Page footer",
- \&ShowFooter, \&SaveFooter]
+ \&ShowFooter, \&SaveFooter],
+ ["permissions", "Permissions",
+ \&ShowPermissions, undef]
);
@@ -300,9 +338,11 @@ if (defined $bankdescription) {
</TABLE>
<INPUT TYPE="hidden" NAME="dosave" VALUE="1">
<INPUT TYPE="hidden" NAME="bank" VALUE="$bank">
-<INPUT TYPE="submit" VALUE="Submit">
-</FORM>
};
+ if ($savefunc) {
+ print qq{<INPUT TYPE="submit" VALUE="Submit">\n};
+ }
+ print qq{</FORM>\n};
} else {
print "<P>Please choose from the above links which settings you wish to change.</P>";
}