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 { ...@@ -167,6 +167,16 @@ sub UpdateParams {
delete $param{'usequip'}; 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 --- # --- DEFAULTS FOR NEW PARAMS ---
foreach my $item (@param_list) { 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 @@ ...@@ -24,7 +24,7 @@
use strict; use strict;
use RelationSet; use RelationSet;
use Bugzilla::Constants;
# Use the Attachment module to display attachments for the bug. # Use the Attachment module to display attachments for the bug.
use Attachment; use Attachment;
...@@ -144,12 +144,9 @@ sub show_bug { ...@@ -144,12 +144,9 @@ sub show_bug {
next; next;
} }
if (Param("usebuggroupsentry") if (!CanEnterProduct($product)) {
&& GroupExists($product)
&& !UserInGroup($product))
{
# If we're using bug groups to restrict entry on products, and # 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. # group, we don't want to include that product in this list.
next; next;
} }
...@@ -275,7 +272,7 @@ sub show_bug { ...@@ -275,7 +272,7 @@ sub show_bug {
SendSQL("SELECT DISTINCT groups.id, name, description," . SendSQL("SELECT DISTINCT groups.id, name, description," .
" bug_group_map.group_id IS NOT NULL," . " bug_group_map.group_id IS NOT NULL," .
" user_group_map.group_id IS NOT NULL," . " user_group_map.group_id IS NOT NULL," .
" isactive" . " isactive, membercontrol, othercontrol" .
" FROM groups" . " FROM groups" .
" LEFT JOIN bug_group_map" . " LEFT JOIN bug_group_map" .
" ON bug_group_map.group_id = groups.id" . " ON bug_group_map.group_id = groups.id" .
...@@ -284,33 +281,48 @@ sub show_bug { ...@@ -284,33 +281,48 @@ sub show_bug {
" ON user_group_map.group_id = groups.id" . " ON user_group_map.group_id = groups.id" .
" AND user_id = $::userid" . " AND user_id = $::userid" .
" AND NOT isbless" . " 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"); " WHERE isbuggroup");
$user{'inallgroups'} = 1; $user{'inallgroups'} = 1;
while (MoreSQLData()) { while (MoreSQLData()) {
my ($groupid, $name, $description, $ison, $ingroup, $isactive) my ($groupid, $name, $description, $ison, $ingroup, $isactive,
= FetchSQLData(); $membercontrol, $othercontrol) = FetchSQLData();
$bug{'inagroup'} = 1 if ($ison); $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 # For product groups, we only want to display the checkbox if either
# (1) The bit is already set, or # (1) The bit is set and not required, or
# (2) The user is in the group, but either: # (2) The group is Shown or Default for members and
# (a) The group is a product group for the current product, or # the user is a member of the group.
# (b) The group name isn't a product name if ($ison ||
# This means that all product groups will be skipped, but ($isactive && $ingroup
# non-product bug groups will still be displayed. && (($membercontrol == CONTROLMAPDEFAULT)
if($ison || || ($membercontrol == CONTROLMAPSHOWN))
($isactive && ($ingroup && (!Param("usebuggroups") || ($name eq $bug{'product'}) || ))
(!defined $::proddesc{$name})))))
{ {
$user{'inallgroups'} &= $ingroup; $user{'inallgroups'} &= $ingroup;
push (@groups, { "bit" => $groupid, my $mandatory;
"ison" => $ison, if ($isactive && ($membercontrol == CONTROLMAPMANDATORY)) {
"ingroup" => $ingroup, $mandatory = 1;
"description" => $description }); } else {
$mandatory = 0;
}
if (($ison) || ($ingroup)) {
push (@groups, { "bit" => $groupid,
"ison" => $ison,
"ingroup" => $ingroup,
"mandatory" => $mandatory,
"description" => $description });
}
} }
} }
......
...@@ -728,7 +728,6 @@ $vars->{'order'} = $order; ...@@ -728,7 +728,6 @@ $vars->{'order'} = $order;
my $login = $::COOKIE{'Bugzilla_login'}; my $login = $::COOKIE{'Bugzilla_login'};
$vars->{'caneditbugs'} = UserInGroup('editbugs'); $vars->{'caneditbugs'} = UserInGroup('editbugs');
$vars->{'usebuggroups'} = Param('usebuggroups');
# Whether or not this user is authorized to move bugs to another installation. # Whether or not this user is authorized to move bugs to another installation.
$vars->{'ismover'} = 1 $vars->{'ismover'} = 1
......
...@@ -112,6 +112,7 @@ ...@@ -112,6 +112,7 @@
use strict; use strict;
use vars qw( $db_name %answer ); use vars qw( $db_name %answer );
use Bugzilla::Constants;
########################################################################### ###########################################################################
# Non-interactive override # Non-interactive override
...@@ -1716,6 +1717,17 @@ $table{quips} = ...@@ -1716,6 +1717,17 @@ $table{quips} =
userid mediumint not null default 0, userid mediumint not null default 0,
quip text not null'; 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 # Create tables
########################################################################### ###########################################################################
...@@ -2954,7 +2966,7 @@ if (GetFieldDef("logincookies", "hostname")) { ...@@ -2954,7 +2966,7 @@ if (GetFieldDef("logincookies", "hostname")) {
AddField("logincookies", "ipaddr", "varchar(40) NOT NULL"); 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 # Add private comments and private attachments on less-private bugs
AddField('longdescs', 'isprivate', 'tinyint not null default 0'); AddField('longdescs', 'isprivate', 'tinyint not null default 0');
AddField('attachments', 'isprivate', 'tinyint not null default 0'); AddField('attachments', 'isprivate', 'tinyint not null default 0');
...@@ -3121,7 +3133,7 @@ if (($fielddef = GetFieldDef("attachments", "creation_ts")) && ...@@ -3121,7 +3133,7 @@ if (($fielddef = GetFieldDef("attachments", "creation_ts")) &&
ChangeFieldType("attachments", "creation_ts", "datetime NOT NULL"); 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, # If the whole groups system is new, but the installation isn't,
# convert all the old groupset groups, etc... # convert all the old groupset groups, etc...
...@@ -3464,6 +3476,54 @@ if (TableExists("attachstatuses") && TableExists("attachstatusdefs")) { ...@@ -3464,6 +3476,54 @@ if (TableExists("attachstatuses") && TableExists("attachstatusdefs")) {
print "done.\n"; 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 # If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment. # differential change code *** A B O V E *** this comment.
# #
...@@ -3793,3 +3853,4 @@ $dbh->do("UPDATE components SET initialowner = $adminuid WHERE initialowner = 0" ...@@ -3793,3 +3853,4 @@ $dbh->do("UPDATE components SET initialowner = $adminuid WHERE initialowner = 0"
unlink "data/versioncache"; unlink "data/versioncache";
print "Reminder: Bugzilla now requires version 8.7 or later of sendmail.\n" unless $silent; print "Reminder: Bugzilla now requires version 8.7 or later of sendmail.\n" unless $silent;
...@@ -244,7 +244,7 @@ sub check_netmask { ...@@ -244,7 +244,7 @@ sub check_netmask {
}, },
{ {
name => 'usebuggroups', name => 'makeproductgroups',
desc => 'If this is on, Bugzilla will associate a bug group with each ' . desc => 'If this is on, Bugzilla will associate a bug group with each ' .
'product in the database, and use it for querying bugs.', 'product in the database, and use it for querying bugs.',
type => 'b', type => 'b',
...@@ -252,9 +252,9 @@ sub check_netmask { ...@@ -252,9 +252,9 @@ sub check_netmask {
}, },
{ {
name => 'usebuggroupsentry', name => 'useentrygroupdefault',
desc => 'If this is on, Bugzilla will use product bug groups to restrict ' . 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', type => 'b',
default => 0 default => 0
}, },
......
...@@ -42,11 +42,11 @@ if (!defined $::FORM{'product'}) { ...@@ -42,11 +42,11 @@ if (!defined $::FORM{'product'}) {
# Reference to a subset of %::proddesc, which the user is allowed to see # Reference to a subset of %::proddesc, which the user is allowed to see
my %products; my %products;
if (Param("usebuggroups")) { if (AnyDefaultGroups()) {
# OK, now only add products the user can see # OK, now only add products the user can see
confirm_login() unless $::userid; confirm_login() unless $::userid;
foreach my $p (@::legal_product) { foreach my $p (@::legal_product) {
if (!GroupExists($p) || UserInGroup($p)) { if (CanEnterProduct($p)) {
$products{$p} = $::proddesc{$p}; $products{$p} = $::proddesc{$p};
} }
} }
...@@ -88,11 +88,8 @@ if (!$product_id) { ...@@ -88,11 +88,8 @@ if (!$product_id) {
} }
# Make sure the user is authorized to access this product. # Make sure the user is authorized to access this product.
if (Param("usebuggroups") && GroupExists($product)) { CanEnterProduct($product)
confirm_login() unless $::userid;
UserInGroup($product)
|| ThrowUserError("product_access_denied"); || ThrowUserError("product_access_denied");
}
###################################################################### ######################################################################
# End Data/Security Validation # End Data/Security Validation
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
use strict; use strict;
use lib "."; use lib ".";
use Bugzilla::Constants;
require "CGI.pl"; require "CGI.pl";
ConnectToDatabase(); ConnectToDatabase();
...@@ -117,9 +118,9 @@ unless ($action) { ...@@ -117,9 +118,9 @@ unless ($action) {
while (MoreSQLData()) { while (MoreSQLData()) {
my ($groupid, $name, $desc, $regexp, $isactive, $isbuggroup) = FetchSQLData(); my ($groupid, $name, $desc, $regexp, $isactive, $isbuggroup) = FetchSQLData();
print "<tr>\n"; print "<tr>\n";
print "<td>$name</td>\n"; print "<td>" . html_quote($name) . "</td>\n";
print "<td>$desc</td>\n"; print "<td>" . html_quote($desc) . "</td>\n";
print "<td>$regexp&nbsp</td>\n"; print "<td>" . html_quote($regexp) . "&nbsp</td>\n";
print "<td align=center>"; print "<td align=center>";
print "X" if (($isactive != 0) && ($isbuggroup != 0)); print "X" if (($isactive != 0) && ($isbuggroup != 0));
print "&nbsp</td>\n"; print "&nbsp</td>\n";
...@@ -185,22 +186,27 @@ if ($action eq 'changeform') { ...@@ -185,22 +186,27 @@ if ($action eq 'changeform') {
print "<TABLE BORDER=1 CELLPADDING=4>"; print "<TABLE BORDER=1 CELLPADDING=4>";
print "<TR><TH>Group:</TH><TD>"; print "<TR><TH>Group:</TH><TD>";
if ($isbuggroup == 0) { if ($isbuggroup == 0) {
print "$name"; print html_quote($name);
} else { } else {
print "<INPUT TYPE=HIDDEN NAME=\"oldname\" VALUE=$name> print "<INPUT TYPE=HIDDEN NAME=\"oldname\" VALUE=" .
<INPUT SIZE=60 NAME=\"name\" VALUE=\"$name\">"; html_quote($name) . ">
<INPUT SIZE=60 NAME=\"name\" VALUE=\"" . html_quote($name) . "\">";
} }
print "</TD></TR><TR><TH>Description:</TH><TD>"; print "</TD></TR><TR><TH>Description:</TH><TD>";
if ($isbuggroup == 0) { if ($isbuggroup == 0) {
print "$description"; print html_quote($description);
} else { } else {
print "<INPUT TYPE=HIDDEN NAME=\"olddesc\" VALUE=\"$description\"> print "<INPUT TYPE=HIDDEN NAME=\"olddesc\" VALUE=\"" .
<INPUT SIZE=70 NAME=\"desc\" VALUE=\"$description\">"; html_quote($description) . "\">
<INPUT SIZE=70 NAME=\"desc\" VALUE=\"" .
html_quote($description) . "\">";
} }
print "</TD></TR><TR> print "</TD></TR><TR>
<TH>User Regexp:</TH><TD>"; <TH>User Regexp:</TH><TD>";
print "<INPUT TYPE=HIDDEN NAME=\"oldrexp\" VALUE=\"$rexp\"> print "<INPUT TYPE=HIDDEN NAME=\"oldrexp\" VALUE=\"" .
<INPUT SIZE=40 NAME=\"rexp\" VALUE=\"$rexp\"></TD></TR>"; html_quote($rexp) . "\">
<INPUT SIZE=40 NAME=\"rexp\" VALUE=\"" .
html_quote($rexp) . "\"></TD></TR>";
if ($isbuggroup == 1) { if ($isbuggroup == 1) {
print "<TR><TH>Use For Bugs:</TH><TD> print "<TR><TH>Use For Bugs:</TH><TD>
<INPUT TYPE=checkbox NAME =\"isactive\" VALUE=1 " . (($isactive == 1) ? "CHECKED" : "") . "> <INPUT TYPE=checkbox NAME =\"isactive\" VALUE=1 " . (($isactive == 1) ? "CHECKED" : "") . ">
...@@ -252,8 +258,8 @@ if ($action eq 'changeform') { ...@@ -252,8 +258,8 @@ if ($action eq 'changeform') {
print "<INPUT TYPE=HIDDEN NAME=\"oldbless-$grpid\" VALUE=$blessmember></TD>"; print "<INPUT TYPE=HIDDEN NAME=\"oldbless-$grpid\" VALUE=$blessmember></TD>";
print "<TD><INPUT TYPE=checkbox NAME=\"grp-$grpid\" $grpchecked VALUE=1>"; print "<TD><INPUT TYPE=checkbox NAME=\"grp-$grpid\" $grpchecked VALUE=1>";
print "<INPUT TYPE=HIDDEN NAME=\"oldgrp-$grpid\" VALUE=$grpmember></TD>"; print "<INPUT TYPE=HIDDEN NAME=\"oldgrp-$grpid\" VALUE=$grpmember></TD>";
print "<TD><B>$grpnam</B></TD>"; print "<TD><B>" . html_quote($grpnam) . "</B></TD>";
print "<TD>$grpdesc</TD>"; print "<TD>" . html_quote($grpdesc) . "</TD>";
print "</TR>\n"; print "</TR>\n";
} }
...@@ -290,6 +296,10 @@ if ($action eq 'add') { ...@@ -290,6 +296,10 @@ if ($action eq 'add') {
print "<td><input size=30 name=\"regexp\"></td>\n"; print "<td><input size=30 name=\"regexp\"></td>\n";
print "<td><input type=\"checkbox\" name=\"isactive\" value=\"1\" checked></td>\n"; print "<td><input type=\"checkbox\" name=\"isactive\" value=\"1\" checked></td>\n";
print "</TR></TABLE>\n<HR>\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=SUBMIT VALUE=\"Add\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"new\">\n"; print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"new\">\n";
print "</FORM>"; print "</FORM>";
...@@ -308,9 +318,13 @@ to this group, although bugs already in the group will remain in the group. ...@@ -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 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 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>"; 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 print "<b>User RegExp</b> is optional, and if filled in, will ";
grant membership to this group to anyone creating a new account with an print "automatically grant membership to this group to anyone with an ";
email address that matches this regular expression.<p>"; 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>"); PutTrailer("<a href=editgroups.cgi>Back to the group list</a>");
exit; exit;
...@@ -384,6 +398,16 @@ if ($action eq 'new') { ...@@ -384,6 +398,16 @@ if ($action eq 'new') {
VALUES ($admin, $gid, 0)"); VALUES ($admin, $gid, 0)");
SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless) SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless)
VALUES ($admin, $gid, 1)"); 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"; print "OK, done.<p>\n";
PutTrailer("<a href=\"editgroups.cgi?action=add\">Add another group</a>", PutTrailer("<a href=\"editgroups.cgi?action=add\">Add another group</a>",
"<a href=\"editgroups.cgi\">Back to the group list</a>"); "<a href=\"editgroups.cgi\">Back to the group list</a>");
...@@ -543,6 +567,7 @@ if ($action eq 'delete') { ...@@ -543,6 +567,7 @@ if ($action eq 'delete') {
SendSQL("DELETE FROM user_group_map WHERE group_id = $gid"); SendSQL("DELETE FROM user_group_map WHERE group_id = $gid");
SendSQL("DELETE FROM group_group_map WHERE grantor_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 bug_group_map WHERE group_id = $gid");
SendSQL("DELETE FROM group_control_map WHERE group_id = $gid");
SendSQL("DELETE FROM groups WHERE id = $gid"); SendSQL("DELETE FROM groups WHERE id = $gid");
print "<B>Group $gid has been deleted.</B><BR>"; print "<B>Group $gid has been deleted.</B><BR>";
......
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
use strict; use strict;
use lib "."; use lib ".";
use vars qw ($template $vars);
use Bugzilla::Constants;
require "CGI.pl"; require "CGI.pl";
require "globals.pl"; require "globals.pl";
...@@ -38,9 +39,16 @@ require "globals.pl"; ...@@ -38,9 +39,16 @@ require "globals.pl";
sub sillyness { sub sillyness {
my $zz; my $zz;
$zz = %::MFORM;
$zz = $::unconfirmedstate; $zz = $::unconfirmedstate;
} }
my %ctl = (
&::CONTROLMAPNA => 'NA',
&::CONTROLMAPSHOWN => 'Shown',
&::CONTROLMAPDEFAULT => 'Default',
&::CONTROLMAPMANDATORY => 'Mandatory'
);
# TestProduct: just returns if the specified product does exists # TestProduct: just returns if the specified product does exists
# CheckProduct: same check, optionally emit an error text # CheckProduct: same check, optionally emit an error text
...@@ -187,10 +195,9 @@ unless (UserInGroup("editcomponents")) { ...@@ -187,10 +195,9 @@ unless (UserInGroup("editcomponents")) {
# #
my $product = trim($::FORM{product} || ''); my $product = trim($::FORM{product} || '');
my $action = trim($::FORM{action} || ''); my $action = trim($::FORM{action} || '');
my $headerdone = 0;
my $localtrailer = "<A HREF=\"editproducts.cgi\">edit</A> more products"; my $localtrailer = "<A HREF=\"editproducts.cgi\">edit</A> more products";
# #
# action='' -> Show nice list of products # action='' -> Show nice list of products
# #
...@@ -342,7 +349,7 @@ if ($action eq 'new') { ...@@ -342,7 +349,7 @@ if ($action eq 'new') {
# If we're using bug groups, then we need to create a group for this # If we're using bug groups, then we need to create a group for this
# product as well. -JMR, 2/16/00 # product as well. -JMR, 2/16/00
if(Param("usebuggroups")) { if(Param("makeproductgroups")) {
# Next we insert into the groups table # Next we insert into the groups table
SendSQL("INSERT INTO groups " . SendSQL("INSERT INTO groups " .
"(name, description, isbuggroup, last_changed) " . "(name, description, isbuggroup, last_changed) " .
...@@ -356,6 +363,35 @@ if ($action eq 'new') { ...@@ -356,6 +363,35 @@ if ($action eq 'new') {
VALUES ($admin, $gid, 0)"); VALUES ($admin, $gid, 0)");
SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless) SendSQL("INSERT INTO group_group_map (member_id, grantor_id, isbless)
VALUES ($admin, $gid, 1)"); 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();
}
}
} }
...@@ -510,7 +546,7 @@ one."; ...@@ -510,7 +546,7 @@ one.";
print "<INPUT TYPE=SUBMIT VALUE=\"Yes, delete\">\n"; print "<INPUT TYPE=SUBMIT VALUE=\"Yes, delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"delete\">\n"; print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"product\" VALUE=\"" . print "<INPUT TYPE=HIDDEN NAME=\"product\" VALUE=\"" .
value_quote($product) . "\">\n"; html_quote($product) . "\">\n";
print "</FORM>"; print "</FORM>";
PutTrailer($localtrailer); PutTrailer($localtrailer);
...@@ -538,6 +574,7 @@ if ($action eq 'delete') { ...@@ -538,6 +574,7 @@ if ($action eq 'delete') {
versions WRITE, versions WRITE,
products WRITE, products WRITE,
groups WRITE, groups WRITE,
group_control_map WRITE,
profiles WRITE, profiles WRITE,
milestones WRITE, milestones WRITE,
flaginclusions WRITE, flaginclusions WRITE,
...@@ -583,6 +620,10 @@ if ($action eq 'delete') { ...@@ -583,6 +620,10 @@ if ($action eq 'delete') {
WHERE product_id=$product_id"); WHERE product_id=$product_id");
print "Milestones deleted.<BR>\n"; 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 SendSQL("DELETE FROM flaginclusions
WHERE product_id=$product_id"); WHERE product_id=$product_id");
SendSQL("DELETE FROM flagexclusions SendSQL("DELETE FROM flagexclusions
...@@ -593,7 +634,6 @@ if ($action eq 'delete') { ...@@ -593,7 +634,6 @@ if ($action eq 'delete') {
WHERE id=$product_id"); WHERE id=$product_id");
print "Product '$product' deleted.<BR>\n"; print "Product '$product' deleted.<BR>\n";
SendSQL("UNLOCK TABLES"); SendSQL("UNLOCK TABLES");
unlink "data/versioncache"; unlink "data/versioncache";
...@@ -693,6 +733,27 @@ if ($action eq 'edit') { ...@@ -693,6 +733,27 @@ if ($action eq 'edit') {
} }
print "</TD>\n</TR><TR>\n"; 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 " <TH ALIGN=\"right\">Bugs:</TH>\n";
print " <TD>"; print " <TD>";
SendSQL("SELECT count(bug_id),product_id SendSQL("SELECT count(bug_id),product_id
...@@ -706,11 +767,11 @@ if ($action eq 'edit') { ...@@ -706,11 +767,11 @@ if ($action eq 'edit') {
print "</TD>\n</TR></TABLE>\n"; print "</TD>\n</TR></TABLE>\n";
print "<INPUT TYPE=HIDDEN NAME=\"productold\" VALUE=\"" . print "<INPUT TYPE=HIDDEN NAME=\"productold\" VALUE=\"" .
value_quote($product) . "\">\n"; html_quote($product) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"descriptionold\" VALUE=\"" . print "<INPUT TYPE=HIDDEN NAME=\"descriptionold\" VALUE=\"" .
value_quote($description) . "\">\n"; html_quote($description) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"milestoneurlold\" VALUE=\"" . 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=\"disallownewold\" VALUE=\"$disallownew\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"votesperuserold\" VALUE=\"$votesperuser\">\n"; print "<INPUT TYPE=HIDDEN NAME=\"votesperuserold\" VALUE=\"$votesperuser\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"maxvotesperbugold\" VALUE=\"$maxvotesperbug\">\n"; print "<INPUT TYPE=HIDDEN NAME=\"maxvotesperbugold\" VALUE=\"$maxvotesperbug\">\n";
...@@ -729,6 +790,230 @@ if ($action eq 'edit') { ...@@ -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 # action='update' -> update the product
...@@ -770,6 +1055,7 @@ if ($action eq 'update') { ...@@ -770,6 +1055,7 @@ if ($action eq 'update') {
SendSQL("LOCK TABLES products WRITE, SendSQL("LOCK TABLES products WRITE,
versions READ, versions READ,
groups WRITE, groups WRITE,
group_control_map WRITE,
profiles WRITE, profiles WRITE,
milestones READ"); milestones READ");
...@@ -863,9 +1149,9 @@ if ($action eq 'update') { ...@@ -863,9 +1149,9 @@ if ($action eq 'update') {
} }
SendSQL("UPDATE products SET name=$qp WHERE id=$product_id"); SendSQL("UPDATE products SET name=$qp WHERE id=$product_id");
# Need to do an update to groups as well. If there is a corresponding # Need to do an update to groups as well. If there is
# bug group, whether usebuggroups is currently set or not, we want to # a corresponding bug group, we want to update it so it will
# update it so it will match in the future. If there is no group, this # match in the future. If there is no group, this
# update statement will do nothing, so no harm done. -JMR, 3/8/00 # update statement will do nothing, so no harm done. -JMR, 3/8/00
SendSQL("UPDATE groups " . SendSQL("UPDATE groups " .
"SET name = $qp, " . "SET name = $qp, " .
...@@ -945,6 +1231,235 @@ if ($action eq 'update') { ...@@ -945,6 +1231,235 @@ if ($action eq 'update') {
exit; 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; ...@@ -36,6 +36,7 @@ use strict;
use lib qw(.); use lib qw(.);
use Bugzilla::Constants;
require "CGI.pl"; require "CGI.pl";
use vars qw( use vars qw(
...@@ -51,6 +52,7 @@ use vars qw( ...@@ -51,6 +52,7 @@ use vars qw(
$userid $userid
%MFORM %MFORM
%versions %versions
$proddesc
); );
# We have to connect to the database, even though we don't use it in this code, # We have to connect to the database, even though we don't use it in this code,
...@@ -60,7 +62,7 @@ ConnectToDatabase(); ...@@ -60,7 +62,7 @@ ConnectToDatabase();
# If we're using bug groups to restrict bug entry, we need to know who the # If we're using bug groups to restrict bug entry, we need to know who the
# user is right from the start. # user is right from the start.
confirm_login() if (Param("usebuggroupsentry")); confirm_login() if AnyEntryGroups();
if (!defined $::FORM{'product'}) { if (!defined $::FORM{'product'}) {
GetVersionTable(); GetVersionTable();
...@@ -69,9 +71,7 @@ if (!defined $::FORM{'product'}) { ...@@ -69,9 +71,7 @@ if (!defined $::FORM{'product'}) {
my %products; my %products;
foreach my $p (@enterable_products) { foreach my $p (@enterable_products) {
if (!(Param("usebuggroupsentry") if (CanEnterProduct($p))
&& GroupExists($p)
&& !UserInGroup($p)))
{ {
$products{$p} = $::proddesc{$p}; $products{$p} = $::proddesc{$p};
} }
...@@ -215,13 +215,11 @@ sub pickos { ...@@ -215,13 +215,11 @@ sub pickos {
# End of subroutines # 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. # that the user has permission to enter a bug against this product.
if(Param("usebuggroupsentry") if(!CanEnterProduct($product))
&& GroupExists($product)
&& !UserInGroup($product))
{ {
ThrowUserError("entry_access_denied", { product => $product}); ThrowUserError("entry_access_denied", { product => $product});
} }
...@@ -309,30 +307,25 @@ if (UserInGroup("editbugs") || UserInGroup("canconfirm")) { ...@@ -309,30 +307,25 @@ if (UserInGroup("editbugs") || UserInGroup("canconfirm")) {
$vars->{'bug_status'} = \@status; $vars->{'bug_status'} = \@status;
$default{'bug_status'} = $status[0]; $default{'bug_status'} = $status[0];
# Select whether to restrict this bug to the product's bug group or not, SendSQL("SELECT DISTINCT groups.id, groups.name, groups.description, " .
# if the usebuggroups parameter is set, and if this product has a bug group. "membercontrol, othercontrol " .
# First we get the bit and description for the group. "FROM groups LEFT JOIN group_control_map " .
my $group_id = '0'; "ON group_id = id AND product_id = $product_id " .
"WHERE isbuggroup != 0 AND isactive != 0 ORDER BY description");
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");
my @groups; my @groups;
while (MoreSQLData()) { while (MoreSQLData()) {
my ($id, $prodname, $description) = FetchSQLData(); my ($id, $groupname, $description, $membercontrol, $othercontrol)
# Don't want to include product groups other than this product. = FetchSQLData();
next unless(!Param("usebuggroups") || $prodname eq $product || # Only include groups if the entering user will have an option.
!defined($::proddesc{$prodname})); next if ((!$membercontrol)
|| ($membercontrol == CONTROLMAPNA)
|| ($membercontrol == CONTROLMAPMANDATORY)
|| (($othercontrol != CONTROLMAPSHOWN)
&& ($othercontrol != CONTROLMAPDEFAULT)
&& (!UserInGroup($groupname)))
);
my $check; my $check;
# If this is the group for this product, make it checked. # If this is the group for this product, make it checked.
...@@ -343,11 +336,10 @@ while (MoreSQLData()) { ...@@ -343,11 +336,10 @@ while (MoreSQLData()) {
$check = formvalue("bit-$id", 0); $check = formvalue("bit-$id", 0);
} }
else { else {
# $group_bit will only have a non-zero value if we're using # Checkbox is checked by default if $control is a default state.
# bug groups and have one for this product. $check = (($membercontrol == CONTROLMAPDEFAULT)
# If $group_bit is 0, it won't match the current group, so compare || (($othercontrol == CONTROLMAPDEFAULT)
# it to the current bit instead of checking for non-zero. && (!UserInGroup($groupname))));
$check = ($group_id == $id);
} }
my $group = my $group =
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
use strict; use strict;
use Bugzilla::Constants;
use Bugzilla::Util; use Bugzilla::Util;
# Bring ChmodDataFile in until this is all moved to the module # Bring ChmodDataFile in until this is all moved to the module
use Bugzilla::Config qw(:DEFAULT ChmodDataFile); use Bugzilla::Config qw(:DEFAULT ChmodDataFile);
...@@ -684,6 +685,116 @@ sub GenerateRandomPassword { ...@@ -684,6 +685,116 @@ sub GenerateRandomPassword {
return $password; 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 { sub CanSeeBug {
my ($id, $userid) = @_; my ($id, $userid) = @_;
...@@ -1749,5 +1860,5 @@ $::vars = ...@@ -1749,5 +1860,5 @@ $::vars =
'VERSION' => $Bugzilla::Config::VERSION, 'VERSION' => $Bugzilla::Config::VERSION,
}; };
1; 1;
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
use strict; use strict;
use lib qw(.); use lib qw(.);
use Bugzilla::Constants;
require "CGI.pl"; require "CGI.pl";
require "bug_form.pl"; require "bug_form.pl";
...@@ -101,9 +102,8 @@ if (defined $::FORM{'maketemplate'}) { ...@@ -101,9 +102,8 @@ if (defined $::FORM{'maketemplate'}) {
umask 0; umask 0;
# Some sanity checking # Some sanity checking
if(Param("usebuggroupsentry") && GroupExists($product)) { if (!CanEnterProduct($product)) {
UserInGroup($product) || ThrowUserError("entry_access_denied", {product => $product});
ThrowUserError("entry_access_denied", {product => $product});
} }
my $component_id = get_component_id($product_id, $::FORM{component}); my $component_id = get_component_id($product_id, $::FORM{component});
...@@ -363,13 +363,38 @@ foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) { ...@@ -363,13 +363,38 @@ foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) {
WHERE user_id = $::userid WHERE user_id = $::userid
AND group_id = $v AND group_id = $v
AND isbless = 0"); AND isbless = 0");
my ($member) = FetchSQLData(); my ($permit) = FetchSQLData();
if ($member) { 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) 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 # Lock tables before inserting records for the new bug into the database
# if we are using a shadow database to prevent shadow database corruption # if we are using a shadow database to prevent shadow database corruption
......
...@@ -31,6 +31,7 @@ my $UserInCanConfirmGroupSet = -1; ...@@ -31,6 +31,7 @@ my $UserInCanConfirmGroupSet = -1;
use lib qw(.); use lib qw(.);
use Bugzilla::Constants;
require "CGI.pl"; require "CGI.pl";
require "bug_form.pl"; require "bug_form.pl";
...@@ -236,7 +237,7 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct) ...@@ -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 # 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 # whether or not to add the bugs to their new product's group, display
# a verification form. # a verification form.
if (!$vok || !$cok || !$mok || (Param('usebuggroups') && !defined($::FORM{'addtonewgroup'}))) { if (!$vok || !$cok || !$mok || (AnyDefaultGroups() && !defined($::FORM{'addtonewgroup'}))) {
$vars->{'form'} = \%::FORM; $vars->{'form'} = \%::FORM;
$vars->{'mform'} = \%::MFORM; $vars->{'mform'} = \%::MFORM;
...@@ -276,7 +277,7 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct) ...@@ -276,7 +277,7 @@ if ((($::FORM{'id'} && $::FORM{'product'} ne $::oldproduct)
$vars->{"verify_fields"} = 0; $vars->{"verify_fields"} = 0;
} }
$vars->{'verify_bug_group'} = (Param('usebuggroups') $vars->{'verify_bug_group'} = (AnyDefaultGroups()
&& !defined($::FORM{'addtonewgroup'})); && !defined($::FORM{'addtonewgroup'}));
$template->process("bug/process/verify-new-product.html.tmpl", $vars) $template->process("bug/process/verify-new-product.html.tmpl", $vars)
...@@ -594,11 +595,9 @@ sub ChangeResolution { ...@@ -594,11 +595,9 @@ sub ChangeResolution {
# select lists. This means that instead of looking for the bit-X values in # 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 # 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. # 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 # If the form element isn't present, or the user isn't in the group, leave
# it as-is # it as-is
my @groupAdd = (); my @groupAdd = ();
my @groupDel = (); my @groupDel = ();
...@@ -1070,7 +1069,10 @@ foreach my $id (@idlist) { ...@@ -1070,7 +1069,10 @@ foreach my $id (@idlist) {
"bug_group_map $write, flags $write, duplicates $write," . "bug_group_map $write, flags $write, duplicates $write," .
"user_group_map READ, flagtypes READ, " . "user_group_map READ, flagtypes READ, " .
"flaginclusions AS i READ, flagexclusions AS e 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 @oldvalues = SnapShotBug($id);
my %oldhash; my %oldhash;
# Fun hack. @::log_columns only contains the component_id, # Fun hack. @::log_columns only contains the component_id,
...@@ -1104,6 +1106,18 @@ foreach my $id (@idlist) { ...@@ -1104,6 +1106,18 @@ foreach my $id (@idlist) {
$i++; $i++;
} }
$oldhash{'product'} = get_product_name($oldhash{'product_id'}); $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) { if ($requiremilestone) {
my $value = $::FORM{'target_milestone'}; my $value = $::FORM{'target_milestone'};
if (!defined $value || $value eq $::FORM{'dontchange'}) { if (!defined $value || $value eq $::FORM{'dontchange'}) {
...@@ -1268,6 +1282,7 @@ foreach my $id (@idlist) { ...@@ -1268,6 +1282,7 @@ foreach my $id (@idlist) {
if ($::comma ne "") { if ($::comma ne "") {
SendSQL($query); SendSQL($query);
} }
# Check for duplicates if the bug is [re]open # Check for duplicates if the bug is [re]open
SendSQL("SELECT resolution FROM bugs WHERE bug_id = $id"); SendSQL("SELECT resolution FROM bugs WHERE bug_id = $id");
my $resolution = FetchOneColumn(); my $resolution = FetchOneColumn();
...@@ -1275,8 +1290,34 @@ foreach my $id (@idlist) { ...@@ -1275,8 +1290,34 @@ foreach my $id (@idlist) {
SendSQL("DELETE FROM duplicates WHERE dupe = $id"); 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 = (); 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)) { if (!BugInGroupId($id, $grouptoadd)) {
push(@groupAddNames, GroupIdToName($grouptoadd)); push(@groupAddNames, GroupIdToName($grouptoadd));
SendSQL("INSERT INTO bug_group_map (bug_id, group_id) SendSQL("INSERT INTO bug_group_map (bug_id, group_id)
...@@ -1284,7 +1325,10 @@ foreach my $id (@idlist) { ...@@ -1284,7 +1325,10 @@ foreach my $id (@idlist) {
} }
} }
my @groupDelNames = (); 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)) { if (BugInGroupId($id, $grouptodel)) {
push(@groupDelNames, GroupIdToName($grouptodel)); push(@groupDelNames, GroupIdToName($grouptodel));
} }
...@@ -1399,63 +1443,117 @@ foreach my $id (@idlist) { ...@@ -1399,63 +1443,117 @@ foreach my $id (@idlist) {
# group or add it to the new one. There are a very specific series of # group or add it to the new one. There are a very specific series of
# conditions under which these activities take place, more information # conditions under which these activities take place, more information
# about which can be found in comments within the conditionals below. # 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 ( if (
# the "usebuggroups" parameter is on, indicating that products defined $::FORM{'product'}
# are associated with groups of the same name;
Param('usebuggroups')
# the user has changed the product to which the bug belongs;
&& defined $::FORM{'product'}
&& $::FORM{'product'} ne $::FORM{'dontchange'} && $::FORM{'product'} ne $::FORM{'dontchange'}
&& $::FORM{'product'} ne $oldhash{'product'} && $::FORM{'product'} ne $oldhash{'product'}
) { ) {
if ( my $newproduct_id = get_product_id($::FORM{'product'});
# the user wants to add the bug to the new product's group; # Depending on the "addtonewgroup" variable, groups with
($::FORM{'addtonewgroup'} eq 'yes' # defaults will change.
|| ($::FORM{'addtonewgroup'} eq 'yesifinold' #
&& BugInGroup($id, $oldhash{'product'}))) # For each group, determine
# - The group id and if it is active
# the new product is associated with a group; # - The control map value for the old product and this group
&& GroupExists($::FORM{'product'}) # - The control map value for the new product and this group
# - Is the user in this group?
# the bug is not already in the group; (This can happen when the user # - Is the bug in this group?
# goes to the "edit multiple bugs" form with a list of bugs at least SendSQL("SELECT DISTINCT groups.id, isactive, " .
# one of which is in the new group. In this situation, the user can "oldcontrolmap.membercontrol, newcontrolmap.membercontrol, " .
# simultaneously change the bugs to a new product and move the bugs "user_group_map.user_id IS NOT NULL, " .
# into that product's group, which happens earlier in this script "bug_group_map.group_id IS NOT NULL " .
# and thus is already done. If we didn't check for this, then this "FROM groups " .
# situation would cause us to add the bug to the group twice, which "LEFT JOIN group_control_map AS oldcontrolmap " .
# would result in the bug being added to a totally different group.) "ON oldcontrolmap.group_id = groups.id " .
&& !BugInGroup($id, $::FORM{'product'}) "AND oldcontrolmap.product_id = " . $oldhash{'product_id'} .
" LEFT JOIN group_control_map AS newcontrolmap " .
# the user is a member of the associated group, indicating they "ON newcontrolmap.group_id = groups.id " .
# are authorized to add bugs to that group, *or* the "usebuggroupsentry" "AND newcontrolmap.product_id = $newproduct_id " .
# parameter is off, indicating that users can add bugs to a product "LEFT JOIN user_group_map " .
# regardless of whether or not they belong to its associated group; "ON user_group_map.group_id = groups.id " .
&& (UserInGroup($::FORM{'product'}) || !Param('usebuggroupsentry')) "AND user_group_map.user_id = $::userid " .
"AND user_group_map.isbless = 0 " .
# the associated group is active, indicating it can accept new bugs; "LEFT JOIN bug_group_map " .
&& GroupIsActive(GroupNameToId($::FORM{'product'})) "ON bug_group_map.group_id = groups.id " .
) { "AND bug_group_map.bug_id = $id "
# Add the bug to the group associated with its new product. );
my $groupid = GroupNameToId($::FORM{'product'}); my @groupstoremove = ();
if (!BugInGroupId($id, $groupid)) { my @groupstoadd = ();
SendSQL("INSERT INTO bug_group_map (bug_id, group_id) VALUES ($id, $groupid)"); 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
if ( # and new default groups will be added.
# the old product is associated with a group; # If addtonewgroups = "yesifinold", old default groups will be removed
GroupExists($oldhash{'product'}) # and new default groups will be added only if the bug was in ANY
# of the old default groups.
# the bug is a member of that group; # If addtonewgroups = "no", old default groups will be removed and not
&& BugInGroup($id, $oldhash{'product'}) # replaced.
) { push(@groupstoremove, @defaultstoremove);
# Remove the bug from the group associated with its old product. if (AnyDefaultGroups()
my $groupid = GroupNameToId($oldhash{'product'}); && (($::FORM{'addtonewgroup'} eq 'yes')
SendSQL("DELETE FROM bug_group_map WHERE bug_id = $id AND group_id = $groupid"); || (($::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, # get a snapshot of the newly set values out of the database,
...@@ -1471,7 +1569,6 @@ foreach my $id (@idlist) { ...@@ -1471,7 +1569,6 @@ foreach my $id (@idlist) {
$newhash{$col} = $newvalues[$i]; $newhash{$col} = $newvalues[$i];
$i++; $i++;
} }
# for passing to processmail to ensure that when someone is removed # for passing to processmail to ensure that when someone is removed
# from one of these fields, they get notified of that fact (if desired) # from one of these fields, they get notified of that fact (if desired)
# #
......
...@@ -191,19 +191,14 @@ if ($default{'chfieldto'}->[0] eq "") { ...@@ -191,19 +191,14 @@ if ($default{'chfieldto'}->[0] eq "") {
GetVersionTable(); GetVersionTable();
# if using usebuggroups, then we don't want people to see products they don't # if using groups for entry, then we don't want people to see products they
# have access to. Remove them from the list. # don't have access to. Remove them from the list.
my @products = (); my @products = ();
my %component_set; my %component_set;
my %version_set; my %version_set;
my %milestone_set; my %milestone_set;
foreach my $p (@::legal_product) { foreach my $p (GetEnterableProducts()) {
# 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));
# We build up boolean hashes in the "-set" hashes for each of these things # 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. # before making a list because there may be duplicates names across products.
push @products, $p; push @products, $p;
......
...@@ -663,7 +663,7 @@ SendSQL("SELECT name, description FROM products ORDER BY name"); ...@@ -663,7 +663,7 @@ SendSQL("SELECT name, description FROM products ORDER BY name");
while (MoreSQLData()) { while (MoreSQLData()) {
my ($product, $productdesc) = FetchSQLData(); my ($product, $productdesc) = FetchSQLData();
next if (Param("usebuggroups") && GroupExists($product) && !UserInGroup($product)); next if (!CanEnterProduct($product));
push (@products, $product); push (@products, $product);
$line_count++; $line_count++;
......
...@@ -23,8 +23,6 @@ ...@@ -23,8 +23,6 @@
# Dawn Endico <endico@mozilla.org> # Dawn Endico <endico@mozilla.org>
# Bryce Nesbitt <bryce@nextbus.COM>, # Bryce Nesbitt <bryce@nextbus.COM>,
# Joe Robins <jmrobins@tgix.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> # Gervase Markham <gerv@gerv.net> and Adam Spiers <adam@spiers.net>
# Added ability to chart any combination of resolutions/statuses. # Added ability to chart any combination of resolutions/statuses.
# Derive the choice of resolutions/statuses from the -All- data file # Derive the choice of resolutions/statuses from the -All- data file
...@@ -60,20 +58,11 @@ quietly_check_login(); ...@@ -60,20 +58,11 @@ quietly_check_login();
GetVersionTable(); 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. # We only want those products that the user has permissions for.
my @myproducts; my @myproducts;
if(Param("usebuggroups")) { push( @myproducts, "-All-");
push( @myproducts, "-All-"); foreach my $this_product (@legal_product) {
foreach my $this_product (@legal_product) { push(@myproducts, $this_product) if CanEnterProduct($this_product);
if(GroupExists($this_product) && !UserInGroup($this_product)) {
next;
} else {
push( @myproducts, $this_product )
}
}
} else {
push( @myproducts, "-All-", @legal_product );
} }
if (! defined $FORM{'product'}) { if (! defined $FORM{'product'}) {
...@@ -91,12 +80,11 @@ if (! defined $FORM{'product'}) { ...@@ -91,12 +80,11 @@ if (! defined $FORM{'product'}) {
grep($_ eq $FORM{'product'}, @myproducts) grep($_ eq $FORM{'product'}, @myproducts)
|| ThrowUserError("invalid_product_name", {product => $FORM{'product'}}); || 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... # reports for products they don't have permissions for...
Param("usebuggroups") if (!CanEnterProduct($FORM{'product'})) {
&& GroupExists($FORM{'product'}) ThrowUserError("report_access_denied");
&& !UserInGroup($FORM{'product'}) }
&& ThrowUserError("report_access_denied");
# We've checked that the product exists, and that the user can see it # We've checked that the product exists, and that the user can see it
# This means that is OK to detaint # This means that is OK to detaint
......
...@@ -26,6 +26,7 @@ use strict; ...@@ -26,6 +26,7 @@ use strict;
use lib qw(.); use lib qw(.);
require "CGI.pl"; require "CGI.pl";
use Bugzilla::Constants;
use vars qw(%FORM $unconfirmedstate); use vars qw(%FORM $unconfirmedstate);
...@@ -263,6 +264,7 @@ CrossCheck("groups", "id", ...@@ -263,6 +264,7 @@ CrossCheck("groups", "id",
["bug_group_map", "group_id"], ["bug_group_map", "group_id"],
["group_group_map", "grantor_id"], ["group_group_map", "grantor_id"],
["group_group_map", "member_id"], ["group_group_map", "member_id"],
["group_control_map", "group_id"],
["user_group_map", "group_id"]); ["user_group_map", "group_id"]);
CrossCheck("profiles", "userid", CrossCheck("profiles", "userid",
...@@ -288,6 +290,7 @@ CrossCheck("products", "id", ...@@ -288,6 +290,7 @@ CrossCheck("products", "id",
["components", "product_id", "name"], ["components", "product_id", "name"],
["milestones", "product_id", "value"], ["milestones", "product_id", "value"],
["versions", "product_id", "value"], ["versions", "product_id", "value"],
["group_control_map", "product_id"],
["flaginclusions", "product_id", "type_id"], ["flaginclusions", "product_id", "type_id"],
["flagexclusions", "product_id", "type_id"]); ["flagexclusions", "product_id", "type_id"]);
...@@ -613,6 +616,53 @@ DateCheck("groups", "last_changed"); ...@@ -613,6 +616,53 @@ DateCheck("groups", "last_changed");
DateCheck("profiles", "refreshed_when"); 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 # 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,20 +405,28 @@ ...@@ -405,20 +405,28 @@
<br> <br>
[% IF groups.size > 0 %] [% IF groups.size > 0 %]
<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>
<br>
<br>
[% FOREACH group = groups %] [% 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 more public bug.)
</font>
<br>
<br>
[% END %]
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
<input type="checkbox" name="bit-[% group.bit %]" value="1" <input type="checkbox" name="bit-[% group.bit %]" value="1"
[% " checked=\"checked\"" IF group.ison %] [% " checked=\"checked\"" IF group.ison %]
[% " disabled=\"disabled\"" IF NOT group.ingroup %]> [% " disabled=\"disabled\"" IF NOT group.ingroup %]>
[% group.description %] [% group.description %]
<br> <br>
[% END %]
[% END %] [% END %]
[% IF NOT user.inallgroups %] [% IF NOT user.inallgroups %]
...@@ -431,7 +439,7 @@ ...@@ -431,7 +439,7 @@
[% IF bug.inagroup %] [% IF bug.inagroup %]
<p> <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> <br>
<small> <small>
(The assignee (The assignee
......
...@@ -81,14 +81,14 @@ ...@@ -81,14 +81,14 @@
<h3>Verify Bug Group</h3> <h3>Verify Bug Group</h3>
<p> <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>
<p> <p>
<input type="radio" name="addtonewgroup" value="no"><b>no</b><br> <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="yes"><b>yes</b><br>
<input type="radio" name="addtonewgroup" value="yesifinold" checked="checked"> <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> </p>
[% END %] [% END %]
......
...@@ -230,6 +230,12 @@ ...@@ -230,6 +230,12 @@
It must also not contain any of these special characters: It must also not contain any of these special characters:
<tt>\ ( ) &amp; &lt; &gt; , ; : &quot; [ ]</tt>, or any whitespace. <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" %] [% ELSIF error == "illegal_is_obsolete" %]
[% title = "Your Query Makes No Sense" %] [% title = "Your Query Makes No Sense" %]
The only legal values for the <em>Attachment is obsolete</em> field are The only legal values for the <em>Attachment is obsolete</em> field are
...@@ -462,6 +468,10 @@ ...@@ -462,6 +468,10 @@
[% title = "Access Denied" %] [% title = "Access Denied" %]
You do not have the permissions necessary to access that product. 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" %] [% ELSIF error == "query_name_missing" %]
[% title = "No Query Name Specified" %] [% title = "No Query Name Specified" %]
You must enter a name for your query. 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