From e9a32920f47ce268e3835b12abccc9fb2e1dd8c6 Mon Sep 17 00:00:00 2001 From: "terry%mozilla.org" <> Date: Thu, 17 Feb 2000 13:15:20 +0000 Subject: 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. --- CGI.pl | 52 +++++++++++-- bug_form.pl | 100 ++++++++++++++++--------- bug_status.html | 16 ++-- buglist.cgi | 19 ++++- checksetup.pl | 82 ++++++++++++++++---- confirmhelp.html | 154 ++++++++++++++++++++++++++++++++++++++ defparams.pl | 9 ++- doeditvotes.cgi | 22 ++++-- editproducts.cgi | 178 ++++++++++++++++++++++++++++++++++++-------- editusers.cgi | 222 +++++++++++++++++++++++++++++++++++++++---------------- enter_bug.cgi | 21 +++++- globals.pl | 45 +++++++---- post_bug.cgi | 20 +++++ process_bug.cgi | 139 ++++++++++++++++++++++++++++++++-- showvotes.cgi | 3 +- userprefs.cgi | 46 +++++++++++- 16 files changed, 942 insertions(+), 186 deletions(-) create mode 100644 confirmhelp.html 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{$desc\n}; } $entry .= qq{}; @@ -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 "

Bug $id has been confirmed by votes.

\n"; + system("./processmail", $id); + print "
Go To BUG# $id
\n"; + } + +} + + + sub DumpBugActivity { my ($id, $starttime) = (@_); my $datepart = ""; @@ -885,7 +923,7 @@ sub GetCommandMenu { $html .= ", parameters"; $html .= ", sanity check"; } - if (UserInGroup("editusers")) { + if (UserInGroup("editusers") || UserInGroup("editgroupmembers")) { $html .= ", users"; } 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{
\n}; + +# foreach my $i (sort(keys(%bug))) { +# my $q = value_quote($bug{$i}); +# print qq{\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 " - - @@ -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 ""; - print "Accept bug (change status to ASSIGNED)
"; - $knum++; - } - if ($bug{'resolution'} ne "") { - print "\n"; - print "Clear the resolution (remove the current resolution of\n"; - print "$bug{'resolution'})
\n"; +my $canedit = UserInGroup("editbugs"); +my $canconfirm; + +if ($status eq $::unconfirmedstate) { + $canconfirm = UserInGroup("canconfirm"); + if ($canedit || $canconfirm) { + print ""; + print "Confirm bug (change status to NEW)
"; $knum++; } - print " +} + + +if ($::userid && ($canedit || $::userid == $assignedtoid || + $::userid == $reporterid || $::userid == $qacontactid)) { + if (IsOpenedState($status)) { + if ($status ne "ASSIGNED") { + print ""; + my $extra = ""; + if ($status eq $::unconfirmedstate && ($canconfirm || $canedit)) { + $extra = "confirm bug, "; + } + print "Accept bug (${extra}change status to ASSIGNED)
"; + $knum++; + } + if ($bug{'resolution'} ne "") { + print "\n"; + print "Clear the resolution (remove the current resolution of\n"; + print "$bug{'resolution'})
\n"; + $knum++; + } + print " Resolve bug, changing resolution to
\n"; - $knum++; - print " + $knum++; + print " Resolve bug, mark it as duplicate of bug #
\n"; - $knum++; - my $assign_element = ""; + $knum++; + my $assign_element = ""; - print " + print "Reassign bug to $assign_element
\n"; - $knum++; - print " + if ($status eq $::unconfirmedstate && ($canconfirm || $canedit)) { + print "     and confirm bug (change status to NEW)
"; + } + $knum++; + print " Reassign bug to owner of selected component
\n"; - $knum++; -} else { - print " Reopen bug
\n"; - $knum++; - if ($status eq "RESOLVED") { - print " - Mark bug as VERIFIED
\n"; $knum++; - } - if ($status ne "CLOSED") { - print " - Mark bug as CLOSED
\n"; + } else { + print " Reopen bug
\n"; $knum++; + if ($status eq "RESOLVED") { + print " + Mark bug as VERIFIED
\n"; + $knum++; + } + if ($status ne "CLOSED") { + print " + Mark bug as CLOSED
\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 status and resolution field define and track the life cycle of a bug. +

+

Bug#:$bug{'bug_id'} Platform:
@@ -42,7 +44,12 @@ certain status transitions are allowed. \n"; } 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+$/) { Do nothing else
"; $knum++; + if ($statushash{$::unconfirmedstate} && 1 == scaler(keys(%statushash))) { + print " + + Confirm bugs (change status to NEW)
"; + } + $knum++; print " Accept bugs (change status to ASSIGNED)
"; @@ -1363,7 +1378,7 @@ if ($count > 0) { Enter New Bug    Change columns"; - if (!$dotweak && $count > 1) { + if (!$dotweak && $count > 1 && UserInGroup("canedit")) { print "  \n"; print ""; print "Change several bugs at once\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 @@ + + + + +Understanding the UNCONFIRMED state, and other recent changes + + + +

Understanding the UNCONFIRMED state, and other recent changes

+ +[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.] + +

+ +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.) + +

+ +The page describing bug fields has been +updated to include UNCONFIRMED. +

+ +There are two basic ways that a bug can become confirmed (and enter +the NEW) state. + +

+ +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. + +

Permissions.

+ +Users now have a certain set of permissions. To see your permissions, +check out the +user preferences page. + +

+ +If you have the "Can confirm a bug" permission, then you will be able +to move UNCONFIRMED bugs into the NEW state. + +

+ +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. + +

+ +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. + +

Other details.

+ +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. + +

+ + +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. + +

+ +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. + +

+ +Note that only some products support the UNCONFIRMED state. In other +products, all new bugs will automatically start in the NEW state. + +

Things still to be done.

+ +There probably ought to be a way to get a bug back into the +UNCONFIRMED state, but there isn't yet. + +

+ +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. + +

+ +There should also be a way to automatically promote people to get the +"Can edit all aspects of any bug" permission. + +

+ +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. + +


+ +Last modified: Wed Feb 16 21:04:56 2000 + + 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 ^[^@, ]$, 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 ^[^@, ]*$, 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. %anythingelse% 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.%anythingelse% 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 "$prod product, but you are using $::FORM{$id}.\n"; + print "

Please click Back and try again.


\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 "$prod product, but you are using $prodcount{$prod}.\n"; - print "Please click Back and try again.
\n"; + print "

Please click Back and try again.


\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{

Review your votes


\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 "\n"; print " \n"; print " \n"; + + print "\n"; + print " \n"; + print " \n"; + + print "\n"; + print " \n"; + print " \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 " \n"; print " \n"; print " \n"; + print " \n"; + print " \n"; print " \n"; print " \n"; print ""; while ( MoreSQLData() ) { my ($product, $description, $disallownew, $votesperuser, - $bugs) = FetchSQLData(); + $maxvotesperbug, $votestoconfirm, $bugs) = FetchSQLData(); $description ||= "missing"; $disallownew = $disallownew ? 'closed' : 'open'; $bugs ||= 'none'; @@ -197,6 +213,8 @@ unless ($action) { print " \n"; print " \n"; print " \n"; + print " \n"; + print " \n"; print " \n"; print " \n"; print ""; @@ -227,7 +245,7 @@ if ($action eq 'add') { print "\n"; print "
The resolution field indicates what happened to this bug.
-
NEW +
UNCONFIRMED +
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. +
NEW
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 ASSIGNED, passed on to someone else, and remain @@ -60,8 +67,8 @@ certain status transitions are allowed.
-
No resolution yet. All bugs which are NEW or - ASSIGNED have the resolution set to blank. All other bugs +
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.
@@ -188,8 +195,7 @@ searching for bugs that have been resolved or verified, remember to set the status field appropriately.
-
Terry Weissman <terry@netscape.com>
-Last modified: Thu Jan 13 16:22:39 2000 +Last modified: Wed Feb 16 20:41:24 2000 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 .= "
Maximum votes per person:
Maximum votes a person can put on a single bug:
Number of votes a bug in this product needs to automatically get out of the UNCONFIRMED state:DescriptionStatusVotes
per
user
Max
Votes
per
bug
Votes
to
confirm
BugsAction
$description$disallownew$votesperuser$maxvotesperbug$votestoconfirm$bugsDelete
\n"; - EmitFormElements('', '', '', 0, 0); + EmitFormElements('', '', '', 0, 0, 10000, 0); print "\n"; print " \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 "\n"; print "
Version:
\n"; EmitFormElements($product, $description, $milestoneurl, $disallownew, - $votesperuser); + $votesperuser, $maxvotesperbug, $votestoconfirm); print "\n"; print " \n"; @@ -586,6 +611,8 @@ if ($action eq 'edit') { value_quote($milestoneurl) . "\">\n"; print "\n"; print "\n"; + print "\n"; + print "\n"; print "\n"; print "\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.
\n"; + $checkvotes = 1; } + if ($maxvotesperbug ne $maxvotesperbugold) { + SendSQL("UPDATE products + SET maxvotesperbug=$maxvotesperbug + WHERE product=" . SqlQuote($productold)); + print "Update max votes per bug.
\n"; + $checkvotes = 1; + } + + + if ($votestoconfirm ne $votestoconfirmold) { + SendSQL("UPDATE products + SET votestoconfirm=$votestoconfirm + WHERE product=" . SqlQuote($productold)); + print "Update votes to confirm.
\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.
\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{
Removed votes for bug $id 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{
Removed votes for bug $id 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{\n}; + } else { + print qq{\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 " \n"; - print " \n"; + EmitElement("user", $user); print "\n"; print " \n"; - print " \n"; - - print "\n"; - print " \n"; - print " \n"; - - print "\n"; - print " \n"; - print qq{\n"; + print " \n"; + print " \n"; + + print "\n"; + print " \n"; + print qq{\n"; + print "\n"; + print " \n"; + print " \n"; + print "\n"; + print " \n"; } - print "\n"; - print "\n"; - print " \n"; - print " \n"; - print "\n"; - print " \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 "\n"; print " \n"; $checked = ($checked) ? "CHECKED" : ""; print " \n"; + if ($editall) { + print "\n"; + print ""; + $blchecked = ($blchecked) ? "CHECKED" : ""; + print "\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:
@@ -261,14 +299,17 @@ if ($action eq 'list') { } print ""; } - print "\n"; - my $span = $candelete ? 3 : 2; - print qq{ + if ($editall) { + print "\n"; + my $span = $candelete ? 3 : 2; + print qq{ }; - print "
Edit components:$valueLogin name:
Real name:
Password:
Email notification:
Password:
Email notification:
Disable text:\n"; + print "
If non-empty, then the account will\n"; + print "be disabled, and this text should explain why.
Disable text:\n"; - print "
If non-empty, then the account will\n"; - print "be disabled, and this text should explain why.
", ucfirst($name), ": $description
Can turn this bit on for other users
Add a new user
\n"; + print ""; + } + print "\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 "\n"; print "\n"; - EmitFormElements('', '', '', 0, 'ExcludeSelfChanges', ''); + EmitFormElements('', '', '', 0, 0, 'ExcludeSelfChanges', ''); print "
\n
\n"; print "\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 "\n"; print "\n"; - EmitFormElements($user, $password, $realname, $groupset, + EmitFormElements($user, $password, $realname, $groupset, $blessgroupset, $emailnotification, $disabledtext); print "
\n"; print "\n"; - print "\n"; + if ($editall) { + print "\n"; + } print "\n"; print "\n"; + print "\n"; print "\n"; print "\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{$_}
\n"; - $groupset .= "+ $::FORM{$_}"; + $groupset .= " + $::FORM{$_}"; + } + my $blessgroupset = "0"; + foreach (keys %::FORM) { + next unless /^blbit_/; + #print "$_=$::FORM{$_}
\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.
\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.
\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.
\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.
\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 " - "; @@ -260,6 +259,26 @@ print " +"; + +if (UserInGroup("canedit") || UserInGroup("canconfirm")) { + SendSQL("SELECT votestoconfirm FROM products WHERE product = " . + SqlQuote($product)); + if (FetchOneColumn()) { + print qq{ + + + "; + } +} + + +print "
 
Initial state: +}; + print BuildPulldown("bug_status", + [[$::unconfirmedstate], ["NEW"]], + "NEW"); + print "
Assigned To: $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{ +

Sorry, not allowed.

+Only the owner or submitter of the bug, or a sufficiently +empowered user, may make that change to the $f field. + + + +
Old value:$oldvalue
New value:$newvalue
+
($reporterid eq $whoid || $ownerid eq $whoid || $qacontactid eq $whoid)
+ +

Click Back 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 comment on this change." . "

" . "Please press Back 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 ? "" : ""; my $endstrike = $opened ? "" : ""; $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 "

    \n"; + my $found = 0; + SendSQL("SELECT description FROM groups " . + "WHERE bit & $::usergroupset != 0 " . + "ORDER BY bit"); + while (MoreSQLData()) { + my ($description) = (FetchSQLData()); + print "
  • $description\n"; + $found = 1; + } + if ($found == 0) { + print "
  • (No extra permission bits have been set).\n"; + } + print "
\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{other users:\n}; + print "

    \n"; + SendSQL("SELECT description FROM groups " . + "WHERE bit & $blessgroupset != 0 " . + "ORDER BY bit"); + while (MoreSQLData()) { + my ($description) = (FetchSQLData()); + print "
  • $description\n"; + } + print "
\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) {
- -
}; + if ($savefunc) { + print qq{\n}; + } + print qq{\n}; } else { print "

Please choose from the above links which settings you wish to change.

"; } -- cgit v1.2.3-24-g4f1b