Commit c64d5111 authored by bugreport%peshkin.net's avatar bugreport%peshkin.net

Bug 147275 Rearchitect product groups

Patch by joel r=bbaetz,justdave a=justdave
parent e7720dcd
......@@ -167,6 +167,16 @@ sub UpdateParams {
delete $param{'usequip'};
}
# Change from old product groups to controls for group_control_map
# 2002-10-14 bug 147275 bugreport@peshkin.net
if (exists $param{'usebuggroups'} && !exists $param{'makeproductgroups'}) {
$param{'makeproductgroups'} = $param{'usebuggroups'};
}
if (exists $param{'usebuggroupsentry'}
&& !exists $param{'useentrygroupdefault'}) {
$param{'useentrygroupdefault'} = $param{'usebuggroupsentry'};
}
# --- DEFAULTS FOR NEW PARAMS ---
foreach my $item (@param_list) {
......
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Terry Weissman <terry@mozilla.org>
# Dawn Endico <endico@mozilla.org>
# Dan Mosedale <dmose@mozilla.org>
# Joe Robins <jmrobins@tgix.com>
# Jake <jake@bugzilla.org>
# J. Paul Reed <preed@sigkill.com>
# Bradley Baetz <bbaetz@student.usyd.edu.au>
# Christopher Aillon <christopher@aillon.com>
package Bugzilla::Constants;
use strict;
use base qw(Exporter);
@Bugzilla::Constants::EXPORT = qw(
CONTROLMAPNA
CONTROLMAPSHOWN
CONTROLMAPDEFAULT
CONTROLMAPMANDATORY
);
# CONSTANTS
#
# ControlMap constants for group_control_map.
# membercontol:othercontrol => meaning
# Na:Na => Bugs in this product may not be restricted to this
# group.
# Shown:Na => Members of the group may restrict bugs
# in this product to this group.
# Shown:Shown => Members of the group may restrict bugs
# in this product to this group.
# Anyone who can enter bugs in this product may initially
# restrict bugs in this product to this group.
# Shown:Mandatory => Members of the group may restrict bugs
# in this product to this group.
# Non-members who can enter bug in this product
# will be forced to restrict it.
# Default:Na => Members of the group may restrict bugs in this
# product to this group and do so by default.
# Default:Default => Members of the group may restrict bugs in this
# product to this group and do so by default and
# nonmembers have this option on entry.
# Default:Mandatory => Members of the group may restrict bugs in this
# product to this group and do so by default.
# Non-members who can enter bug in this product
# will be forced to restrict it.
# Mandatory:Mandatory => Bug will be forced into this group regardless.
# All other combinations are illegal.
use constant CONTROLMAPNA => 0;
use constant CONTROLMAPSHOWN => 1;
use constant CONTROLMAPDEFAULT => 2;
use constant CONTROLMAPMANDATORY => 3;
1;
......@@ -24,7 +24,7 @@
use strict;
use RelationSet;
use Bugzilla::Constants;
# Use the Attachment module to display attachments for the bug.
use Attachment;
......@@ -144,12 +144,9 @@ sub show_bug {
next;
}
if (Param("usebuggroupsentry")
&& GroupExists($product)
&& !UserInGroup($product))
{
if (!CanEnterProduct($product)) {
# If we're using bug groups to restrict entry on products, and
# this product has a bug group, and the user is not in that
# this product has an entry group, and the user is not in that
# group, we don't want to include that product in this list.
next;
}
......@@ -275,7 +272,7 @@ sub show_bug {
SendSQL("SELECT DISTINCT groups.id, name, description," .
" bug_group_map.group_id IS NOT NULL," .
" user_group_map.group_id IS NOT NULL," .
" isactive" .
" isactive, membercontrol, othercontrol" .
" FROM groups" .
" LEFT JOIN bug_group_map" .
" ON bug_group_map.group_id = groups.id" .
......@@ -284,35 +281,50 @@ sub show_bug {
" ON user_group_map.group_id = groups.id" .
" AND user_id = $::userid" .
" AND NOT isbless" .
" LEFT JOIN group_control_map" .
" ON group_control_map.group_id = groups.id" .
" AND group_control_map.product_id = " . $bug{'product_id'} .
" WHERE isbuggroup");
$user{'inallgroups'} = 1;
while (MoreSQLData()) {
my ($groupid, $name, $description, $ison, $ingroup, $isactive)
= FetchSQLData();
my ($groupid, $name, $description, $ison, $ingroup, $isactive,
$membercontrol, $othercontrol) = FetchSQLData();
$bug{'inagroup'} = 1 if ($ison);
$membercontrol ||= 0;
if ($isactive && ($membercontrol == CONTROLMAPMANDATORY)) {
$bug{'inagroup'} = 1;
}
# For product groups, we only want to display the checkbox if either
# (1) The bit is already set, or
# (2) The user is in the group, but either:
# (a) The group is a product group for the current product, or
# (b) The group name isn't a product name
# This means that all product groups will be skipped, but
# non-product bug groups will still be displayed.
if($ison ||
($isactive && ($ingroup && (!Param("usebuggroups") || ($name eq $bug{'product'}) ||
(!defined $::proddesc{$name})))))
# (1) The bit is set and not required, or
# (2) The group is Shown or Default for members and
# the user is a member of the group.
if ($ison ||
($isactive && $ingroup
&& (($membercontrol == CONTROLMAPDEFAULT)
|| ($membercontrol == CONTROLMAPSHOWN))
))
{
$user{'inallgroups'} &= $ingroup;
my $mandatory;
if ($isactive && ($membercontrol == CONTROLMAPMANDATORY)) {
$mandatory = 1;
} else {
$mandatory = 0;
}
if (($ison) || ($ingroup)) {
push (@groups, { "bit" => $groupid,
"ison" => $ison,
"ingroup" => $ingroup,
"mandatory" => $mandatory,
"description" => $description });
}
}
}
# If the bug is restricted to a group, get flags that allow
# the user to set whether or not the reporter
......
......@@ -728,7 +728,6 @@ $vars->{'order'} = $order;
my $login = $::COOKIE{'Bugzilla_login'};
$vars->{'caneditbugs'} = UserInGroup('editbugs');
$vars->{'usebuggroups'} = Param('usebuggroups');
# Whether or not this user is authorized to move bugs to another installation.
$vars->{'ismover'} = 1
......
......@@ -112,6 +112,7 @@
use strict;
use vars qw( $db_name %answer );
use Bugzilla::Constants;
###########################################################################
# Non-interactive override
......@@ -1716,6 +1717,17 @@ $table{quips} =
userid mediumint not null default 0,
quip text not null';
$table{group_control_map} =
'group_id mediumint not null,
product_id mediumint not null,
entry tinyint not null,
membercontrol tinyint not null,
othercontrol tinyint not null,
canedit tinyint not null,
unique(product_id, group_id),
index(group_id)';
###########################################################################
# Create tables
###########################################################################
......@@ -2954,7 +2966,7 @@ if (GetFieldDef("logincookies", "hostname")) {
AddField("logincookies", "ipaddr", "varchar(40) NOT NULL");
}
# 2002-05-10 - enhanchment bug 143826
# 2002-08-19 - bugreport@peshkin.net bug 143826
# Add private comments and private attachments on less-private bugs
AddField('longdescs', 'isprivate', 'tinyint not null default 0');
AddField('attachments', 'isprivate', 'tinyint not null default 0');
......@@ -3121,7 +3133,7 @@ if (($fielddef = GetFieldDef("attachments", "creation_ts")) &&
ChangeFieldType("attachments", "creation_ts", "datetime NOT NULL");
}
# 2002-08-XX - bugreport@peshkin.net - bug 157756
# 2002-09-22 - bugreport@peshkin.net - bug 157756
#
# If the whole groups system is new, but the installation isn't,
# convert all the old groupset groups, etc...
......@@ -3464,6 +3476,54 @@ if (TableExists("attachstatuses") && TableExists("attachstatusdefs")) {
print "done.\n";
}
# 2002-11-24 - bugreport@peshkin.net - bug 147275
#
if (Param('makeproductgroups')) {
# If makeproductgroups is enabled and group_control_map is empty,
# backward-compatbility usebuggroups-equivalent records should
# be created.
my $entry = Param('useentrygroupdefault');
$sth = $dbh->prepare("SELECT COUNT(*) FROM group_control_map");
$sth->execute();
my ($mapcnt) = $sth->fetchrow_array();
if ($mapcnt == 0) {
# Initially populate group_control_map.
# First, get all the existing products and their groups.
$sth = $dbh->prepare("SELECT groups.id, products.id, groups.name, " .
"products.name FROM groups, products " .
"WHERE isbuggroup != 0 AND isactive != 0");
$sth->execute();
while (my ($groupid, $productid, $groupname, $productname)
= $sth->fetchrow_array()) {
if ($groupname eq $productname) {
# Product and group have same name.
$dbh->do("INSERT INTO group_control_map " .
"(group_id, product_id, entry, membercontrol, " .
"othercontrol, canedit) " .
"VALUES ($groupid, $productid, $entry, " .
CONTROLMAPDEFAULT . ", " .
CONTROLMAPNA . ", 0)");
} else {
# See if this group is a product group at all.
my $sth2 = $dbh->prepare("SELECT id FROM products WHERE name = " .
$dbh->quote($groupname));
$sth2->execute();
my ($id) = $sth2->fetchrow_array();
if (!$id) {
# If there is no product with the same name as this
# group, then it is permitted for all products.
$dbh->do("INSERT INTO group_control_map " .
"(group_id, product_id, entry, membercontrol, " .
"othercontrol, canedit) " .
"VALUES ($groupid, $productid, 0, " .
CONTROLMAPSHOWN . ", " .
CONTROLMAPNA . ", 0)");
}
}
}
}
}
# If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment.
#
......@@ -3793,3 +3853,4 @@ $dbh->do("UPDATE components SET initialowner = $adminuid WHERE initialowner = 0"
unlink "data/versioncache";
print "Reminder: Bugzilla now requires version 8.7 or later of sendmail.\n" unless $silent;
......@@ -244,7 +244,7 @@ sub check_netmask {
},
{
name => 'usebuggroups',
name => 'makeproductgroups',
desc => 'If this is on, Bugzilla will associate a bug group with each ' .
'product in the database, and use it for querying bugs.',
type => 'b',
......@@ -252,9 +252,9 @@ sub check_netmask {
},
{
name => 'usebuggroupsentry',
name => 'useentrygroupdefault',
desc => 'If this is on, Bugzilla will use product bug groups to restrict ' .
'who can enter bugs. Requires usebuggroups to be on as well.',
'who can enter bugs. Requires makeproductgroups to be on as well.',
type => 'b',
default => 0
},
......
......@@ -42,11 +42,11 @@ if (!defined $::FORM{'product'}) {
# Reference to a subset of %::proddesc, which the user is allowed to see
my %products;
if (Param("usebuggroups")) {
if (AnyDefaultGroups()) {
# OK, now only add products the user can see
confirm_login() unless $::userid;
foreach my $p (@::legal_product) {
if (!GroupExists($p) || UserInGroup($p)) {
if (CanEnterProduct($p)) {
$products{$p} = $::proddesc{$p};
}
}
......@@ -88,11 +88,8 @@ if (!$product_id) {
}
# Make sure the user is authorized to access this product.
if (Param("usebuggroups") && GroupExists($product)) {
confirm_login() unless $::userid;
UserInGroup($product)
CanEnterProduct($product)
|| ThrowUserError("product_access_denied");
}
######################################################################
# End Data/Security Validation
......
......@@ -27,6 +27,7 @@
use strict;
use lib ".";
use Bugzilla::Constants;
require "CGI.pl";
ConnectToDatabase();
......@@ -117,9 +118,9 @@ unless ($action) {
while (MoreSQLData()) {
my ($groupid, $name, $desc, $regexp, $isactive, $isbuggroup) = FetchSQLData();
print "<tr>\n";
print "<td>$name</td>\n";
print "<td>$desc</td>\n";
print "<td>$regexp&nbsp</td>\n";
print "<td>" . html_quote($name) . "</td>\n";
print "<td>" . html_quote($desc) . "</td>\n";
print "<td>" . html_quote($regexp) . "&nbsp</td>\n";
print "<td align=center>";
print "X" if (($isactive != 0) && ($isbuggroup != 0));
print "&nbsp</td>\n";
......@@ -185,22 +186,27 @@ if ($action eq 'changeform') {
print "<TABLE BORDER=1 CELLPADDING=4>";
print "<TR><TH>Group:</TH><TD>";
if ($isbuggroup == 0) {
print "$name";
print html_quote($name);
} else {
print "<INPUT TYPE=HIDDEN NAME=\"oldname\" VALUE=$name>
<INPUT SIZE=60 NAME=\"name\" VALUE=\"$name\">";
print "<INPUT TYPE=HIDDEN NAME=\"oldname\" VALUE=" .
html_quote($name) . ">
<INPUT SIZE=60 NAME=\"name\" VALUE=\"" . html_quote($name) . "\">";
}
print "</TD></TR><TR><TH>Description:</TH><TD>";
if ($isbuggroup == 0) {
print "$description";
print html_quote($description);
} else {
print "<INPUT TYPE=HIDDEN NAME=\"olddesc\" VALUE=\"$description\">
<INPUT SIZE=70 NAME=\"desc\" VALUE=\"$description\">";
print "<INPUT TYPE=HIDDEN NAME=\"olddesc\" VALUE=\"" .
html_quote($description) . "\">
<INPUT SIZE=70 NAME=\"desc\" VALUE=\"" .
html_quote($description) . "\">";
}
print "</TD></TR><TR>
<TH>User Regexp:</TH><TD>";
print "<INPUT TYPE=HIDDEN NAME=\"oldrexp\" VALUE=\"$rexp\">
<INPUT SIZE=40 NAME=\"rexp\" VALUE=\"$rexp\"></TD></TR>";
print "<INPUT TYPE=HIDDEN NAME=\"oldrexp\" VALUE=\"" .
html_quote($rexp) . "\">
<INPUT SIZE=40 NAME=\"rexp\" VALUE=\"" .
html_quote($rexp) . "\"></TD></TR>";
if ($isbuggroup == 1) {
print "<TR><TH>Use For Bugs:</TH><TD>
<INPUT TYPE=checkbox NAME =\"isactive\" VALUE=1 " . (($isactive == 1) ? "CHECKED" : "") . ">
......@@ -252,8 +258,8 @@ if ($action eq 'changeform') {
print "<INPUT TYPE=HIDDEN NAME=\"oldbless-$grpid\" VALUE=$blessmember></TD>";
print "<TD><INPUT TYPE=checkbox NAME=\"grp-$grpid\" $grpchecked VALUE=1>";
print "<INPUT TYPE=HIDDEN NAME=\"oldgrp-$grpid\" VALUE=$grpmember></TD>";
print "<TD><B>$grpnam</B></TD>";
print "<TD>$grpdesc</TD>";
print "<TD><B>" . html_quote($grpnam) . "</B></TD>";
print "<TD>" . html_quote($grpdesc) . "</TD>";
print "</TR>\n";
}
......@@ -290,6 +296,10 @@ if ($action eq 'add') {
print "<td><input size=30 name=\"regexp\"></td>\n";
print "<td><input type=\"checkbox\" name=\"isactive\" value=\"1\" checked></td>\n";
print "</TR></TABLE>\n<HR>\n";
print "<input type=\"checkbox\" name=\"insertnew\" value=\"1\"";
print " checked" if Param("makeproductgroups");
print ">\n";
print "Insert new group into all existing products.<P>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Add\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"new\">\n";
print "</FORM>";
......@@ -308,9 +318,13 @@ to this group, although bugs already in the group will remain in the group.
Doing so is a much less drastic way to stop a group from growing
than deleting the group would be. <b>Note: If you are creating a group, you
probably want it to be usable for bugs, in which case you should leave this checked.</b><p>";
print "<b>User RegExp</b> is optional, and if filled in, will automatically
grant membership to this group to anyone creating a new account with an
email address that matches this regular expression.<p>";
print "<b>User RegExp</b> is optional, and if filled in, will ";
print "automatically grant membership to this group to anyone with an ";
print "email address that matches this regular expression.<p>\n";
print "By default, the new group will be associated with existing ";
print "products. Unchecking the \"Insert new group into all existing ";
print "products\" option will prevent this and make the group become ";
print "visible only when its controls have been added to a product.<P>\n";
PutTrailer("<a href=editgroups.cgi>Back to the group list</a>");
exit;
......@@ -384,6 +398,16 @@ if ($action eq 'new') {
VALUES ($admin, $gid, 0)");
SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless)
VALUES ($admin, $gid, 1)");
# Permit all existing products to use the new group if makeproductgroups.
if ($::FORM{insertnew}) {
SendSQL("INSERT INTO group_control_map " .
"(group_id, product_id, entry, membercontrol, " .
"othercontrol, canedit) " .
"SELECT $gid, products.id, 0, " .
CONTROLMAPSHOWN . ", " .
CONTROLMAPNA . ", 0 " .
"FROM products");
}
print "OK, done.<p>\n";
PutTrailer("<a href=\"editgroups.cgi?action=add\">Add another group</a>",
"<a href=\"editgroups.cgi\">Back to the group list</a>");
......@@ -543,6 +567,7 @@ if ($action eq 'delete') {
SendSQL("DELETE FROM user_group_map WHERE group_id = $gid");
SendSQL("DELETE FROM group_group_map WHERE grantor_id = $gid");
SendSQL("DELETE FROM bug_group_map WHERE group_id = $gid");
SendSQL("DELETE FROM group_control_map WHERE group_id = $gid");
SendSQL("DELETE FROM groups WHERE id = $gid");
print "<B>Group $gid has been deleted.</B><BR>";
......
......@@ -29,7 +29,8 @@
use strict;
use lib ".";
use vars qw ($template $vars);
use Bugzilla::Constants;
require "CGI.pl";
require "globals.pl";
......@@ -38,9 +39,16 @@ require "globals.pl";
sub sillyness {
my $zz;
$zz = %::MFORM;
$zz = $::unconfirmedstate;
}
my %ctl = (
&::CONTROLMAPNA => 'NA',
&::CONTROLMAPSHOWN => 'Shown',
&::CONTROLMAPDEFAULT => 'Default',
&::CONTROLMAPMANDATORY => 'Mandatory'
);
# TestProduct: just returns if the specified product does exists
# CheckProduct: same check, optionally emit an error text
......@@ -187,10 +195,9 @@ unless (UserInGroup("editcomponents")) {
#
my $product = trim($::FORM{product} || '');
my $action = trim($::FORM{action} || '');
my $headerdone = 0;
my $localtrailer = "<A HREF=\"editproducts.cgi\">edit</A> more products";
#
# action='' -> Show nice list of products
#
......@@ -342,7 +349,7 @@ if ($action eq 'new') {
# If we're using bug groups, then we need to create a group for this
# product as well. -JMR, 2/16/00
if(Param("usebuggroups")) {
if(Param("makeproductgroups")) {
# Next we insert into the groups table
SendSQL("INSERT INTO groups " .
"(name, description, isbuggroup, last_changed) " .
......@@ -357,6 +364,35 @@ if ($action eq 'new') {
SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless)
VALUES ($admin, $gid, 1)");
# Associate the new group and new product.
SendSQL("INSERT INTO group_control_map " .
"(group_id, product_id, entry, " .
"membercontrol, othercontrol, canedit) VALUES " .
"($gid, $product_id, " . Param("useentrygroupdefault") .
", " . CONTROLMAPDEFAULT . ", " .
CONTROLMAPNA . ", 0)");
# Permit the new product to use any non-product bug groups.
SendSQL("SELECT groups.id, products.id IS NULL FROM groups " .
"LEFT JOIN products " .
"ON groups.name = products.name " .
"WHERE isactive != 0 AND isbuggroup != 0 ");
while (MoreSQLData()) {
my ($grpid, $nonproductgroup) = FetchSQLData();
if ($nonproductgroup) {
PushGlobalSQLState();
SendSQL("INSERT INTO group_control_map " .
"(group_id, product_id, entry, " .
"membercontrol, othercontrol, canedit) VALUES " .
"entry, control, canedit) VALUES " .
"($grpid, $product_id, 0, " .
CONTROLMAPSHOWN . ", " .
CONTROLMAPNA . ", 0)");
PopGlobalSQLState();
}
}
}
# Make versioncache flush
......@@ -510,7 +546,7 @@ one.";
print "<INPUT TYPE=SUBMIT VALUE=\"Yes, delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"product\" VALUE=\"" .
value_quote($product) . "\">\n";
html_quote($product) . "\">\n";
print "</FORM>";
PutTrailer($localtrailer);
......@@ -538,6 +574,7 @@ if ($action eq 'delete') {
versions WRITE,
products WRITE,
groups WRITE,
group_control_map WRITE,
profiles WRITE,
milestones WRITE,
flaginclusions WRITE,
......@@ -583,6 +620,10 @@ if ($action eq 'delete') {
WHERE product_id=$product_id");
print "Milestones deleted.<BR>\n";
SendSQL("DELETE FROM group_control_map
WHERE product_id=$product_id");
print "Group controls deleted.<BR>\n";
SendSQL("DELETE FROM flaginclusions
WHERE product_id=$product_id");
SendSQL("DELETE FROM flagexclusions
......@@ -593,7 +634,6 @@ if ($action eq 'delete') {
WHERE id=$product_id");
print "Product '$product' deleted.<BR>\n";
SendSQL("UNLOCK TABLES");
unlink "data/versioncache";
......@@ -693,6 +733,27 @@ if ($action eq 'edit') {
}
print "</TD>\n</TR><TR>\n";
print " <TH ALIGN=\"right\" VALIGN=\"top\"><A HREF=\"editproducts.cgi?action=editgroupcontrols&product=", url_quote($product), "\">Edit Group Access Controls</A></TH>\n";
print "<TD>\n";
SendSQL("SELECT id, name, isactive, entry, membercontrol, othercontrol, canedit " .
"FROM groups, " .
"group_control_map " .
"WHERE group_control_map.group_id = id AND product_id = $product_id " .
"AND isbuggroup != 0 ORDER BY name");
while (MoreSQLData()) {
my ($id, $name, $isactive, $entry, $membercontrol, $othercontrol, $canedit)
= FetchSQLData();
print "<B>" . html_quote($name) . ":</B> ";
if ($isactive) {
print $ctl{$membercontrol} . "/" . $ctl{$othercontrol};
print ", ENTRY" if $entry;
print ", CANEDIT" if $canedit;
} else {
print "DISABLED";
}
print "<BR>\n";
}
print "</TD>\n</TR><TR>\n";
print " <TH ALIGN=\"right\">Bugs:</TH>\n";
print " <TD>";
SendSQL("SELECT count(bug_id),product_id
......@@ -706,11 +767,11 @@ if ($action eq 'edit') {
print "</TD>\n</TR></TABLE>\n";
print "<INPUT TYPE=HIDDEN NAME=\"productold\" VALUE=\"" .
value_quote($product) . "\">\n";
html_quote($product) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"descriptionold\" VALUE=\"" .
value_quote($description) . "\">\n";
html_quote($description) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"milestoneurlold\" VALUE=\"" .
value_quote($milestoneurl) . "\">\n";
html_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";
......@@ -729,6 +790,230 @@ if ($action eq 'edit') {
}
#
# action='updategroupcontrols' -> update the product
#
if ($action eq 'updategroupcontrols') {
my $product_id = get_product_id($product);
my @now_na = ();
my @now_mandatory = ();
foreach my $f (keys %::FORM) {
if ($f =~ /^membercontrol_(\d+)$/) {
my $id = $1;
if ($::FORM{$f} == CONTROLMAPNA) {
push @now_na,$id;
} elsif ($::FORM{$f} == CONTROLMAPMANDATORY) {
push @now_mandatory,$id;
}
}
}
if (!($::FORM{'confirmed'})) {
$vars->{'form'} = \%::FORM;
$vars->{'mform'} = \%::MFORM;
my @na_groups = ();
if (@now_na) {
SendSQL("SELECT groups.name, COUNT(bugs.bug_id)
FROM bugs, bug_group_map, groups
WHERE groups.id IN(" . join(',',@now_na) . ")
AND bug_group_map.group_id = groups.id
AND bug_group_map.bug_id = bugs.bug_id
AND bugs.product_id = $product_id
GROUP BY groups.name");
while (MoreSQLData()) {
my ($groupname, $bugcount) = FetchSQLData();
my %g = ();
$g{'name'} = $groupname;
$g{'count'} = $bugcount;
push @na_groups,\%g;
}
}
my @mandatory_groups = ();
if (@now_mandatory) {
SendSQL("SELECT groups.name, COUNT(bugs.bug_id)
FROM bugs, groups
LEFT JOIN bug_group_map
ON bug_group_map.group_id = groups.id
AND bug_group_map.bug_id = bugs.bug_id
WHERE groups.id IN(" . join(',',@now_mandatory) . ")
AND bugs.product_id = $product_id
AND bug_group_map.bug_id IS NULL
GROUP BY groups.name");
while (MoreSQLData()) {
my ($groupname, $bugcount) = FetchSQLData();
my %g = ();
$g{'name'} = $groupname;
$g{'count'} = $bugcount;
push @mandatory_groups,\%g;
}
}
if ((@na_groups) || (@mandatory_groups)) {
$vars->{'product'} = $product;
$vars->{'na_groups'} = \@na_groups;
$vars->{'mandatory_groups'} = \@mandatory_groups;
$template->process("admin/products/groupcontrol/confirm-edit.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
exit;
}
}
PutHeader("Update group access controls for product \"$product\"");
$headerdone = 1;
SendSQL("SELECT id, name FROM groups " .
"WHERE isbuggroup != 0 AND isactive != 0");
while (MoreSQLData()){
my ($groupid, $groupname) = FetchSQLData();
my $newmembercontrol = $::FORM{"membercontrol_$groupid"} || 0;
my $newothercontrol = $::FORM{"othercontrol_$groupid"} || 0;
# Legality of control combination is a function of
# membercontrol\othercontrol
# NA SH DE MA
# NA + - - -
# SH + + + +
# DE + - + +
# MA - - - +
unless (($newmembercontrol == $newothercontrol)
|| ($newmembercontrol == CONTROLMAPSHOWN)
|| (($newmembercontrol == CONTROLMAPDEFAULT)
&& ($newothercontrol != CONTROLMAPSHOWN))) {
ThrowUserError('illegal_group_control_combination',
{groupname => $groupname,
header_done => 1});
}
}
SendSQL("LOCK TABLES groups READ,
group_control_map WRITE,
bugs WRITE,
bugs_activity WRITE,
bug_group_map WRITE,
fielddefs READ");
SendSQL("SELECT id, name, entry, membercontrol, othercontrol, canedit " .
"FROM groups " .
"LEFT JOIN group_control_map " .
"ON group_control_map.group_id = id AND product_id = $product_id " .
"WHERE isbuggroup != 0 AND isactive != 0");
while (MoreSQLData()) {
my ($groupid, $groupname, $entry, $membercontrol,
$othercontrol, $canedit) = FetchSQLData();
my $newentry = $::FORM{"entry_$groupid"} || 0;
my $newmembercontrol = $::FORM{"membercontrol_$groupid"} || 0;
my $newothercontrol = $::FORM{"othercontrol_$groupid"} || 0;
my $newcanedit = $::FORM{"canedit_$groupid"} || 0;
my $oldentry = $entry;
$entry = $entry || 0;
$membercontrol = $membercontrol || 0;
$othercontrol = $othercontrol || 0;
$canedit = $canedit || 0;
detaint_natural($newentry);
detaint_natural($newothercontrol);
detaint_natural($newmembercontrol);
detaint_natural($newcanedit);
if ((!defined($oldentry)) &&
(($newentry) || ($newmembercontrol) || ($newcanedit))) {
PushGlobalSQLState();
SendSQL("INSERT INTO group_control_map " .
"(group_id, product_id, entry, " .
"membercontrol, othercontrol, canedit) " .
"VALUES " .
"($groupid, $product_id, $newentry, " .
"$newmembercontrol, $newothercontrol, $newcanedit)");
PopGlobalSQLState();
} elsif (($newentry != $entry)
|| ($newmembercontrol != $membercontrol)
|| ($newothercontrol != $othercontrol)
|| ($newcanedit != $canedit)) {
PushGlobalSQLState();
SendSQL("UPDATE group_control_map " .
"SET entry = $newentry, " .
"membercontrol = $newmembercontrol, " .
"othercontrol = $newothercontrol, " .
"canedit = $newcanedit " .
"WHERE group_id = $groupid " .
"AND product_id = $product_id");
PopGlobalSQLState();
}
if (($newentry == 0) && ($newmembercontrol == 0)
&& ($newothercontrol == 0) && ($newcanedit == 0)) {
PushGlobalSQLState();
SendSQL("DELETE FROM group_control_map " .
"WHERE group_id = $groupid " .
"AND product_id = $product_id");
PopGlobalSQLState();
}
}
foreach my $groupid (@now_na) {
print "Removing bugs from NA group "
. html_quote(GroupIdToName($groupid)) . "<P>\n";
my $count = 0;
SendSQL("SELECT bugs.bug_id,
(lastdiffed >= delta_ts)
FROM bugs, bug_group_map
WHERE group_id = $groupid
AND bug_group_map.bug_id = bugs.bug_id
AND bugs.product_id = $product_id
ORDER BY bugs.bug_id");
while (MoreSQLData()) {
my ($bugid, $mailiscurrent) = FetchSQLData();
PushGlobalSQLState();
SendSQL("DELETE FROM bug_group_map WHERE
bug_id = $bugid AND group_id = $groupid");
SendSQL("SELECT name, NOW() FROM groups WHERE id = $groupid");
my ($removed, $timestamp) = FetchSQLData();
LogActivityEntry($bugid, "bug_group", $removed, "",
$::userid, $timestamp);
if ($mailiscurrent != 0) {
SendSQL("UPDATE bugs SET lastdiffed = " . SqlQuote($timestamp)
. " WHERE bug_id = $bugid");
}
SendSQL("UPDATE bugs SET delta_ts = " . SqlQuote($timestamp)
. " WHERE bug_id = $bugid");
PopGlobalSQLState();
$count++;
}
print "dropped $count bugs<p>\n";
}
foreach my $groupid (@now_mandatory) {
print "Adding bugs to Mandatory group "
. html_quote(GroupIdToName($groupid)) . "<P>\n";
my $count = 0;
SendSQL("SELECT bugs.bug_id,
(lastdiffed >= delta_ts)
FROM bugs
LEFT JOIN bug_group_map
ON bug_group_map.bug_id = bugs.bug_id
AND group_id = $groupid
WHERE bugs.product_id = $product_id
AND bug_group_map.bug_id IS NULL
ORDER BY bugs.bug_id");
while (MoreSQLData()) {
my ($bugid, $mailiscurrent) = FetchSQLData();
PushGlobalSQLState();
SendSQL("INSERT INTO bug_group_map (bug_id, group_id)
VALUES ($bugid, $groupid)");
SendSQL("SELECT name, NOW() FROM groups WHERE id = $groupid");
my ($added, $timestamp) = FetchSQLData();
LogActivityEntry($bugid, "bug_group", "", $added,
$::userid, $timestamp);
if ($mailiscurrent != 0) {
SendSQL("UPDATE bugs SET lastdiffed = " . SqlQuote($timestamp)
. " WHERE bug_id = $bugid");
}
SendSQL("UPDATE bugs SET delta_ts = " . SqlQuote($timestamp)
. " WHERE bug_id = $bugid");
PopGlobalSQLState();
$count++;
}
print "added $count bugs<p>\n";
}
SendSQL("UNLOCK TABLES");
print "Group control updates done<P>\n";
PutTrailer($localtrailer);
exit;
}
#
# action='update' -> update the product
......@@ -770,6 +1055,7 @@ if ($action eq 'update') {
SendSQL("LOCK TABLES products WRITE,
versions READ,
groups WRITE,
group_control_map WRITE,
profiles WRITE,
milestones READ");
......@@ -863,9 +1149,9 @@ if ($action eq 'update') {
}
SendSQL("UPDATE products SET name=$qp WHERE id=$product_id");
# Need to do an update to groups as well. If there is a corresponding
# bug group, whether usebuggroups is currently set or not, we want to
# update it so it will match in the future. If there is no group, this
# Need to do an update to groups as well. If there is
# a corresponding bug group, we want to update it so it will
# match in the future. If there is no group, this
# update statement will do nothing, so no harm done. -JMR, 3/8/00
SendSQL("UPDATE groups " .
"SET name = $qp, " .
......@@ -945,6 +1231,235 @@ if ($action eq 'update') {
exit;
}
#
# action='editgroupcontrols' -> update product group controls
#
if ($action eq 'editgroupcontrols') {
my $product_id = get_product_id($product);
# Display a group if it is either enabled or has bugs for this product.
SendSQL("SELECT id, name, entry, membercontrol, othercontrol, canedit, " .
"isactive, COUNT(bugs.bug_id) " .
"FROM groups " .
"LEFT JOIN group_control_map " .
"ON group_control_map.group_id = id " .
"AND group_control_map.product_id = $product_id " .
"LEFT JOIN bug_group_map " .
"ON bug_group_map.group_id = groups.id " .
"LEFT JOIN bugs " .
"ON bugs.bug_id = bug_group_map.bug_id " .
"AND bugs.product_id = $product_id " .
"WHERE isbuggroup != 0 " .
"AND (isactive != 0 OR entry IS NOT NULL " .
"OR bugs.bug_id IS NOT NULL) " .
"GROUP BY name");
my @groups = ();
while (MoreSQLData()) {
my %group = ();
my ($groupid, $groupname, $entry, $membercontrol, $othercontrol,
$canedit, $isactive, $bugcount) = FetchSQLData();
$group{'id'} = $groupid;
$group{'name'} = $groupname;
$group{'entry'} = $entry;
$group{'membercontrol'} = $membercontrol;
$group{'othercontrol'} = $othercontrol;
$group{'canedit'} = $canedit;
$group{'isactive'} = $isactive;
$group{'bugcount'} = $bugcount;
push @groups,\%group;
}
$vars->{'header_done'} = $headerdone;
$vars->{'product'} = $product;
$vars->{'groups'} = \@groups;
$vars->{'const'} = {
'CONTROLMAPNA' => CONTROLMAPNA,
'CONTROLMAPSHOWN' => CONTROLMAPSHOWN,
'CONTROLMAPDEFAULT' => CONTROLMAPDEFAULT,
'CONTROLMAPMANDATORY' => CONTROLMAPMANDATORY,
};
$template->process("admin/products/groupcontrol/edit.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
exit;
print "<!-- \n";
print "<script type=\"text/javascript\">\n";
print "function hide(id) {\n";
print " id.visibility = 0\n";
print " alert(id)\n";
print "}\n";
print "</script>";
print " -->\n";
print "<STYLE type=\"text/css\">\n";
print " .hstyle { visibility: visible; color: red; }\n";
print "</STYLE>\n";
print "<FORM METHOD=POST ACTION=editproducts.cgi>\n";
print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR BGCOLOR=\"#6666FF\">\n";
print " <TH ALIGN=\"left\">Group</TH>\n";
print " <TH ALIGN=\"left\">Entry</TH>\n";
print " <TH ALIGN=\"left\">MemberControl</TH>\n";
print " <TH ALIGN=\"left\">OtherControl</TH>\n";
print " <TH ALIGN=\"left\">Canedit</TH>\n";
while (MoreSQLData()) {
print "</TR>\n";
my ($groupid, $groupname, $entry, $membercontrol, $othercontrol,
$canedit) = FetchSQLData();
print "<TR id=\"row_$groupname\" class=\"hstyle\" ";
print "onload=\"document.row.row_$groupname.color=green\">\n";
print " <TD>\n";
print " $groupname\n";
print " </TD><TD>\n";
$entry |= 0;
print " <INPUT TYPE=CHECKBOX NAME=\"entry_$groupid\" VALUE=1";
print " CHECKED " if $entry;
print ">\n";
print " </TD><TD>\n";
$membercontrol |= 0;
$othercontrol |= 0;
print " <SELECT NAME=\"membercontrol_$groupid\">\n";
print " <OPTION VALUE=" . CONTROLMAPNA;
print " selected=\"selected\"" if ($membercontrol == CONTROLMAPNA);
print ">NA</OPTION>\n";
print " <OPTION VALUE=" . CONTROLMAPSHOWN;
print " selected=\"selected\"" if ($membercontrol == CONTROLMAPSHOWN);
print ">Shown</OPTION>\n";
print " <OPTION VALUE=" . CONTROLMAPDEFAULT;
print " selected=\"selected\"" if ($membercontrol == CONTROLMAPDEFAULT);
print ">Default</OPTION>\n";
print " <OPTION VALUE=" . CONTROLMAPMANDATORY;
print " selected=\"selected\"" if ($membercontrol == CONTROLMAPMANDATORY);
print ">Mandatory</OPTION>\n";
print "</SELECT>\n";
print " </TD><TD>\n";
print " <SELECT NAME=\"othercontrol_$groupid\">\n";
print " <OPTION VALUE=" . CONTROLMAPNA;
print " selected=\"selected\"" if ($othercontrol == CONTROLMAPNA);
print ">NA</OPTION>\n";
print " <OPTION VALUE=" . CONTROLMAPSHOWN;
print " selected=\"selected\"" if ($othercontrol == CONTROLMAPSHOWN);
print ">Shown</OPTION>\n";
print " <OPTION VALUE=" . CONTROLMAPDEFAULT;
print " selected=\"selected\"" if ($othercontrol == CONTROLMAPDEFAULT);
print ">Default</OPTION>\n";
print " <OPTION VALUE=" . CONTROLMAPMANDATORY;
print " selected=\"selected\"" if ($othercontrol == CONTROLMAPMANDATORY);
print ">Mandatory</OPTION>\n";
print "</SELECT>\n";
print " </TD><TD>\n";
$canedit |= 0;
print " <INPUT TYPE=CHECKBOX NAME=\"canedit_$groupid\" VALUE=1";
print " CHECKED " if $canedit;
print ">\n";
}
print "</TR>\n";
print "</TABLE><BR>";
print "Add controls to the panel above:<BR>\n";
print "<SELECT NAME=\"newgroups\" SIZE=\"10\" MULTIPLE=\"MULTIPLE\">\n";
SendSQL("SELECT id, name " .
"FROM groups " .
"LEFT JOIN group_control_map " .
"ON group_control_map.group_id = id AND product_id = $product_id " .
"WHERE canedit IS NULL AND isbuggroup != 0 AND isactive != 0 " .
"ORDER BY name");
while (MoreSQLData()) {
my ($groupid, $groupname) = FetchSQLData();
print "<OPTION VALUE=\"$groupid\">$groupname</OPTION>\n";
}
print "</SELECT><BR><BR>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Update\">\n";
print "<INPUT TYPE=RESET>\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"updategroupcontrols\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"product\" VALUE=\"$product\">\n";
print "</FORM>\n";
print "<P>note: Any group controls Set to NA/NA with no other checkboxes ";
print "will automatically be removed from the panel the next time ";
print "update is clicked.\n";
print "<P>These settings control the relationship of the groups to this ";
print "product.\n";
print "<P>If any group has <B>Entry</B> selected, then this product will ";
print "restrict bug entry to only those users who are members of all the ";
print "groups with entry selected.\n";
print "<P>If any group has <B>Canedit</B> selected, then this product ";
print "will be read-only for any users who are not members of all of ";
print "the groups with Canedit selected. ONLY users who are members of ";
print "all the canedit groups will be able to edit. This is an additional ";
print "restriction that further restricts what can be edited by a user.\n";
print "<P>The <B>MemberControl</B> and <B>OtherControl</B> fields ";
print "indicate which bugs will be placed in ";
print "this group according to the following definitions.\n";
print "<BR><TABLE BORDER=1>";
print "<TR>";
print "<TH>MemberControl</TH><TH>OtherControl</TH><TH>Interpretation</TH>";
print "</TR><TR>";
print "<TD>NA</TD>\n";
print "<TD>NA</TD>\n";
print "<TD>Bugs in this product are never associated with this group.</TD>\n";
print "</TR><TR>";
print "<TD>Shown</TD>\n";
print "<TD>NA</TD>\n";
print "<TD>Bugs in this product are permitted to be restricted to this ";
print "group. Users who are a member of this group will be able ";
print "to place bugs in this group.</TD>\n";
print "</TR><TR>";
print "<TD>Shown</TD>\n";
print "<TD>Shown</TD>\n";
print "<TD>Bugs in this product can be placed in this group by anyone ";
print "with permission to edit the bug even if they are not a member ";
print "of this group.</TD>\n";
print "</TR><TR>";
print "<TD>Shown</TD>\n";
print "<TD>Default</TD>\n";
print "<TD>Bugs in this product can be placed in this group by anyone ";
print "with permission to edit the bug even if they are not a member ";
print "of this group. Non-members place bugs in this group by default.";
print "</TD>\n";
print "</TR><TR>";
print "<TD>Shown</TD>\n";
print "<TD>Mandatory</TD>\n";
print "<TD>Bugs in this product are permitted to be restricted to this ";
print "group. Users who are a member of this group will be able ";
print "to place bugs in this group.";
print "Non-members will be forced to restrict bugs to this group ";
print "when they initially enter a bug in this product.";
print "</TD>\n";
print "</TR><TR>";
print "<TD>Default</TD>\n";
print "<TD>NA</TD>\n";
print "<TD>Bugs in this product are permitted to be restricted to this ";
print "group and are placed in this group by default.";
print "Users who are a member of this group will be able ";
print "to place bugs in this group.</TD>\n";
print "</TR><TR>";
print "<TD>Default</TD>\n";
print "<TD>Default</TD>\n";
print "<TD>Bugs in this product are permitted to be restricted to this ";
print "group and are placed in this group by default.";
print "Users who are a member of this group will be able ";
print "to place bugs in this group. Non-members will be able to ";
print "restrict bugs to this group on entry and will do so by default ";
print "</TD>\n";
print "</TR><TR>";
print "<TD>Default</TD>\n";
print "<TD>Mandatory</TD>\n";
print "<TD>Bugs in this product are permitted to be restricted to this ";
print "group and are placed in this group by default.";
print "Users who are a member of this group will be able ";
print "to place bugs in this group. Non-members will be forced ";
print "to place bugs in this group on entry.";
print "</TR><TR>";
print "<TD>Mandatory</TD>\n";
print "<TD>Mandatory</TD>\n";
print "<TD>Bugs in this product are required to be restricted to this ";
print "group. Users are not given any option.</TD>\n";
print "</TABLE>";
PutTrailer($localtrailer);
exit;
}
#
......
......@@ -36,6 +36,7 @@ use strict;
use lib qw(.);
use Bugzilla::Constants;
require "CGI.pl";
use vars qw(
......@@ -51,6 +52,7 @@ use vars qw(
$userid
%MFORM
%versions
$proddesc
);
# We have to connect to the database, even though we don't use it in this code,
......@@ -60,7 +62,7 @@ ConnectToDatabase();
# If we're using bug groups to restrict bug entry, we need to know who the
# user is right from the start.
confirm_login() if (Param("usebuggroupsentry"));
confirm_login() if AnyEntryGroups();
if (!defined $::FORM{'product'}) {
GetVersionTable();
......@@ -69,9 +71,7 @@ if (!defined $::FORM{'product'}) {
my %products;
foreach my $p (@enterable_products) {
if (!(Param("usebuggroupsentry")
&& GroupExists($p)
&& !UserInGroup($p)))
if (CanEnterProduct($p))
{
$products{$p} = $::proddesc{$p};
}
......@@ -215,13 +215,11 @@ sub pickos {
# End of subroutines
##############################################################################
confirm_login() if (!(Param("usebuggroupsentry")));
confirm_login() if (!(AnyEntryGroups()));
# If the usebuggroupsentry parameter is set, we need to check and make sure
# We need to check and make sure
# that the user has permission to enter a bug against this product.
if(Param("usebuggroupsentry")
&& GroupExists($product)
&& !UserInGroup($product))
if(!CanEnterProduct($product))
{
ThrowUserError("entry_access_denied", { product => $product});
}
......@@ -309,30 +307,25 @@ if (UserInGroup("editbugs") || UserInGroup("canconfirm")) {
$vars->{'bug_status'} = \@status;
$default{'bug_status'} = $status[0];
# Select whether to restrict this bug to the product's bug group or not,
# if the usebuggroups parameter is set, and if this product has a bug group.
# First we get the bit and description for the group.
my $group_id = '0';
if(Param("usebuggroups")) {
($group_id) = GroupExists($product);
}
SendSQL("SELECT DISTINCT groups.id, groups.name, groups.description " .
"FROM groups, user_group_map " .
"WHERE user_group_map.group_id = groups.id " .
"AND user_group_map.user_id = $::userid " .
"AND isbless = 0 " .
"AND isbuggroup = 1 AND isactive = 1 ORDER BY description");
SendSQL("SELECT DISTINCT groups.id, groups.name, groups.description, " .
"membercontrol, othercontrol " .
"FROM groups LEFT JOIN group_control_map " .
"ON group_id = id AND product_id = $product_id " .
"WHERE isbuggroup != 0 AND isactive != 0 ORDER BY description");
my @groups;
while (MoreSQLData()) {
my ($id, $prodname, $description) = FetchSQLData();
# Don't want to include product groups other than this product.
next unless(!Param("usebuggroups") || $prodname eq $product ||
!defined($::proddesc{$prodname}));
my ($id, $groupname, $description, $membercontrol, $othercontrol)
= FetchSQLData();
# Only include groups if the entering user will have an option.
next if ((!$membercontrol)
|| ($membercontrol == CONTROLMAPNA)
|| ($membercontrol == CONTROLMAPMANDATORY)
|| (($othercontrol != CONTROLMAPSHOWN)
&& ($othercontrol != CONTROLMAPDEFAULT)
&& (!UserInGroup($groupname)))
);
my $check;
# If this is the group for this product, make it checked.
......@@ -343,11 +336,10 @@ while (MoreSQLData()) {
$check = formvalue("bit-$id", 0);
}
else {
# $group_bit will only have a non-zero value if we're using
# bug groups and have one for this product.
# If $group_bit is 0, it won't match the current group, so compare
# it to the current bit instead of checking for non-zero.
$check = ($group_id == $id);
# Checkbox is checked by default if $control is a default state.
$check = (($membercontrol == CONTROLMAPDEFAULT)
|| (($othercontrol == CONTROLMAPDEFAULT)
&& (!UserInGroup($groupname))));
}
my $group =
......
......@@ -28,6 +28,7 @@
use strict;
use Bugzilla::Constants;
use Bugzilla::Util;
# Bring ChmodDataFile in until this is all moved to the module
use Bugzilla::Config qw(:DEFAULT ChmodDataFile);
......@@ -684,6 +685,116 @@ sub GenerateRandomPassword {
return $password;
}
#
# This function checks if there are any entry groups defined.
# If called with no arguments, it identifies
# entry groups for all products. If called with a product
# id argument, it checks for entry groups associated with
# one particular product.
sub AnyEntryGroups {
my $product_id = shift;
$product_id = 0 unless ($product_id);
return $::CachedAnyEntryGroups{$product_id}
if defined($::CachedAnyEntryGroups{$product_id});
PushGlobalSQLState();
my $query = "SELECT 1 FROM group_control_map WHERE entry != 0";
$query .= " AND product_id = $product_id" if ($product_id);
$query .= " LIMIT 1";
SendSQL($query);
$::CachedAnyEntryGroups{$product_id} = MoreSQLData();
FetchSQLData();
PopGlobalSQLState();
return $::CachedAnyEntryGroups{$product_id};
}
#
# This function checks if there are any default groups defined.
# If so, then groups may have to be changed when bugs move from
# one bug to another.
sub AnyDefaultGroups {
return $::CachedAnyDefaultGroups if defined($::CachedAnyDefaultGroups);
PushGlobalSQLState();
SendSQL("SELECT 1 FROM group_control_map, groups WHERE " .
"groups.id = group_control_map.group_id " .
"AND isactive != 0 AND " .
"(membercontrol = " . CONTROLMAPDEFAULT .
" OR othercontrol = " . CONTROLMAPDEFAULT .
") LIMIT 1");
$::CachedAnyDefaultGroups = MoreSQLData();
FetchSQLData();
PopGlobalSQLState();
return $::CachedAnyDefaultGroups;
}
#
# This function checks if, given a product id, the user can edit
# bugs in this product at all.
sub CanEditProductId {
my ($productid) = @_;
my $query = "SELECT group_id FROM group_control_map " .
"WHERE product_id = $productid " .
"AND canedit != 0 ";
if ((defined @{$::vars->{user}{groupids}})
&& (@{$::vars->{user}{groupids}} > 0)) {
$query .= "AND group_id NOT IN(" .
join(',',@{$::vars->{user}{groupids}}) . ") ";
}
$query .= "LIMIT 1";
PushGlobalSQLState();
SendSQL($query);
my ($result) = FetchSQLData();
PopGlobalSQLState();
return (!defined($result));
}
#
# This function determines if a user can enter bugs in the named
# product.
sub CanEnterProduct {
my ($productname) = @_;
my $query = "SELECT group_id IS NULL " .
"FROM products " .
"LEFT JOIN group_control_map " .
"ON group_control_map.product_id = products.id " .
"AND group_control_map.entry != 0 ";
if ((defined @{$::vars->{user}{groupids}})
&& (@{$::vars->{user}{groupids}} > 0)) {
$query .= "AND group_id NOT IN(" .
join(',',@{$::vars->{user}{groupids}}) . ") ";
}
$query .= "WHERE products.name = " . SqlQuote($productname) . " LIMIT 1";
PushGlobalSQLState();
SendSQL($query);
my ($ret) = FetchSQLData();
PopGlobalSQLState();
return ($ret);
}
#
# This function returns an alphabetical list of product names to which
# the user can enter bugs.
sub GetEnterableProducts {
my $query = "SELECT name " .
"FROM products " .
"LEFT JOIN group_control_map " .
"ON group_control_map.product_id = products.id " .
"AND group_control_map.entry != 0 ";
if ((defined @{$::vars->{user}{groupids}})
&& (@{$::vars->{user}{groupids}} > 0)) {
$query .= "AND group_id NOT IN(" .
join(',',@{$::vars->{user}{groupids}}) . ") ";
}
$query .= "WHERE group_id IS NULL ORDER BY name";
PushGlobalSQLState();
SendSQL($query);
my @products = ();
while (MoreSQLData()) {
push @products,FetchOneColumn();
}
PopGlobalSQLState();
return (@products);
}
sub CanSeeBug {
my ($id, $userid) = @_;
......@@ -1749,5 +1860,5 @@ $::vars =
'VERSION' => $Bugzilla::Config::VERSION,
};
1;
......@@ -26,6 +26,7 @@
use strict;
use lib qw(.);
use Bugzilla::Constants;
require "CGI.pl";
require "bug_form.pl";
......@@ -101,8 +102,7 @@ if (defined $::FORM{'maketemplate'}) {
umask 0;
# Some sanity checking
if(Param("usebuggroupsentry") && GroupExists($product)) {
UserInGroup($product) ||
if (!CanEnterProduct($product)) {
ThrowUserError("entry_access_denied", {product => $product});
}
......@@ -363,13 +363,38 @@ foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) {
WHERE user_id = $::userid
AND group_id = $v
AND isbless = 0");
my ($member) = FetchSQLData();
if ($member) {
my ($permit) = FetchSQLData();
if (!$permit) {
SendSQL("SELECT othercontrol FROM group_control_map
WHERE group_id = $v AND product_id = $product_id");
my ($othercontrol) = FetchSQLData();
$permit = (($othercontrol == CONTROLMAPSHOWN)
|| ($othercontrol == CONTROLMAPDEFAULT));
}
if ($permit) {
push(@groupstoadd, $v)
}
}
}
SendSQL("SELECT DISTINCT groups.id, groups.name, " .
"membercontrol, othercontrol " .
"FROM groups LEFT JOIN group_control_map " .
"ON group_id = id AND product_id = $product_id " .
" WHERE isbuggroup != 0 AND isactive != 0 ORDER BY description");
while (MoreSQLData()) {
my ($id, $groupname, $membercontrol, $othercontrol ) = FetchSQLData();
$membercontrol ||= 0;
$othercontrol ||= 0;
# Add groups required
if (($membercontrol == CONTROLMAPMANDATORY)
|| (($othercontrol == CONTROLMAPMANDATORY)
&& (!UserInGroup($groupname)))) {
# User had no option, bug needs to be in this group.
push(@groupstoadd, $id)
}
}
# Lock tables before inserting records for the new bug into the database
# if we are using a shadow database to prevent shadow database corruption
......
......@@ -31,6 +31,7 @@ my $UserInCanConfirmGroupSet = -1;
use lib qw(.);
use Bugzilla::Constants;
require "CGI.pl";
require "bug_form.pl";
......@@ -236,7 +237,7 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct)
# If the product-specific fields need to be verified, or we need to verify
# whether or not to add the bugs to their new product's group, display
# a verification form.
if (!$vok || !$cok || !$mok || (Param('usebuggroups') && !defined($::FORM{'addtonewgroup'}))) {
if (!$vok || !$cok || !$mok || (AnyDefaultGroups() && !defined($::FORM{'addtonewgroup'}))) {
$vars->{'form'} = \%::FORM;
$vars->{'mform'} = \%::MFORM;
......@@ -276,7 +277,7 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct)
$vars->{"verify_fields"} = 0;
}
$vars->{'verify_bug_group'} = (Param('usebuggroups')
$vars->{'verify_bug_group'} = (AnyDefaultGroups()
&& !defined($::FORM{'addtonewgroup'}));
$template->process("bug/process/verify-new-product.html.tmpl", $vars)
......@@ -594,11 +595,9 @@ sub ChangeResolution {
# select lists. This means that instead of looking for the bit-X values in
# the form, we need to loop through all the bug groups this user has access
# to, and for each one, see if it's selected.
# In order to make mass changes work correctly, keep a sum of bits for groups
# added, and another one for groups removed, and then let mysql do the bit
# operations
# If the form element isn't present, or the user isn't in the group, leave
# it as-is
my @groupAdd = ();
my @groupDel = ();
......@@ -1070,7 +1069,10 @@ foreach my $id (@idlist) {
"bug_group_map $write, flags $write, duplicates $write," .
"user_group_map READ, flagtypes READ, " .
"flaginclusions AS i READ, flagexclusions AS e READ, " .
"keyworddefs READ, groups READ, attachments READ");
"keyworddefs READ, groups READ, attachments READ, " .
"group_control_map AS oldcontrolmap READ, " .
"group_control_map AS newcontrolmap READ, " .
"group_control_map READ");
my @oldvalues = SnapShotBug($id);
my %oldhash;
# Fun hack. @::log_columns only contains the component_id,
......@@ -1104,6 +1106,18 @@ foreach my $id (@idlist) {
$i++;
}
$oldhash{'product'} = get_product_name($oldhash{'product_id'});
if (!CanEditProductId($oldhash{'product_id'})) {
$vars->{'product'} = $oldhash{'product'};
ThrowUserError("product_edit_denied");
}
if (defined $::FORM{'product'}
&& $::FORM{'product'} ne $::FORM{'dontchange'}
&& $::FORM{'product'} ne $oldhash{'product'}
&& !CanEnterProduct($::FORM{'product'})) {
$vars->{'product'} = $::FORM{'product'};
ThrowUserError("entry_access_denied");
}
if ($requiremilestone) {
my $value = $::FORM{'target_milestone'};
if (!defined $value || $value eq $::FORM{'dontchange'}) {
......@@ -1268,6 +1282,7 @@ foreach my $id (@idlist) {
if ($::comma ne "") {
SendSQL($query);
}
# Check for duplicates if the bug is [re]open
SendSQL("SELECT resolution FROM bugs WHERE bug_id = $id");
my $resolution = FetchOneColumn();
......@@ -1275,8 +1290,34 @@ foreach my $id (@idlist) {
SendSQL("DELETE FROM duplicates WHERE dupe = $id");
}
my $newproduct_id = $oldhash{'product_id'};
if ((defined $::FORM{'product'})
&& ($::FORM{'product'} ne $::FORM{'dontchange'})) {
my $newproduct_id = get_product_id($::FORM{'product'});
}
my %groupsrequired = ();
my %groupsforbidden = ();
SendSQL("SELECT id, membercontrol
FROM groups LEFT JOIN group_control_map
ON id = group_id
AND product_id = $newproduct_id WHERE isactive != 0");
while (MoreSQLData()) {
my ($group, $control) = FetchSQLData();
$control ||= 0;
unless ($control > &::CONTROLMAPNA) {
$groupsforbidden{$group} = 1;
}
if ($control == &::CONTROLMAPMANDATORY) {
$groupsrequired{$group} = 1;
}
}
my @groupAddNames = ();
foreach my $grouptoadd (@groupAdd) {
my @groupAddNamesAll = ();
foreach my $grouptoadd (@groupAdd, keys %groupsrequired) {
next if $groupsforbidden{$grouptoadd};
push(@groupAddNamesAll, GroupIdToName($grouptoadd));
if (!BugInGroupId($id, $grouptoadd)) {
push(@groupAddNames, GroupIdToName($grouptoadd));
SendSQL("INSERT INTO bug_group_map (bug_id, group_id)
......@@ -1284,7 +1325,10 @@ foreach my $id (@idlist) {
}
}
my @groupDelNames = ();
foreach my $grouptodel (@groupDel) {
my @groupDelNamesAll = ();
foreach my $grouptodel (@groupDel, keys %groupsforbidden) {
push(@groupDelNamesAll, GroupIdToName($grouptodel));
next if $groupsrequired{$grouptodel};
if (BugInGroupId($id, $grouptodel)) {
push(@groupDelNames, GroupIdToName($grouptodel));
}
......@@ -1399,63 +1443,117 @@ foreach my $id (@idlist) {
# group or add it to the new one. There are a very specific series of
# conditions under which these activities take place, more information
# about which can be found in comments within the conditionals below.
# Check if the user has changed the product to which the bug belongs;
if (
# the "usebuggroups" parameter is on, indicating that products
# are associated with groups of the same name;
Param('usebuggroups')
# the user has changed the product to which the bug belongs;
&& defined $::FORM{'product'}
defined $::FORM{'product'}
&& $::FORM{'product'} ne $::FORM{'dontchange'}
&& $::FORM{'product'} ne $oldhash{'product'}
) {
if (
# the user wants to add the bug to the new product's group;
($::FORM{'addtonewgroup'} eq 'yes'
|| ($::FORM{'addtonewgroup'} eq 'yesifinold'
&& BugInGroup($id, $oldhash{'product'})))
# the new product is associated with a group;
&& GroupExists($::FORM{'product'})
# the bug is not already in the group; (This can happen when the user
# goes to the "edit multiple bugs" form with a list of bugs at least
# one of which is in the new group. In this situation, the user can
# simultaneously change the bugs to a new product and move the bugs
# into that product's group, which happens earlier in this script
# and thus is already done. If we didn't check for this, then this
# situation would cause us to add the bug to the group twice, which
# would result in the bug being added to a totally different group.)
&& !BugInGroup($id, $::FORM{'product'})
# the user is a member of the associated group, indicating they
# are authorized to add bugs to that group, *or* the "usebuggroupsentry"
# parameter is off, indicating that users can add bugs to a product
# regardless of whether or not they belong to its associated group;
&& (UserInGroup($::FORM{'product'}) || !Param('usebuggroupsentry'))
# the associated group is active, indicating it can accept new bugs;
&& GroupIsActive(GroupNameToId($::FORM{'product'}))
) {
# Add the bug to the group associated with its new product.
my $groupid = GroupNameToId($::FORM{'product'});
if (!BugInGroupId($id, $groupid)) {
SendSQL("INSERT INTO bug_group_map (bug_id, group_id) VALUES ($id, $groupid)");
}
}
if (
# the old product is associated with a group;
GroupExists($oldhash{'product'})
# the bug is a member of that group;
&& BugInGroup($id, $oldhash{'product'})
) {
# Remove the bug from the group associated with its old product.
my $groupid = GroupNameToId($oldhash{'product'});
SendSQL("DELETE FROM bug_group_map WHERE bug_id = $id AND group_id = $groupid");
my $newproduct_id = get_product_id($::FORM{'product'});
# Depending on the "addtonewgroup" variable, groups with
# defaults will change.
#
# For each group, determine
# - The group id and if it is active
# - The control map value for the old product and this group
# - The control map value for the new product and this group
# - Is the user in this group?
# - Is the bug in this group?
SendSQL("SELECT DISTINCT groups.id, isactive, " .
"oldcontrolmap.membercontrol, newcontrolmap.membercontrol, " .
"user_group_map.user_id IS NOT NULL, " .
"bug_group_map.group_id IS NOT NULL " .
"FROM groups " .
"LEFT JOIN group_control_map AS oldcontrolmap " .
"ON oldcontrolmap.group_id = groups.id " .
"AND oldcontrolmap.product_id = " . $oldhash{'product_id'} .
" LEFT JOIN group_control_map AS newcontrolmap " .
"ON newcontrolmap.group_id = groups.id " .
"AND newcontrolmap.product_id = $newproduct_id " .
"LEFT JOIN user_group_map " .
"ON user_group_map.group_id = groups.id " .
"AND user_group_map.user_id = $::userid " .
"AND user_group_map.isbless = 0 " .
"LEFT JOIN bug_group_map " .
"ON bug_group_map.group_id = groups.id " .
"AND bug_group_map.bug_id = $id "
);
my @groupstoremove = ();
my @groupstoadd = ();
my @defaultstoremove = ();
my @defaultstoadd = ();
my @allgroups = ();
my $buginanydefault = 0;
my $buginanychangingdefault = 0;
while (MoreSQLData()) {
my ($groupid, $isactive, $oldcontrol, $newcontrol,
$useringroup, $bugingroup) = FetchSQLData();
# An undefined newcontrol is none.
$newcontrol = CONTROLMAPNA unless $newcontrol;
$oldcontrol = CONTROLMAPNA unless $oldcontrol;
push(@allgroups, $groupid);
if (($bugingroup) && ($isactive)
&& ($oldcontrol == CONTROLMAPDEFAULT)) {
# Bug was in a default group.
$buginanydefault = 1;
if ($newcontrol != CONTROLMAPDEFAULT) {
# Bug was in a default group that no longer is.
$buginanychangingdefault = 1;
push (@defaultstoremove, $groupid);
}
}
if (($isactive) && (!$bugingroup)
&& ($newcontrol == CONTROLMAPDEFAULT)
&& ($useringroup)) {
push (@defaultstoadd, $groupid);
}
if (($bugingroup) && ($isactive) && ($newcontrol == CONTROLMAPNA)) {
# Group is no longer permitted.
push(@groupstoremove, $groupid);
}
if ((!$bugingroup) && ($isactive)
&& ($newcontrol == CONTROLMAPMANDATORY)) {
# Group is now required.
push(@groupstoadd, $groupid);
}
}
# If addtonewgroups = "yes", old default groups will be removed
# and new default groups will be added.
# If addtonewgroups = "yesifinold", old default groups will be removed
# and new default groups will be added only if the bug was in ANY
# of the old default groups.
# If addtonewgroups = "no", old default groups will be removed and not
# replaced.
push(@groupstoremove, @defaultstoremove);
if (AnyDefaultGroups()
&& (($::FORM{'addtonewgroup'} eq 'yes')
|| (($::FORM{'addtonewgroup'} eq 'yesifinold')
&& ($buginanydefault)))) {
push(@groupstoadd, @defaultstoadd);
}
# Now actually update the bug_group_map.
my @DefGroupsAdded = ();
my @DefGroupsRemoved = ();
foreach my $groupid (@allgroups) {
my $thisadd = grep( ($_ == $groupid), @groupstoadd);
my $thisdel = grep( ($_ == $groupid), @groupstoremove);
if ($thisadd) {
push(@DefGroupsAdded, GroupIdToName($groupid));
SendSQL("INSERT INTO bug_group_map (bug_id, group_id) VALUES " .
"($id, $groupid)");
} elsif ($thisdel) {
push(@DefGroupsRemoved, GroupIdToName($groupid));
SendSQL("DELETE FROM bug_group_map WHERE bug_id = $id " .
"AND group_id = $groupid");
}
}
if ((@DefGroupsAdded) || (@DefGroupsRemoved)) {
LogActivityEntry($id, "bug_group",
join(', ', @DefGroupsRemoved),
join(', ', @DefGroupsAdded),
$whoid, $timestamp);
}
}
# get a snapshot of the newly set values out of the database,
......@@ -1471,7 +1569,6 @@ foreach my $id (@idlist) {
$newhash{$col} = $newvalues[$i];
$i++;
}
# for passing to processmail to ensure that when someone is removed
# from one of these fields, they get notified of that fact (if desired)
#
......
......@@ -191,19 +191,14 @@ if ($default{'chfieldto'}->[0] eq "") {
GetVersionTable();
# if using usebuggroups, then we don't want people to see products they don't
# have access to. Remove them from the list.
# if using groups for entry, then we don't want people to see products they
# don't have access to. Remove them from the list.
my @products = ();
my %component_set;
my %version_set;
my %milestone_set;
foreach my $p (@::legal_product) {
# If we're using bug groups to restrict entry on products, and
# this product has a bug group, and the user is not in that
# group, we don't want to include that product in this list.
next if (Param("usebuggroups") && GroupExists($p) && !UserInGroup($p));
foreach my $p (GetEnterableProducts()) {
# We build up boolean hashes in the "-set" hashes for each of these things
# before making a list because there may be duplicates names across products.
push @products, $p;
......
......@@ -663,7 +663,7 @@ SendSQL("SELECT name, description FROM products ORDER BY name");
while (MoreSQLData()) {
my ($product, $productdesc) = FetchSQLData();
next if (Param("usebuggroups") && GroupExists($product) && !UserInGroup($product));
next if (!CanEnterProduct($product));
push (@products, $product);
$line_count++;
......
......@@ -23,8 +23,6 @@
# Dawn Endico <endico@mozilla.org>
# Bryce Nesbitt <bryce@nextbus.COM>,
# Joe Robins <jmrobins@tgix.com>,
# If using the usebuggroups parameter, users shouldn't be able to see
# reports for products they don't have access to.
# Gervase Markham <gerv@gerv.net> and Adam Spiers <adam@spiers.net>
# Added ability to chart any combination of resolutions/statuses.
# Derive the choice of resolutions/statuses from the -All- data file
......@@ -60,20 +58,11 @@ quietly_check_login();
GetVersionTable();
# If the usebuggroups parameter is set, we don't want to list all products.
# We only want those products that the user has permissions for.
my @myproducts;
if(Param("usebuggroups")) {
push( @myproducts, "-All-");
foreach my $this_product (@legal_product) {
if(GroupExists($this_product) && !UserInGroup($this_product)) {
next;
} else {
push( @myproducts, $this_product )
}
}
} else {
push( @myproducts, "-All-", @legal_product );
push( @myproducts, "-All-");
foreach my $this_product (@legal_product) {
push(@myproducts, $this_product) if CanEnterProduct($this_product);
}
if (! defined $FORM{'product'}) {
......@@ -91,12 +80,11 @@ if (! defined $FORM{'product'}) {
grep($_ eq $FORM{'product'}, @myproducts)
|| ThrowUserError("invalid_product_name", {product => $FORM{'product'}});
# If usebuggroups is on, we don't want people to be able to view
# We don't want people to be able to view
# reports for products they don't have permissions for...
Param("usebuggroups")
&& GroupExists($FORM{'product'})
&& !UserInGroup($FORM{'product'})
&& ThrowUserError("report_access_denied");
if (!CanEnterProduct($FORM{'product'})) {
ThrowUserError("report_access_denied");
}
# We've checked that the product exists, and that the user can see it
# This means that is OK to detaint
......
......@@ -26,6 +26,7 @@ use strict;
use lib qw(.);
require "CGI.pl";
use Bugzilla::Constants;
use vars qw(%FORM $unconfirmedstate);
......@@ -263,6 +264,7 @@ CrossCheck("groups", "id",
["bug_group_map", "group_id"],
["group_group_map", "grantor_id"],
["group_group_map", "member_id"],
["group_control_map", "group_id"],
["user_group_map", "group_id"]);
CrossCheck("profiles", "userid",
......@@ -288,6 +290,7 @@ CrossCheck("products", "id",
["components", "product_id", "name"],
["milestones", "product_id", "value"],
["versions", "product_id", "value"],
["group_control_map", "product_id"],
["flaginclusions", "product_id", "type_id"],
["flagexclusions", "product_id", "type_id"]);
......@@ -613,6 +616,53 @@ DateCheck("groups", "last_changed");
DateCheck("profiles", "refreshed_when");
###########################################################################
# Control Values
###########################################################################
# Checks for values that are invalid OR
# not among the 9 valid combinations
Status("Checking for bad values in group_control_map");
SendSQL("SELECT COUNT(product_id) FROM group_control_map WHERE " .
"membercontrol NOT IN(" . CONTROLMAPNA . "," . CONTROLMAPSHOWN .
"," . CONTROLMAPDEFAULT . "," . CONTROLMAPMANDATORY . ")" .
" OR " .
"othercontrol NOT IN(" . CONTROLMAPNA . "," . CONTROLMAPSHOWN .
"," . CONTROLMAPDEFAULT . "," . CONTROLMAPMANDATORY . ")" .
" OR " .
"( (membercontrol != othercontrol) " .
"AND (membercontrol != " . CONTROLMAPSHOWN . ") " .
"AND ((membercontrol != " . CONTROLMAPDEFAULT . ") " .
"OR (othercontrol = " . CONTROLMAPSHOWN . ")))");
my $c = FetchOneColumn();
if ($c) {
Alert("Found $c bad group_control_map entries");
}
Status("Checking for bugs with groups violating their product's group controls");
BugCheck("bugs, groups, bug_group_map
LEFT JOIN group_control_map
ON group_control_map.product_id = bugs.product_id
AND group_control_map.group_id = bug_group_map.group_id
WHERE bugs.bug_id = bug_group_map.bug_id
AND bug_group_map.group_id = groups.id
AND groups.isactive != 0
AND ((group_control_map.membercontrol = " . CONTROLMAPNA . ")
OR (group_control_map.membercontrol IS NULL))",
"Have groups not permitted for their products");
BugCheck("bugs, groups, group_control_map
LEFT JOIN bug_group_map
ON group_control_map.group_id = bug_group_map.group_id
AND bugs.bug_id = bug_group_map.bug_id
WHERE group_control_map.product_id = bugs.product_id
AND bug_group_map.group_id = groups.id
AND groups.isactive != 0
AND group_control_map.membercontrol = " . CONTROLMAPMANDATORY . "
AND bug_group_map.group_id IS NULL",
"Are missing groups required for their products");
###########################################################################
# Unsent mail
###########################################################################
......
<!-- 1.0@bugzilla.org -->
[%# 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) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Joel Peshkin <bugreport@peshkin.net>
#
#%]
[% PROCESS global/header.html.tmpl title="Confirm Group Control Change for product \'$product\'" %]
[% FOREACH group = mandatory_groups %]
<P>
group '[% group.name FILTER html %]' impacts [% group.count %] bugs for which the group is
newly mandatory and will be added.
[% END %]
[% FOREACH group = na_groups %]
<P>
group '[% group.name FILTER html %]' impacts [% group.count %] bugs for which the group is no longer applicable and will be removed.
[% END %]
<form method="post" >
[% PROCESS "global/hidden-fields.html.tmpl" exclude="^(Bugzilla|LDAP)_(login|password)$" %]
<br>
Click "Continue" to proceed with the change including the changes
indicated above. If you do not want these changes, use "back" to
return to the previous page.
<p>
<input type="hidden" name="confirmed" value="confirmed">
<input type="submit" value="Continue">
</p>
</form>
[% PROCESS global/footer.html.tmpl %]
<!-- 1.0@bugzilla.org -->
[%# 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) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Joel Peshkin <bugreport@peshkin.net>
#%]
[% filt_product = product FILTER html %]
[% PROCESS global/header.html.tmpl
title = "Edit Group Controls for '$filt_product'"
%]
<form method="post" action="editproducts.cgi">
<input type="hidden" name="action" value="updategroupcontrols">
<input type="hidden" name="product" value="[% filt_product %]">
<table id="form" cellspacing="0" cellpadding="4" border="1">
<tr bgcolor="#6666ff">
<th>Group</th>
<th>Entry</th>
<th>MemberControl</th>
<th>OtherControl</th>
<th>Canedit</th>
<th>Bugs</th>
</tr>
[% FOREACH group = groups %]
[% IF group.isactive == 0 AND group.bugcount > 0 %]
<tr bgcolor="#bbbbbb">
<td>
[% group.name FILTER html %]
</td>
<td align="center" colspan=4>
Disabled
</td>
<td>
[% group.bugcount %]
</td>
<tr>
[% ELSIF group.isactive != 0 %]
<tr>
<td>
[% group.name FILTER html %]
</td>
<td>
<input type=checkbox value=1 name=entry_[% group.id %]
[% " checked=\"checked\"" IF group.entry %]>
</td>
<td>
<select name="membercontrol_[% group.id %]">
<option value=[% const.CONTROLMAPNA %]
[% " selected=\"selected\""
IF group.membercontrol == const.CONTROLMAPNA %]
>NA
</option>
<option value=[% const.CONTROLMAPSHOWN %]
[% " selected=\"selected\""
IF group.membercontrol == const.CONTROLMAPSHOWN %]
>Shown
</option>
<option value=[% const.CONTROLMAPDEFAULT %]
[% " selected=\"selected\""
IF group.membercontrol == const.CONTROLMAPDEFAULT %]
>Default
</option>
<option value=[% const.CONTROLMAPMANDATORY %]
[% " selected=\"selected\""
IF group.membercontrol == const.CONTROLMAPMANDATORY %]
>Mandatory
</option>
</select>
</td>
<td>
<select name="othercontrol_[% group.id %]">
<option value=[% const.CONTROLMAPNA %]
[% " selected=\"selected\""
IF group.othercontrol == const.CONTROLMAPNA %]
>NA
</option>
<option value=[% const.CONTROLMAPSHOWN %]
[% " selected=\"selected\""
IF group.othercontrol == const.CONTROLMAPSHOWN %]
>Shown
</option>
<option value=[% const.CONTROLMAPDEFAULT %]
[% " selected=\"selected\""
IF group.othercontrol == const.CONTROLMAPDEFAULT %]
>Default
</option>
<option value=[% const.CONTROLMAPMANDATORY %]
[% " selected=\"selected\""
IF group.othercontrol == const.CONTROLMAPMANDATORY %]
>Mandatory
</option>
</select>
</td>
<td>
<input type=checkbox value=1 name=canedit_[% group.id %]
[% " checked=\"checked\"" IF group.canedit %]>
</td>
<td>
[% group.bugcount %]
</td>
</tr>
[% END %]
[% END %]
</table>
<br>
<input type=submit name="submit" value="submit">
<br>
</form>
<p>
These settings control the relationship of the groups to this
product.
<p>
If any group has <b>Entry</b> selected, then this product will
restrict bug entry to only those users who are members of all the
groups with entry selected.
<p>
If any group has <b>Canedit</b> selected, then this product
will be read-only for any users who are not members of all of
the groups with Canedit selected. ONLY users who are members of
all the canedit groups will be able to edit. This is an additional
restriction that further restricts what can be edited by a user.
<p>
The <b>MemberControl</b> and <b>OtherControl</b> fields
indicate which bugs will be placed in
this group according to the following definitions.
<br>
<table border=1>
<tr>
<th>
MemberControl
</th>
<th>
OtherControl
</th>
<th>
Interpretation
</th>
</tr>
<tr>
<td>
NA
</td>
<td>
NA
</td>
<td>
Bugs in this product are never associated with this group.
</td>
</tr>
<tr>
<td>
Shown
</td>
<td>
NA
</td>
<td>
Bugs in this product are permitted to be restricted to this
group. Users who are a member of this group will be able
to place bugs in this group.
</td>
</tr>
<tr>
<td>
Shown
</td>
<td>
Shown
</td>
<td>
Bugs in this product can be placed in this group by anyone
with permission to edit the bug even if they are not a member
of this group.
</td>
</tr>
<tr>
<td>
Shown
</td>
<td>
Default
</td>
<td>
Bugs in this product can be placed in this group by anyone
with permission to edit the bug even if they are not a member
of this group. Non-members place bugs in this group by default.
</td>
</tr>
<tr>
<td>
Shown
</td>
<td>
Mandatory
</td>
<td>
Bugs in this product are permitted to be restricted to this
group. Users who are a member of this group will be able
to place bugs in this group.
Non-members will be forced to restrict bugs to this group
when they initially enter a bug in this product.
</td>
</tr>
<tr>
<td>
Default
</td>
<td>
NA
</td>
<td>
Bugs in this product are permitted to be restricted to this
group and are placed in this group by default.
Users who are a member of this group will be able
to place bugs in this group.
</td>
</tr>
<tr>
<td>
Default
</td>
<td>
Default
</td>
<td>
Bugs in this product are permitted to be restricted to this
group and are placed in this group by default.
Users who are a member of this group will be able
to place bugs in this group. Non-members will be able to
restrict bugs to this group on entry and will do so by default
</td>
</tr>
<tr>
<td>
Default
</td>
<td>
Mandatory
</td>
<td>
Bugs in this product are permitted to be restricted to this
group and are placed in this group by default.
Users who are a member of this group will be able
to place bugs in this group. Non-members will be forced
to place bugs in this group on entry.
</td>
</tr>
<tr>
<td>
Mandatory
</td>
<td>
Mandatory
</td>
<td>
Bugs in this product are required to be restricted to this
group. Users are not given any option.
</td>
</tr>
</table>
[% PROCESS global/footer.html.tmpl %]
......@@ -405,14 +405,21 @@
<br>
[% IF groups.size > 0 %]
[% FOREACH group = groups %]
[% IF NOT group.mandatory %]
[% IF NOT emitted_description %]
[% emitted_description = 1 %]
<br>
<b>Only users in all of the selected groups can view this bug:</b>
<br>
<font size="-1">(Unchecking all boxes makes this a public bug.)</font>
<font size="-1">
(Unchecking all boxes makes this a more public bug.)
</font>
<br>
<br>
[% END %]
[% FOREACH group = groups %]
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="checkbox" name="bit-[% group.bit %]" value="1"
[% " checked=\"checked\"" IF group.ison %]
......@@ -420,6 +427,7 @@
[% group.description %]
<br>
[% END %]
[% END %]
[% IF NOT user.inallgroups %]
<b>
......@@ -431,7 +439,7 @@
[% IF bug.inagroup %]
<p>
<b>But users in the roles selected below can always view this bug:</b>
<b>Users in the roles selected below can always view this bug:</b>
<br>
<small>
(The assignee
......
......@@ -81,14 +81,14 @@
<h3>Verify Bug Group</h3>
<p>
Do you want to add the bug to its new product's group (if any)?
Do you want to add the bug to its new product's default groups (if any)?
</p>
<p>
<input type="radio" name="addtonewgroup" value="no"><b>no</b><br>
<input type="radio" name="addtonewgroup" value="yes"><b>yes</b><br>
<input type="radio" name="addtonewgroup" value="yesifinold" checked="checked">
<b>yes, but only if the bug was in its old product's group</b><br>
<b>yes, but only if the bug was in any of its old product's default groups</b><br>
</p>
[% END %]
......
......@@ -230,6 +230,12 @@
It must also not contain any of these special characters:
<tt>\ ( ) &amp; &lt; &gt; , ; : &quot; [ ]</tt>, or any whitespace.
[% ELSIF error == "illegal_group_control_combination" %]
[% title = "Your Group Control Combination Is Illegal" %]
Your group control combination for group &quot;
[% groupname FILTER html %]
&quot; is illegal.
[% ELSIF error == "illegal_is_obsolete" %]
[% title = "Your Query Makes No Sense" %]
The only legal values for the <em>Attachment is obsolete</em> field are
......@@ -462,6 +468,10 @@
[% title = "Access Denied" %]
You do not have the permissions necessary to access that product.
[% ELSIF error == "product_edit_denied" %]
[% title = "Product Edit Access Denied" %]
You are not permitted to edit bugs in product [% product %].
[% ELSIF error == "query_name_missing" %]
[% title = "No Query Name Specified" %]
You must enter a name for your query.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment