Compare commits

..

38 Commits

Author SHA1 Message Date
dkl%redhat.com
8c591d53e2 Removed some remaining SelectVisible calls in favor of CanSeeBug
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@121533 18797224-902f-48f8-a5cc-f745e15eee43
2002-05-15 18:36:31 +00:00
dkl%redhat.com
c1aa983fd5 Update to HEAD 2002/05/13
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@121401 18797224-902f-48f8-a5cc-f745e15eee43
2002-05-13 21:56:49 +00:00
dkl%redhat.com
3551227412 forgot one
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@121393 18797224-902f-48f8-a5cc-f745e15eee43
2002-05-13 20:49:00 +00:00
dkl%redhat.com
d0cc91f285 Fixed some template inconsistencies with current 2.16 Stable Branch
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@121392 18797224-902f-48f8-a5cc-f745e15eee43
2002-05-13 20:46:50 +00:00
(no author)
65ff7d56b3 This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@121370 18797224-902f-48f8-a5cc-f745e15eee43
2002-05-13 06:24:51 +00:00
dkl%redhat.com
800eccde9a Merge with HEAD 2002/04/26
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@119979 18797224-902f-48f8-a5cc-f745e15eee43
2002-04-26 18:59:37 +00:00
(no author)
5360e5b008 This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@119975 18797224-902f-48f8-a5cc-f745e15eee43
2002-04-26 18:12:55 +00:00
dkl%redhat.com
da759055dd Sync to HEAD 2002/03/21
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@117156 18797224-902f-48f8-a5cc-f745e15eee43
2002-03-22 05:16:48 +00:00
(no author)
1f960bb1bd This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@116669 18797224-902f-48f8-a5cc-f745e15eee43
2002-03-15 23:23:14 +00:00
dkl%redhat.com
e0f4b89db1 Update to HEAD 2002/02/26
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@115395 18797224-902f-48f8-a5cc-f745e15eee43
2002-02-27 01:11:14 +00:00
(no author)
025b6e8e46 This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@115298 18797224-902f-48f8-a5cc-f745e15eee43
2002-02-24 09:28:23 +00:00
dkl%redhat.com
704f46aa53 Update to HEAD 2002/02/04
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@113607 18797224-902f-48f8-a5cc-f745e15eee43
2002-02-04 15:56:15 +00:00
(no author)
f26338df7e This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@113575 18797224-902f-48f8-a5cc-f745e15eee43
2002-02-03 09:28:50 +00:00
dkl%redhat.com
58548c3f0d Update to HEAD 2002/01/30
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@113247 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-30 23:00:13 +00:00
(no author)
9a6b4393ad This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@113166 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-29 23:26:38 +00:00
dkl%redhat.com
4316819604 Fix runtests.sh error on processmail
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@112507 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-21 23:27:24 +00:00
dkl%redhat.com
9d93dfabb8 Fix botched earlier sync with HEAD
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@112499 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-21 20:42:30 +00:00
dkl%redhat.com
d2ddb07675 Update to HEAD 01/18/2002
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@112473 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-18 23:06:43 +00:00
dkl%redhat.com
66d426dc97 Lost the pgsetup.pl file somewhere along the line. Adding back properly.
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111575 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-08 16:13:05 +00:00
dkl%redhat.com
b7e91cb3b6 Changes to CanSeeBug to allow multiple checks in one call for buglist.cgi
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111509 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-07 23:20:15 +00:00
dkl%redhat.com
5ac0899827 Update to HEAD 2002-01-07
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111508 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-07 23:14:41 +00:00
dkl%redhat.com
4f49e57a3b Merge 3 with HEAD: 2001/12/26
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111103 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-27 07:26:12 +00:00
dkl%redhat.com
38c27be28f Merge 2 with HEAD: 2001/12/26
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111102 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-27 06:02:04 +00:00
dkl%redhat.com
d60d3d6121 Merge fix. This is frustrating.
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111101 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-27 05:29:04 +00:00
dkl%redhat.com
db0b87fb6c Merge with HEAD on 2001/12/26
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@111100 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-27 05:09:43 +00:00
(no author)
6e2791a4b7 This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@110404 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-12 22:41:21 +00:00
dkl%redhat.com
14542c62c7 Update to HEAD 2001-12-03
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@109510 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-03 04:06:19 +00:00
dkl%redhat.com
38ebcba576 Fixed error in AddFDef
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@108598 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-20 18:40:38 +00:00
dkl%redhat.com
a5502157a9 Update to HEAD - November 18, 2001
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@108470 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-19 05:57:30 +00:00
(no author)
ba69b37618 This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@108456 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-18 22:20:21 +00:00
dkl%redhat.com
22b863a5e9 Synced up with CVS HEAD and created Bugzilla_PgSQL_branch_sync tag
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@107700 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-08 23:03:10 +00:00
dkl%redhat.com
3e54979994 Fixed conflict in Bug.pm, removal of tabs from lots of files.
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@107385 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-06 01:21:11 +00:00
dkl%redhat.com
d73ca44c76 Add new cpan module dependencies
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@107356 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-05 21:43:11 +00:00
dkl%redhat.com
a4fc52b12e Updates and bug fixes
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@107354 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-05 21:10:15 +00:00
dkl%redhat.com
353baca797 New SQL utility functions. Changes in buglist.cgi to improve queries.
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@106967 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-01 15:35:03 +00:00
dkl%redhat.com
4618ab6c36 Initial checkin of pgsetup.pl. Utility for setting up Bugzilla database in PostgreSQL.
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@106569 18797224-902f-48f8-a5cc-f745e15eee43
2001-10-29 20:15:50 +00:00
justdave%syndicomm.com
faaed9c15f initial commit of PgSQL megapatch v0.2
git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@106532 18797224-902f-48f8-a5cc-f745e15eee43
2001-10-29 07:49:05 +00:00
(no author)
675f64d0ae This commit was manufactured by cvs2svn to create branch
'Bugzilla_PgSQL_branch'.

git-svn-id: svn://10.0.0.236/branches/Bugzilla_PgSQL_branch@106501 18797224-902f-48f8-a5cc-f745e15eee43
2001-10-28 03:41:01 +00:00
62 changed files with 7655 additions and 2266 deletions

View File

@@ -43,7 +43,7 @@ sub query
# "attachments" variable.
my ($bugid) = @_;
my $in_editbugs = &::UserInGroup($::userid, "editbugs");
my $in_editbugs = &::UserInGroup("editbugs");
# Retrieve a list of attachments for this bug and write them into an array
# of hashes in which each hash represents a single attachment.

View File

@@ -36,8 +36,8 @@ my %ok_field;
for my $key (qw (bug_id product version rep_platform op_sys bug_status
resolution priority bug_severity component assigned_to
reporter bug_file_loc short_desc target_milestone
qa_contact status_whiteboard creation_ts
delta_ts votes whoid comment query error) ){
qa_contact status_whiteboard creation_ts groupset
delta_ts votes whoid usergroupset comment query error) ){
$ok_field{$key}++;
}
@@ -101,48 +101,77 @@ sub initBug {
$self->{'whoid'} = $user_id;
&::SendSQL("SELECT groupset FROM profiles WHERE userid=$self->{'whoid'}");
my $usergroupset = &::FetchOneColumn();
if (!$usergroupset) { $usergroupset = '0' }
$self->{'usergroupset'} = $usergroupset;
# First check that we can see it
if (!&::CanSeeBug($bug_id, $user_id)) {
# is it not there, or are we just forbidden to see it?
&::SendSQL("SELECT bug_id FROM bugs WHERE bug_id = $bug_id");
if (&::FetchSQLData()) {
$self->{'error'} = "NotPermitted";
} else {
$self->{'error'} = "NotFound";
}
$self->{'bug_id'} = $bug_id;
# Check to see if we can see this bug
if (!&::CanSeeBug($bug_id, $user_id, $usergroupset)) {
# Permission denied to see bug
$self->{'bug_id'} = $old_bug_id;
$self->{'error'} = "PermissionDenied";
return $self;
}
my $query = "
my $query = "";
if ($::driver eq 'mysql') {
$query = "
select
bugs.bug_id, product, version, rep_platform, op_sys, bug_status,
resolution, priority, bug_severity, component, assigned_to, reporter,
bug_file_loc, short_desc, target_milestone, qa_contact,
status_whiteboard, date_format(creation_ts,'%Y-%m-%d %H:%i'),
delta_ts, sum(votes.count)
groupset, delta_ts, sum(votes.count)
from bugs left join votes using(bug_id)
where bugs.bug_id = $bug_id
group by bugs.bug_id";
} elsif ($::driver eq 'Pg') {
$query = "
select
bugs.bug_id, product, version, rep_platform, op_sys, bug_status,
resolution, priority, bug_severity, component, assigned_to, reporter,
bug_file_loc, short_desc, target_milestone, qa_contact,
status_whiteboard, creation_ts,
groupset, delta_ts, sum(votes.count)
from bugs left join votes using(bug_id)
where bugs.bug_id = $bug_id
and (bugs.groupset & int8($usergroupset)) = bugs.groupset
group by bugs.bug_id, product, version, rep_platform, op_sys, bug_status,
resolution, priority, bug_severity, component, assigned_to, reporter,
bug_file_loc, short_desc, target_milestone, qa_contact, status_whiteboard,
creation_ts, groupset, delta_ts";
}
&::SendSQL($query);
my @row;
@row = &::FetchSQLData();
my $count = 0;
my %fields;
foreach my $field ("bug_id", "product", "version", "rep_platform",
"op_sys", "bug_status", "resolution", "priority",
"bug_severity", "component", "assigned_to", "reporter",
"bug_file_loc", "short_desc", "target_milestone",
"qa_contact", "status_whiteboard", "creation_ts",
"delta_ts", "votes") {
$fields{$field} = shift @row;
if ($fields{$field}) {
$self->{$field} = $fields{$field};
}
$count++;
if (@row = &::FetchSQLData()) {
my $count = 0;
my %fields;
foreach my $field ("bug_id", "product", "version", "rep_platform",
"op_sys", "bug_status", "resolution", "priority",
"bug_severity", "component", "assigned_to", "reporter",
"bug_file_loc", "short_desc", "target_milestone",
"qa_contact", "status_whiteboard", "creation_ts",
"groupset", "delta_ts", "votes") {
$fields{$field} = shift @row;
if ($fields{$field}) {
$self->{$field} = $fields{$field};
}
$count++;
}
} else {
&::SendSQL("select groupset from bugs where bug_id = $bug_id");
if (@row = &::FetchSQLData()) {
$self->{'bug_id'} = $bug_id;
$self->{'error'} = "NotPermitted";
return $self;
} else {
$self->{'bug_id'} = $bug_id;
$self->{'error'} = "NotFound";
return $self;
}
}
$self->{'assigned_to'} = &::DBID_to_name($self->{'assigned_to'});
@@ -346,14 +375,24 @@ sub XML_Footer {
sub UserInGroup {
my $self = shift();
my ($groupname) = (@_);
return &::UserInGroup($self->{'whoid'}, $groupname);
if ($self->{'usergroupset'} eq "0") {
return 0;
}
&::ConnectToDatabase();
&::SendSQL("select (group_bit & int8($self->{'usergroupset'})) != 0 from groups where name = "
. &::SqlQuote($groupname));
my $bit = &::FetchOneColumn();
if ($bit) {
return 1;
}
return 0;
}
sub CanChangeField {
my $self = shift();
my ($f, $oldvalue, $newvalue) = (@_);
my $UserInEditGroup = -1;
my $UserInCanConfirmGroup = -1;
my $UserInEditGroupSet = -1;
my $UserInCanConfirmGroupSet = -1;
my $ownerid;
my $reporterid;
my $qacontactid;
@@ -376,10 +415,10 @@ sub CanChangeField {
if ($f =~ /^longdesc/) {
return 1;
}
if ($UserInEditGroup < 0) {
$UserInEditGroup = UserInGroup($self, "editbugs");
if ($UserInEditGroupSet < 0) {
$UserInEditGroupSet = UserInGroup($self, "editbugs");
}
if ($UserInEditGroup) {
if ($UserInEditGroupSet) {
return 1;
}
&::SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs " .
@@ -400,10 +439,10 @@ sub CanChangeField {
# group? Or, has it ever been confirmed? If not, then this
# isn't legal.
if ($UserInCanConfirmGroup < 0) {
$UserInCanConfirmGroup = &::UserInGroup($self->{'whoid'},"canconfirm");
if ($UserInCanConfirmGroupSet < 0) {
$UserInCanConfirmGroupSet = &::UserInGroup("canconfirm");
}
if ($UserInCanConfirmGroup) {
if ($UserInCanConfirmGroupSet) {
return 1;
}
&::SendSQL("SELECT everconfirmed FROM bugs WHERE bug_id = $self->{'bug_id'}");
@@ -424,14 +463,18 @@ sub Collision {
my $self = shift();
my $write = "WRITE"; # Might want to make a param to control
# whether we do LOW_PRIORITY ...
&::SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " .
"cc AS selectVisible_cc $write, " .
"profiles $write, dependencies $write, votes $write, " .
"keywords $write, longdescs $write, fielddefs $write, " .
"keyworddefs READ, groups READ, attachments READ, products READ");
if ($::driver eq 'mysql') {
&::SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " .
"cc AS selectVisible_cc $write, " .
"profiles $write, dependencies $write, votes $write, " .
"keywords $write, longdescs $write, fielddefs $write, " .
"keyworddefs READ, groups READ, attachments READ, products READ");
}
&::SendSQL("SELECT delta_ts FROM bugs where bug_id=$self->{'bug_id'}");
my $delta_ts = &::FetchOneColumn();
&::SendSQL("unlock tables");
if ($::driver eq 'mysql') {
&::SendSQL("unlock tables");
}
if ($self->{'delta_ts'} ne $delta_ts) {
return 1;
}

View File

@@ -254,27 +254,20 @@ sub ValidateBugID {
# database, and that the user is authorized to access that bug.
# We detaint the number here, too
# Make sure the bug number is a positive integer.
# Whitespace can be ignored because the SQL server will ignore it.
$_[0] = trim($_[0]); # Allow whitespace arround the number
detaint_natural($_[0])
|| DisplayError("The bug number is invalid. If you are trying to use " .
|| DisplayError("The bug number is invalid. If you are trying to use " .
"QuickSearch, you need to enable JavaScript in your " .
"browser. To help us fix this limitation, look " .
"<a href=\"http://bugzilla.mozilla.org/show_bug.cgi?id=70907\">here</a>.")
&& exit;
# Only assign vars here, because we ahve to detaint the reference so that
# it passses taint checks in the caller
my ($id, $userid) = @_;
my ($id) = @_;
# Users are authorized to access bugs if they are a member of one of
# groups to which the bug is restricted.
# A user is also authorized to access a bug if she is the reporter,
# assignee, QA contact, or member of the cc: list of the bug and the bug
# allows users in those roles to see the bug. The boolean fields
# reporter_accessible, assignee_accessible, qacontact_accessible, and
# cclist_accessible identify whether or not those roles can see the bug.
# Get the values of the usergroupset and userid global variables
# and write them to local variables for use within this function,
# setting those local variables to the default value of zero if
# the global variables are undefined.
# First check that the bug exists
SendSQL("SELECT bug_id FROM bugs WHERE bug_id = $id");
@@ -283,13 +276,14 @@ sub ValidateBugID {
|| DisplayError("Bug #$id does not exist.")
&& exit;
return if CanSeeBug($id, $userid);
my $canseeref = CanSeeBug($id, $::userid, $::usergroupset);
return if $canseeref->{$id};
# The user did not pass any of the authorization tests, which means they
# are not authorized to see the bug. Display an error and stop execution.
# The error the user sees depends on whether or not they are logged in
# (i.e. $::userid contains the user's positive integer ID).
if ($userid) {
if ($::userid) {
DisplayError("You are not authorized to access bug #$id.");
} else {
DisplayError(
@@ -299,6 +293,7 @@ sub ValidateBugID {
);
}
exit;
}
sub ValidateComment {
@@ -441,14 +436,15 @@ sub PasswordForLogin {
return $result;
}
sub quietly_check_login {
my ($userid, $loginname, $ok, $disabledtext);
$userid = 0;
sub quietly_check_login() {
$::usergroupset = '0';
my $loginok = 0;
$::disabledreason = '';
$::userid = 0;
if (defined $::COOKIE{"Bugzilla_login"} &&
defined $::COOKIE{"Bugzilla_logincookie"}) {
ConnectToDatabase();
SendSQL("SELECT profiles.userid, " .
SendSQL("SELECT profiles.userid, profiles.groupset, " .
"profiles.login_name, " .
"profiles.login_name = " .
SqlQuote($::COOKIE{"Bugzilla_login"}) .
@@ -460,21 +456,21 @@ sub quietly_check_login {
" AND profiles.userid = logincookies.userid");
my @row;
if (@row = FetchSQLData()) {
($userid, $loginname, $ok, $disabledtext) = (@row);
my ($userid, $groupset, $loginname, $ok, $disabledtext) = (@row);
if ($ok) {
if ($disabledtext eq '') {
$loginok = 1;
$::userid = $userid;
$::usergroupset = $groupset;
$::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case
# is in
# canonical form.
# We've just verified that this is ok
detaint_natural($::COOKIE{"Bugzilla_logincookie"});
} else {
$userid = 0;
$::disabledreason = $disabledtext;
}
} else {
$userid = 0;
}
}
}
}
# if 'who' is passed in, verify that it's a good value
@@ -482,12 +478,13 @@ sub quietly_check_login {
my $whoid = DBname_to_id($::FORM{'who'});
delete $::FORM{'who'} unless $whoid;
}
if (!$userid) {
if (!$loginok) {
delete $::COOKIE{"Bugzilla_login"};
}
$vars->{'user'} = GetUserInfo($userid);
return $userid;
$vars->{'user'} = GetUserInfo($::userid);
return $loginok;
}
# Populate a hash with information about this user.
@@ -503,9 +500,10 @@ sub GetUserInfo {
$user{'login'} = $::COOKIE{"Bugzilla_login"};
$user{'userid'} = $userid;
SendSQL("SELECT mybugslink, realname FROM profiles " .
SendSQL("SELECT mybugslink, realname, groupset FROM profiles " .
"WHERE userid = $userid");
($user{'showmybugslink'}, $user{'realname'}) = FetchSQLData();
($user{'showmybugslink'}, $user{'realname'}, $user{'groupset'}) =
FetchSQLData();
SendSQL("SELECT name, query, linkinfooter FROM namedqueries " .
"WHERE userid = $userid");
@@ -518,11 +516,14 @@ sub GetUserInfo {
$user{'queries'} = \@queries;
SendSQL("select name from groups, user_group_map where groups.group_id = user_group_map.group_id " .
"and user_group_map.user_id = $userid");
if ($::driver eq 'mysql') {
SendSQL("select name, (bit & $user{'groupset'}) != 0 from groups");
} elsif ($::driver eq 'Pg') {
SendSQL("select name, (group_bit & int8($user{'groupset'})) != 0 from groups");
}
while (MoreSQLData()) {
my ($name) = FetchSQLData();
$groups{$name} = 1;
my ($name, $bit) = FetchSQLData();
$groups{$name} = $bit;
}
$user{'groups'} = \%groups;
@@ -764,8 +765,7 @@ sub confirm_login {
if($enteredlogin ne "") {
$::COOKIE{"Bugzilla_login"} = $enteredlogin;
SendSQL("insert into logincookies (userid,ipaddr) values (@{[DBNameToIdAndCheck($enteredlogin)]}, @{[SqlQuote($ENV{'REMOTE_ADDR'})]})");
SendSQL("select LAST_INSERT_ID()");
my $logincookie = FetchOneColumn();
my $logincookie = CurrId("logincookies_cookie_seq");
$::COOKIE{"Bugzilla_logincookie"} = $logincookie;
my $cookiepath = Param("cookiepath");
@@ -773,9 +773,9 @@ sub confirm_login {
print "Set-Cookie: Bugzilla_logincookie=$logincookie ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
}
my $userid = quietly_check_login();
my $loginok = quietly_check_login();
if (!$userid) {
if ($loginok != 1) {
if ($::disabledreason) {
my $cookiepath = Param("cookiepath");
print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT
@@ -878,8 +878,13 @@ Content-type: text/html
# crufty junk in the logincookies table. Get rid of any entry
# that hasn't been used in a month.
if ($::dbwritesallowed) {
SendSQL("DELETE FROM logincookies " .
"WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");
if ($::driver eq 'mysql') {
SendSQL("DELETE FROM logincookies " .
"WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");
} elsif ($::driver eq 'Pg') {
SendSQL("DELETE FROM logincookies " .
"WHERE NOW() - lastused > 30");
}
}
@@ -889,10 +894,10 @@ Content-type: text/html
# Update the timestamp on our logincookie, so it'll keep on working.
if ($::dbwritesallowed) {
SendSQL("UPDATE logincookies SET lastused = null " .
SendSQL("UPDATE logincookies SET lastused = NULL " .
"WHERE cookie = $::COOKIE{'Bugzilla_logincookie'}");
}
return $userid;
return $::userid;
}
sub PutHeader {
@@ -933,7 +938,7 @@ sub ThrowCodeError {
($vars->{'error'}, $vars->{'variables'}, my $unlock_tables) = (@_);
$vars->{'title'} = "Code Error";
SendSQL("UNLOCK TABLES") if $unlock_tables;
SendSQL("UNLOCK TABLES") if $unlock_tables && $::driver eq 'mysql';
# We may optionally log something to file here.
@@ -949,7 +954,7 @@ sub ThrowUserError {
($vars->{'error'}, $vars->{'title'}, my $unlock_tables) = (@_);
$vars->{'title'} ||= "Error";
SendSQL("UNLOCK TABLES") if $unlock_tables;
SendSQL("UNLOCK TABLES") if $unlock_tables && $::driver eq 'mysql';
print "Content-type: text/html\n\n" if !$vars->{'header_done'};
$template->process("global/user-error.html.tmpl", $vars)
@@ -1032,25 +1037,38 @@ sub CheckIfVotedConfirmed {
sub GetBugActivity {
my ($id, $starttime) = (@_);
my $datepart = "";
my $query = "";
die "Invalid id: $id" unless $id=~/^\s*\d+\s*$/;
if (defined $starttime) {
$datepart = "and bugs_activity.bug_when > " . SqlQuote($starttime);
}
my $query = "
SELECT IFNULL(fielddefs.description, bugs_activity.fieldid),
bugs_activity.attach_id,
bugs_activity.bug_when,
bugs_activity.removed, bugs_activity.added,
profiles.login_name
FROM bugs_activity LEFT JOIN fielddefs ON
bugs_activity.fieldid = fielddefs.fieldid,
profiles
WHERE bugs_activity.bug_id = $id $datepart
AND profiles.userid = bugs_activity.who
ORDER BY bugs_activity.bug_when";
if ($::driver eq 'mysql') {
$query = "
SELECT
IFNULL(fielddefs.name, bugs_activity.fieldid), ";
} elsif ($::driver eq 'Pg') {
$query = "
SELECT
COALESCE(fielddefs.name, chr(bugs_activity.fieldid)), ";
}
$query .= "
bugs_activity.attach_id,
bugs_activity.bug_when,
bugs_activity.removed, bugs_activity.added,
profiles.login_name
FROM
bugs_activity LEFT JOIN fielddefs ON
bugs_activity.fieldid = fielddefs.fieldid,
profiles
WHERE
bugs_activity.bug_id = $id $datepart
AND profiles.userid = bugs_activity.who
ORDER BY
bugs_activity.bug_when";
SendSQL($query);

View File

@@ -123,13 +123,13 @@ sub IssuePasswordToken {
# Generate a unique token and insert it into the tokens table.
# We have to lock the tokens table before generating the token,
# since the database must be queried for token uniqueness.
&::SendSQL("LOCK TABLES tokens WRITE");
&::SendSQL("LOCK TABLE tokens WRITE") if $::driver eq 'mysql';
my $token = GenerateUniqueToken();
my $quotedtoken = &::SqlQuote($token);
my $quotedipaddr = &::SqlQuote($::ENV{'REMOTE_ADDR'});
&::SendSQL("INSERT INTO tokens ( userid , issuedate , token , tokentype , eventdata )
VALUES ( $userid , '$issuedate' , $quotedtoken , 'password' , $quotedipaddr )");
&::SendSQL("UNLOCK TABLES");
&::SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
# Mail the user the token along with instructions for using it.
@@ -155,10 +155,13 @@ sub IssuePasswordToken {
sub CleanTokenTable {
&::SendSQL("LOCK TABLES tokens WRITE");
&::SendSQL("DELETE FROM tokens
WHERE TO_DAYS(NOW()) - TO_DAYS(issuedate) >= " . $maxtokenage);
&::SendSQL("UNLOCK TABLES");
&::SendSQL("LOCK TABLES tokens WRITE") if $::driver eq 'mysql';
if ($::driver eq 'mysql') {
&::SendSQL("DELETE FROM tokens WHERE TO_DAYS(NOW()) - TO_DAYS(issuedate) >= " . $maxtokenage);
} elsif ($::driver eq 'Pg') {
&::SendSQL("DELETE FROM tokens WHERE now() - issuedate >= '$maxtokenage days'");
}
&::SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
}
@@ -235,9 +238,9 @@ sub Cancel {
close SENDMAIL;
# Delete the token from the database.
&::SendSQL("LOCK TABLES tokens WRITE");
&::SendSQL("LOCK TABLE tokens WRITE") if $::driver eq 'mysql';
&::SendSQL("DELETE FROM tokens WHERE token = $quotedtoken");
&::SendSQL("UNLOCK TABLES");
&::SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
}
sub HasPasswordToken {

View File

@@ -42,8 +42,9 @@ require "CGI.pl";
# Establish a connection to the database backend.
ConnectToDatabase();
# Check whether or not the user is logged in and, if so, set the $userid
my $userid = quietly_check_login();
# Check whether or not the user is logged in and, if so, set the $::userid
# and $::usergroupset variables.
quietly_check_login();
################################################################################
# Main Body Execution
@@ -63,19 +64,19 @@ if ($action eq "view")
}
elsif ($action eq "viewall")
{
ValidateBugID($::FORM{'bugid'}, $userid);
ValidateBugID($::FORM{'bugid'});
viewall();
}
elsif ($action eq "enter")
{
my $userid = confirm_login();
ValidateBugID($::FORM{'bugid'}, $userid);
confirm_login();
ValidateBugID($::FORM{'bugid'});
enter();
}
elsif ($action eq "insert")
{
my $userid = confirm_login();
ValidateBugID($::FORM{'bugid'}, $userid);
confirm_login();
ValidateBugID($::FORM{'bugid'});
ValidateComment($::FORM{'comment'});
validateFilename();
validateData();
@@ -94,10 +95,7 @@ elsif ($action eq "edit")
}
elsif ($action eq "update")
{
my $userid = confirm_login();
UserInGroup($userid, "editbugs")
|| DisplayError("You are not authorized to edit attachments.")
&& exit;
confirm_login();
ValidateComment($::FORM{'comment'});
validateID();
validateCanEdit($::FORM{'id'});
@@ -136,7 +134,7 @@ sub validateID
# Make sure the user is authorized to access this attachment's bug.
my ($bugid) = FetchSQLData();
ValidateBugID($bugid, $userid);
ValidateBugID($bugid);
}
sub validateCanEdit
@@ -147,14 +145,14 @@ sub validateCanEdit
# the edit scrren to be displayed to people who aren't logged in.
# People not logged in can't actually commit changes, because that code
# calls confirm_login, not quietly_check_login, before calling this sub
return if $userid == 0;
return if $::userid == 0;
# People in editbugs can edit all attachments
return if UserInGroup($userid, "editbugs");
return if UserInGroup("editbugs");
# Bug 97729 - the submitter can edit their attachments
SendSQL("SELECT attach_id FROM attachments WHERE " .
"attach_id = $attach_id AND submitter_id = $userid");
"attach_id = $attach_id AND submitter_id = $::userid");
FetchSQLData()
|| DisplayError("You are not authorised to edit attachment #$attach_id")
@@ -430,8 +428,8 @@ sub enter
# Retrieve the attachments the user can edit from the database and write
# them into an array of hashes where each hash represents one attachment.
my $canEdit = "";
if (!UserInGroup($userid, "editbugs")) {
$canEdit = "AND submitter_id = $userid";
if (!UserInGroup("editbugs")) {
$canEdit = "AND submitter_id = $::userid";
}
SendSQL("SELECT attach_id, description
FROM attachments
@@ -477,7 +475,7 @@ sub insert
# Insert the attachment into the database.
SendSQL("INSERT INTO attachments (bug_id, filename, description, mimetype, ispatch, submitter_id, thedata)
VALUES ($::FORM{'bugid'}, $filename, $description, $contenttype, $::FORM{'ispatch'}, $userid, $thedata)");
VALUES ($::FORM{'bugid'}, $filename, $description, $contenttype, $::FORM{'ispatch'}, $::userid, $thedata)");
# Retrieve the ID of the newly created attachment record.
SendSQL("SELECT LAST_INSERT_ID()");
@@ -501,14 +499,14 @@ sub insert
foreach my $attachid (@{$::MFORM{'obsolete'}}) {
SendSQL("UPDATE attachments SET isobsolete = 1 WHERE attach_id = $attachid");
SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added)
VALUES ($::FORM{'bugid'}, $attachid, $userid, NOW(), $fieldid, '0', '1')");
VALUES ($::FORM{'bugid'}, $attachid, $::userid, NOW(), $fieldid, '0', '1')");
}
# Send mail to let people know the attachment has been created. Uses a
# special syntax of the "open" and "exec" commands to capture the output of
# "processmail", which "system" doesn't allow, without running the command
# through a shell, which backticks (``) do.
#system ("./processmail", $bugid , $userid);
#system ("./processmail", $bugid , $::userid);
#my $mailresults = `./processmail $bugid $::userid`;
my $mailresults = '';
open(PMAIL, "-|") or exec('./processmail', $::FORM{'bugid'}, $::COOKIE{'Bugzilla_login'});
@@ -607,7 +605,7 @@ sub edit
}
sub update
sub update
{
# Update an attachment record.
@@ -618,8 +616,10 @@ sub update
&& exit;
# Lock database tables in preparation for updating the attachment.
SendSQL("LOCK TABLES attachments WRITE , attachstatuses WRITE ,
attachstatusdefs READ , fielddefs READ , bugs_activity WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachments WRITE , attachstatuses WRITE ,
attachstatusdefs READ , fielddefs READ , bugs_activity WRITE");
}
# Get a copy of the attachment record before we make changes
# so we can record those changes in the activity table.
@@ -682,23 +682,23 @@ sub update
my $quotedolddescription = SqlQuote($olddescription);
my $fieldid = GetFieldID('attachments.description');
SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added)
VALUES ($bugid, $::FORM{'id'}, $userid, NOW(), $fieldid, $quotedolddescription, $quoteddescription)");
VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedolddescription, $quoteddescription)");
}
if ($oldcontenttype ne $::FORM{'contenttype'}) {
my $quotedoldcontenttype = SqlQuote($oldcontenttype);
my $fieldid = GetFieldID('attachments.mimetype');
SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added)
VALUES ($bugid, $::FORM{'id'}, $userid, NOW(), $fieldid, $quotedoldcontenttype, $quotedcontenttype)");
VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedoldcontenttype, $quotedcontenttype)");
}
if ($oldispatch ne $::FORM{'ispatch'}) {
my $fieldid = GetFieldID('attachments.ispatch');
SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added)
VALUES ($bugid, $::FORM{'id'}, $userid, NOW(), $fieldid, $oldispatch, $::FORM{'ispatch'})");
VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldispatch, $::FORM{'ispatch'})");
}
if ($oldisobsolete ne $::FORM{'isobsolete'}) {
my $fieldid = GetFieldID('attachments.isobsolete');
SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added)
VALUES ($bugid, $::FORM{'id'}, $userid, NOW(), $fieldid, $oldisobsolete, $::FORM{'isobsolete'})");
VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $oldisobsolete, $::FORM{'isobsolete'})");
}
if ($oldstatuslist ne $newstatuslist) {
my ($removed, $added) = DiffStrings($oldstatuslist, $newstatuslist);
@@ -706,11 +706,13 @@ sub update
my $quotedadded = SqlQuote($added);
my $fieldid = GetFieldID('attachstatusdefs.name');
SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when, fieldid, removed, added)
VALUES ($bugid, $::FORM{'id'}, $userid, NOW(), $fieldid, $quotedremoved, $quotedadded)");
VALUES ($bugid, $::FORM{'id'}, $::userid, NOW(), $fieldid, $quotedremoved, $quotedadded)");
}
# Unlock all database tables now that we are finished updating the database.
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
# If this installation has enabled the request manager, let the manager know
# an attachment was updated so it can check for requests on that attachment
@@ -756,7 +758,9 @@ sub update
}
# Get the user's login name since the AppendComment function needs it.
my $who = DBID_to_name($userid);
my $who = DBID_to_name($::userid);
# Mention $::userid again so Perl doesn't give me a warning about it.
my $neverused = $::userid;
# Append the comment to the list of comments in the database.
AppendComment($bugid, $who, $wrappedcomment);
@@ -767,10 +771,10 @@ sub update
# of the "open" and "exec" commands to capture the output of "processmail",
# which "system" doesn't allow, without running the command through a shell,
# which backticks (``) do.
#system ("./processmail", $bugid , $userid);
#my $mailresults = `./processmail $bugid $userid`;
#system ("./processmail", $bugid , $::userid);
#my $mailresults = `./processmail $bugid $::userid`;
my $mailresults = '';
open(PMAIL, "-|") or exec('./processmail', $bugid, DBID_to_name($userid));
open(PMAIL, "-|") or exec('./processmail', $bugid, DBID_to_name($::userid));
$mailresults .= $_ while <PMAIL>;
close(PMAIL);

View File

@@ -62,7 +62,7 @@ sub show_bug {
$vars->{'lsearch'} = \&lsearch,
$vars->{'header_done'} = (@_),
my $userid = quietly_check_login();
quietly_check_login();
my $id = $::FORM{'id'};
@@ -77,16 +77,64 @@ sub show_bug {
# Populate the bug hash with the info we get directly from the DB.
my $query = "
SELECT bugs.bug_id, product, version, rep_platform,
op_sys, bug_status, resolution, priority,
bug_severity, component, assigned_to, reporter,
bug_file_loc, short_desc, target_milestone,
qa_contact, status_whiteboard,
date_format(creation_ts,'%Y-%m-%d %H:%i'),
delta_ts, sum(votes.count)
FROM bugs LEFT JOIN votes USING(bug_id)
WHERE bugs.bug_id = $id
GROUP BY bugs.bug_id";
SELECT
bugs.bug_id,
product,
version,
rep_platform,
op_sys,
bug_status,
resolution,
priority,
bug_severity,
component,
assigned_to,
reporter,
bug_file_loc,
short_desc,
target_milestone,
qa_contact,
status_whiteboard, ";
if ($::driver eq 'mysql') {
$query .= "
date_format(creation_ts, '%Y-%m-%d %H:%i'),
groupset,
delta_ts, ";
} elsif ($::driver eq 'Pg') {
$query .= "
TO_CHAR(creation_ts, 'YYYY-MM-DD HH24:MI:SS'),
groupset,
TO_CHAR(delta_ts, 'YYYYMMDDHH24MISS'), ";
}
$query .= "
SUM(votes.count)
FROM
bugs LEFT JOIN votes USING(bug_id)
WHERE
bugs.bug_id = $id
GROUP BY
bugs.bug_id,
product,
version,
rep_platform,
op_sys,
bug_status,
resolution,
priority,
bug_severity,
component,
assigned_to,
reporter,
bug_file_loc,
short_desc,
target_milestone,
qa_contact,
status_whiteboard,
creation_ts,
groupset,
delta_ts ";
SendSQL($query);
@@ -97,7 +145,7 @@ sub show_bug {
"bug_severity", "component", "assigned_to", "reporter",
"bug_file_loc", "short_desc", "target_milestone",
"qa_contact", "status_whiteboard", "creation_ts",
"delta_ts", "votes")
"groupset", "delta_ts", "votes")
{
$value = shift(@row);
$bug{$field} = defined($value) ? $value : "";
@@ -122,7 +170,7 @@ sub show_bug {
if (Param("usebuggroupsentry")
&& GroupExists($product)
&& !UserInGroup($userid, $product))
&& !UserInGroup($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
@@ -202,49 +250,50 @@ sub show_bug {
# Groups
my @groups;
my (%buggroups, %usergroups);
# Find out if this bug is private to any group
SendSQL("SELECT group_id FROM bug_group_map WHERE bug_id = $id");
while (my $group_id = FetchOneColumn()) {
$buggroups{$group_id} = 1;
}
# Get a list of active groups the user is in, subject to the above conditions
if ($userid) {
# NB - the number of groups is likely to be small - should we just select
# everything, and weed manually? OTOH, the number of products is likely
# to be small, too. This buggroup stuff needs to be rethought
SendSQL("SELECT groups.group_id, groups.isactive " .
"FROM user_group_map, " .
"groups LEFT JOIN products ON groups.name = products.product " .
"WHERE groups.group_id = user_group_map.group_id AND " .
"user_group_map.user_id = $userid AND groups.isbuggroup != 0 AND " .
"(groups.name = " . SqlQuote($bug{'product'}) . " OR " .
"products.product IS NULL)");
while (my $group_id = FetchOneColumn()) {
$usergroups{$group_id} = 1;
if ($::usergroupset ne '0' || $bug{'groupset'} ne '0') {
my $bug_groupset = $bug{'groupset'};
if ($::driver eq 'mysql') {
SendSQL("select bit, name, description, (bit & $bug{'groupset'} != 0), " .
"(bit & $::usergroupset != 0) from groups where isbuggroup != 0 " .
# Include active groups as well as inactive groups to which
# the bug already belongs. This way the bug can be removed
# from an inactive group but can only be added to active ones.
"and ((isactive = 1 or (bit & $bug{'groupset'} != 0)) or " .
"(bit & $bug{'groupset'} != 0)) " .
"order by description");
} elsif ($::driver eq 'Pg') {
SendSQL("select group_bit, name, description, (group_bit & int8($bug{'groupset'}) != 0), " .
"(group_bit & int8($::usergroupset) != 0) from groups where isbuggroup != 0 " .
# Include active groups as well as inactive groups to which
# the bug already belongs. This way the bug can be removed
# from an inactive group but can only be added to active ones.
"and ((isactive = 1 or (group_bit & int8($bug{'groupset'}) != 0)) or " .
"(group_bit & int8($bug{'groupset'}) != 0)) " .
"order by description");
}
# Now get information about each group
SendSQL("SELECT group_id, name, description " .
"FROM groups " .
# "WHERE group_id IN (" . join(',', @groups) . ") " .
"ORDER BY description");
$user{'inallgroups'} = 1;
while (MoreSQLData()) {
my ($group_id, $name, $description) = FetchSQLData();
my ($ison, $ingroup);
if ($buggroups{$group_id} ||
($usergroups{$group_id} && (($name eq $bug{'product'}) ||
(!defined $::proddesc{$name}))))
my ($bit, $name, $description, $ison, $ingroup) = FetchSQLData();
# 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 ||
($ingroup && (($name eq $bug{'product'}) ||
(!defined $::proddesc{$name}))))
{
$user{'inallgroups'} &= $ingroup;
push (@groups, { "bit" => $group_id,
"ison" => $buggroups{$group_id},
"ingroup" => $usergroups{$group_id},
"description" => $description });
push (@groups, { "bit" => $bit,
"ison" => $ison,
"ingroup" => $ingroup,
"description" => $description });
}
}
@@ -252,7 +301,7 @@ sub show_bug {
# the user to set whether or not the reporter
# and cc list can see the bug even if they are not members of all
# groups to which the bug is restricted.
if (%buggroups) {
if ($bug{'groupset'} != 0) {
$bug{'inagroup'} = 1;
# Determine whether or not the bug is always accessible by the
@@ -274,18 +323,18 @@ sub show_bug {
# User permissions
# In the below, if the person hasn't logged in ($userid == 0), then
# In the below, if the person hasn't logged in ($::userid == 0), then
# we treat them as if they can do anything. That's because we don't
# know why they haven't logged in; it may just be because they don't
# use cookies. Display everything as if they have all the permissions
# in the world; their permissions will get checked when they log in
# and actually try to make the change.
$user{'canedit'} = $userid == 0
|| $userid == $bug{'reporter'}
|| $userid == $bug{'qa_contact'}
|| $userid == $bug{'assigned_to'}
$user{'canedit'} = $::userid == 0
|| $::userid == $bug{'reporter'}
|| $::userid == $bug{'qa_contact'}
|| $::userid == $bug{'assigned_to'}
|| UserInGroup("editbugs");
$user{'canconfirm'} = ($userid == 0) || UserInGroup($userid, "canconfirm");
$user{'canconfirm'} = ($::userid == 0) || UserInGroup("canconfirm");
# Bug states
$bug{'isunconfirmed'} = ($bug{'bug_status'} eq $::unconfirmedstate);

View File

@@ -56,6 +56,7 @@ sub sillyness {
$zz = @::settable_resolution;
$zz = @::target_milestone;
$zz = $::unconfirmedstate;
$zz = $::userid;
$zz = @::versions;
};
@@ -136,10 +137,9 @@ if ($::FORM{'cmdtype'} eq 'runnamed') {
$filename =~ s/\s//;
}
my $userid = 0;
if ($dotweak) {
$userid = confirm_login();
if (!UserInGroup($userid, "editbugs")) {
confirm_login();
if (!UserInGroup("editbugs")) {
DisplayError("Sorry, you do not have sufficient privileges to edit
multiple bugs.");
exit;
@@ -147,7 +147,7 @@ if ($dotweak) {
GetVersionTable();
}
else {
$userid = quietly_check_login();
quietly_check_login();
}
@@ -195,7 +195,7 @@ sub GetByWordList {
$word =~ s/^'//;
$word =~ s/'$//;
$word = '(^|[^a-z0-9])' . $word . '($|[^a-z0-9])';
push(@list, "lower($field) regexp '$word'");
push(@list, SqlRegEx($field, SqlQuote($word)));
}
}
@@ -211,7 +211,7 @@ sub GetByWordListSubstr {
foreach my $word (split(/[\s,]+/, $strs)) {
if ($word ne "") {
push(@list, "INSTR(LOWER($field), " . lc(SqlQuote($word)) . ")");
push(@list, SqlStrSearch($field, SqlQuote($word), "lower"));
}
}
@@ -251,17 +251,25 @@ sub GetQuip {
}
sub GetGroupsByGroupSet {
my ($userid) = @_;
my ($groupset) = @_;
return if !$userid;
return if !$groupset;
SendSQL("
SELECT groups.group_id, groups.name, groups.description, groups.isactive
if ($::driver eq 'mysql') {
SendSQL("
SELECT bit, name, description, isactive
FROM groups
LEFT JOIN user_group_map ON groups.group_id = user_group_map.group_id
WHERE groups.isbuggroup != 0
AND user_group_map.user_id = $userid
ORDER BY description ");
WHERE (bit & $groupset) != 0
AND isbuggroup != 0
ORDER BY description ");
} elsif ($::driver eq 'Pg') {
SendSQL("
SELECT group_bit, name, description, isactive
FROM groups
WHERE (group_bit & int8($groupset)) != 0
AND isbuggroup != 0
ORDER BY description ");
}
my @groups;
@@ -283,25 +291,30 @@ sub GetGroupsByGroupSet {
sub GenerateSQL {
my $debug = 0;
my ($fieldsref, $urlstr) = (@_);
my ($fieldsref, $urlstr, $groupbyref) = (@_);
my @fields;
my @groupbylist;
my @supptables;
my @wherepart;
@fields = @$fieldsref if $fieldsref;
@groupbylist = @$groupbyref if $groupbyref;
my %F;
my %M;
ParseUrlString($urlstr, \%F, \%M);
my @specialchart;
my @andlist;
my $userid = quietly_check_login();
# First, deal with all the old hard-coded non-chart-based poop.
# unshift(@supptables,
# ("profiles map_assigned_to",
# "profiles map_reporter",
# "LEFT JOIN profiles map_qa_contact ON bugs.qa_contact = map_qa_contact.userid"));
unshift(@supptables,
("profiles map_assigned_to",
"profiles map_reporter",
"LEFT JOIN profiles map_qa_contact ON bugs.qa_contact = map_qa_contact.userid"));
"profiles map_reporter"));
unshift(@wherepart,
("bugs.assigned_to = map_assigned_to.userid",
"bugs.reporter = map_reporter.userid"));
@@ -341,7 +354,7 @@ sub GenerateSQL {
my @legal_fields = ("product", "version", "rep_platform", "op_sys",
"bug_status", "resolution", "priority", "bug_severity",
"assigned_to", "reporter", "component",
"target_milestone");
"target_milestone", "groupset");
foreach my $field (keys %F) {
if (lsearch(\@legal_fields, $field) != -1) {
@@ -703,19 +716,19 @@ sub GenerateSQL {
$term = "$ff != $q";
},
",casesubstring" => sub {
$term = "INSTR($ff, $q)";
$term = SqlStrSearch($ff, $q);
},
",(substring|substr)" => sub {
$term = "INSTR(LOWER($ff), " . lc($q) . ")";
$term = SqlStrSearch($ff, $q, "lower", "not");
},
",notsubstring" => sub {
$term = "INSTR(LOWER($ff), " . lc($q) . ") = 0";
$term = SqlStrSearch($ff, $q, "lower");
},
",regexp" => sub {
$term = "LOWER($ff) REGEXP $q";
$term = SqlRegEx($ff, $q);
},
",notregexp" => sub {
$term = "LOWER($ff) NOT REGEXP $q";
",notregexp" => sub {
$term = SqlRegEx($ff, $q);
},
",lessthan" => sub {
$term = "$ff < $q";
@@ -1005,6 +1018,7 @@ sub GenerateSQL {
}
}
}
my %suppseen = ("bugs" => 1);
my $suppstring = "bugs";
foreach my $str (@supptables) {
@@ -1018,7 +1032,10 @@ sub GenerateSQL {
}
my $query = ("SELECT DISTINCT " . join(', ', @fields) .
" FROM $suppstring" .
" WHERE " . join(' AND ', (@wherepart, @andlist)));
" WHERE " . join(' AND ', (@wherepart, @andlist)) .
" GROUP BY " . join(", ", @groupbylist));
# $query = SelectVisible($query, $::userid, $::usergroupset);
if ($debug) {
print "<P><CODE>" . value_quote($query) . "</CODE><P>\n";
@@ -1078,8 +1095,21 @@ CMD: for ($::FORM{'cmdtype'}) {
my $userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"});
my $qname = SqlQuote($::defaultqueryname);
my $qbuffer = SqlQuote($::buffer);
SendSQL("REPLACE INTO namedqueries (userid, name, query)
VALUES ($userid, $qname, $qbuffer)");
if ($::driver eq 'mysql') {
SendSQL("REPLACE INTO namedqueries (userid, name, query)" .
"VALUES ($userid, $qname, $qbuffer)");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT userid FROM namedqueries WHERE userid = $userid " .
"AND name = $qname");
my $result = FetchOneColumn();
if ( $result ) {
SendSQL("UPDATE namedqueries SET query = $qbuffer " .
"WHERE userid = $userid AND name = $qname");
} else {
SendSQL("INSERT INTO namedqueries (userid, name, query, watchfordiffs, linkinfooter) VALUES " .
"($userid, $qname, $qbuffer, '', '')");
}
}
print "Content-Type: text/html\n\n";
# Generate and return the UI (HTML page) from the appropriate template.
$vars->{'title'} = "OK, default is set";
@@ -1118,7 +1148,7 @@ CMD: for ($::FORM{'cmdtype'}) {
WHERE userid = $userid AND name = $qname");
}
else {
SendSQL("REPLACE INTO namedqueries (userid, name, query, linkinfooter)
SendSQL("INSERT INTO namedqueries (userid, name, query, linkinfooter)
VALUES ($userid, $qname, $qbuffer, $tofooter)");
}
@@ -1179,8 +1209,18 @@ sub DefineColumn {
# Column: ID Name Title
DefineColumn("id" , "bugs.bug_id" , "ID" );
DefineColumn("opendate" , "bugs.creation_ts" , "Opened" );
DefineColumn("changeddate" , "bugs.delta_ts" , "Changed" );
DefineColumn("groupset" , "bugs.groupset" , "Groupset" );
if ($::driver eq 'mysql') {
DefineColumn("opendate", "unix_timestamp(bugs.creation_ts)", "Opened",
"bugs.creation_ts");
DefineColumn("changeddate", "unix_timestamp(bugs.delta_ts)", "Changed",
"bugs.delta_ts");
} elsif ($::driver eq 'Pg') {
DefineColumn("opendate", "bugs.creation_ts", "Opened",
"bugs.creation_ts");
DefineColumn("changeddate", "bugs.delta_ts", "Changed",
"bugs.delta_ts");
}
DefineColumn("severity" , "bugs.bug_severity" , "Severity" );
DefineColumn("priority" , "bugs.priority" , "Priority" );
DefineColumn("platform" , "bugs.rep_platform" , "Platform" );
@@ -1236,6 +1276,9 @@ else {
# and are hard-coded into the display templates.
@displaycolumns = grep($_ ne 'id', @displaycolumns);
# IMPORTANT! Never allow the groupset column to be displayed!
@displaycolumns = grep($_ ne 'groupset', @displaycolumns);
# Add the votes column to the list of columns to be displayed
# in the bug list if the user is searching for bugs with a certain
# number of votes and the votes column is not already on the list.
@@ -1247,6 +1290,9 @@ if (trim($::FORM{'votes'}) && !grep($_ eq 'votes', @displaycolumns)) {
push(@displaycolumns, 'votes');
}
################################################################################
# Select Column Determination
################################################################################
################################################################################
# Select Column Determination
@@ -1254,18 +1300,24 @@ if (trim($::FORM{'votes'}) && !grep($_ eq 'votes', @displaycolumns)) {
# Generate the list of columns that will be selected in the SQL query.
# The bug IDs are always selected because bug IDs are always displayed
my @selectcolumns = ("id");
# The bug ID and groupset are always selected because bug IDs are always
# displayed and we need the groupset to determine whether or not the bug
# is visible to the user.
my @selectcolumns = ("id", "groupset");
my @groupbylist = ("id", "groupset");
# Display columns are selected because otherwise we could not display them.
push (@selectcolumns, @displaycolumns);
push (@groupbylist, @displaycolumns);
# If the user is editing multiple bugs, we also make sure to select the product
# and status because the values of those fields determine what options the user
# has for modifying the bugs.
if ($dotweak) {
push(@selectcolumns, "product") if !grep($_ eq 'product', @selectcolumns);
push(@groupbylist, "product") if !grep($_ eq 'product', @groupbylist);
push(@selectcolumns, "status") if !grep($_ eq 'status', @selectcolumns);
push(@groupbylist, "status") if !grep($_ eq 'product', @groupbylist);
}
@@ -1275,9 +1327,10 @@ if ($dotweak) {
# Convert the list of columns being selected into a list of column names.
my @selectnames = map($columns->{$_}->{'name'}, @selectcolumns);
my @groupbynames = map($columns->{$_}->{'name'}, @groupbylist);
# Generate the basic SQL query that will be used to generate the bug list.
my $query = GenerateSQL(\@selectnames, $::buffer);
my $query = GenerateSQL(\@selectnames, $::buffer, \@groupbynames);
################################################################################
@@ -1383,7 +1436,7 @@ ReconnectToShadowDatabase();
# Tell MySQL to store temporary tables on the hard drive instead of memory
# to avoid "table out of space" errors on MySQL versions less than 3.23.2.
SendSQL("SET OPTION SQL_BIG_TABLES=1") if Param('expectbigqueries');
SendSQL("SET OPTION SQL_BIG_TABLES=1") if Param('expectbigqueries') && $::driver eq 'mysql';
# Normally, we ignore SIGTERM and SIGPIPE (see globals.pl) but we need to
# respond to them here to prevent someone DOSing us by reloading a query
@@ -1405,8 +1458,6 @@ SendSQL($query);
my $bugowners = {};
my $bugproducts = {};
my $bugstatuses = {};
my @buglist = ();
my @canseebugs = ();
my @bugs; # the list of records
@@ -1431,24 +1482,26 @@ while (my @row = FetchSQLData()) {
$bugproducts->{$bug->{'product'}} = 1 if $bug->{'product'};
$bugstatuses->{$bug->{'status'}} = 1 if $bug->{'status'};
# Keep list of bugs so we can check them later for permission
push(@buglist, $bug->{id});
# Add the record to the list.
push(@bugs, $bug);
}
# Check to see which bugs we have permission to see
my $canseeref = CanSeeBug(\@buglist, $userid);
# Fix the list of bugs depending on which ones we are allowed to see
my @buglist = ();
my @canseebugs = ();
foreach my $bug (@bugs) {
# next if !$canseeref->{$bug->{id}};
push(@canseebugs, $bug);
push(@buglist, $bug->{id});
}
my $canseeref = CanSeeBug(\@buglist, $::userid, $::usergroupset);
foreach my $bug (@bugs) {
next if !$canseeref->{$bug->{id}};
push (@canseebugs, $bug);
}
# Switch back from the shadow database to the regular database so PutFooter()
# can determine the current user even if the "logincookies" table is corrupted
# in the shadow database.
SendSQL("USE $::db_name");
SendSQL("USE $::db_name") if $::driver eq 'mysql';
################################################################################
# Template Variable Definition
@@ -1456,8 +1509,8 @@ SendSQL("USE $::db_name");
# Define the variables and functions that will be passed to the UI template.
$vars->{'bugs'} = \@bugs;
$vars->{'buglist'} = join(',', map($_->{id}, @bugs));
$vars->{'bugs'} = \@canseebugs;
$vars->{'buglist'} = join(',', map($_->{id}, @canseebugs));
$vars->{'columns'} = $columns;
$vars->{'displaycolumns'} = \@displaycolumns;
@@ -1479,7 +1532,7 @@ $vars->{'order'} = $order;
# The user's login account name (i.e. email address).
my $login = $::COOKIE{'Bugzilla_login'};
$vars->{'caneditbugs'} = UserInGroup($userid, 'editbugs');
$vars->{'caneditbugs'} = UserInGroup('editbugs');
$vars->{'usebuggroups'} = Param('usebuggroups');
# Whether or not this user is authorized to move bugs to another installation.
@@ -1489,7 +1542,7 @@ $vars->{'ismover'} = 1
&& Param('movers') =~ /^(\Q$login\E[,\s])|([,\s]\Q$login\E[,\s]+)/;
my @bugowners = keys %$bugowners;
if (scalar(@bugowners) > 1 && UserInGroup($userid, 'editbugs')) {
if (scalar(@bugowners) > 1 && UserInGroup('editbugs')) {
my $suffix = Param('emailsuffix');
map(s/$/$suffix/, @bugowners) if $suffix;
my $bugowners = join(",", @bugowners);
@@ -1527,7 +1580,7 @@ if ($dotweak) {
$vars->{'bugstatuses'} = [ keys %$bugstatuses ];
# The groups to which the user belongs.
$vars->{'groups'} = GetGroupsByGroupSet($userid) if $userid ne '0';
$vars->{'groups'} = GetGroupsByGroupSet($::usergroupset) if $::usergroupset ne '0';
# If all bugs being changed are in the same product, the user can change
# their version and component, so generate a list of products, a list of
@@ -1541,6 +1594,11 @@ if ($dotweak) {
$vars->{'targetmilestones'} = $::target_milestone{$product} if Param('usetargetmilestone');
}
}
else {
print "Content-Type: $format->{'contenttype'}\n";
}
print "\n"; # end HTTP headers
################################################################################
# HTTP Header Generation
@@ -1564,7 +1622,7 @@ if ($format->{'extension'} eq "html") {
my $qorder = url_quote($order);
print "Set-Cookie: LASTORDER=$qorder ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
}
my $bugids = join(":", map( $_->{'id'}, @bugs));
my $bugids = join(":", map( $_->{'id'}, @canseebugs));
# See also Bug 111999
if (length($bugids) < 4000) {
print "Set-Cookie: BUGLIST=$bugids ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";

View File

@@ -1262,6 +1262,7 @@ $table{attachstatusdefs} =
#
$table{bugs} =
'bug_id mediumint not null auto_increment primary key,
groupset bigint not null,
assigned_to mediumint not null, # This is a comment.
bug_file_loc text,
bug_severity enum($my_severities) not null,
@@ -1348,6 +1349,13 @@ $table{dependencies} =
index(dependson)';
# Group bits must be a power of two. Groups are identified by a bit; sets of
# groups are indicated by or-ing these values together.
#
# isbuggroup is nonzero if this is a group that controls access to a set
# of bugs. In otherword, the groupset field in the bugs table should only
# have this group's bit set if isbuggroup is nonzero.
#
# User regexp is which email addresses are initially put into this group.
# This is only used when an email account is created; otherwise, profiles
# may be individually tweaked to add them in and out of groups.
@@ -1360,14 +1368,14 @@ $table{dependencies} =
# http://bugzilla.mozilla.org/show_bug.cgi?id=75482
$table{groups} =
'group_id mediumint not null auto_increment primary key,
'bit bigint not null,
name varchar(255) not null,
description text not null,
isbuggroup tinyint not null,
userregexp tinytext not null,
isactive tinyint not null default 1,
unique(group_id),
unique(bit),
unique(name)';
$table{logincookies} =
@@ -1380,8 +1388,7 @@ $table{logincookies} =
$table{products} =
'product_id mediumint primary key auto_increment not null,
product varchar(64),
'product varchar(64),
description mediumtext,
milestoneurl tinytext not null,
disallownew tinyint not null,
@@ -1397,10 +1404,12 @@ $table{profiles} =
login_name varchar(255) not null,
cryptpassword varchar(34),
realname varchar(255),
groupset bigint not null,
disabledtext mediumtext not null,
mybugslink tinyint not null default 1,
blessgroupset bigint not null default 0,
emailflags mediumtext,
admin tinyint not null default 0,
unique(login_name)';
@@ -1506,39 +1515,7 @@ $table{tokens} =
index(userid)';
# 2001-09-18, dkl@redhat.com
# Group tables for tracking group memberships, admin memberships,
# product permissions, and bug permissions.
#
# This table determines the groups that a user belongs to
# and the level that they can bless others to.
# canbless:
# 0 = Cannot bless others into the group
# 1 = Can bless others into the group
# 2 = Can give others permission to bless people into the group
$table{user_group_map} =
'user_id mediumint not null,
group_id mediumint not null,
canbless smallint default 0,
unique(user_id, group_id),
index(group_id)';
# This table determines which groups have permission to see a bug
$table{bug_group_map} =
'bug_id mediumint not null,
group_id mediumint not null,
unique(bug_id, group_id),
index(group_id)';
# This table determines which groups may report bugs against a product
$table{product_group_map} =
'product_id mediumint not null,
group_id mediumint not null,
unique(product_id, group_id),
index(group_id)';
###########################################################################
# Create tables
@@ -1580,24 +1557,6 @@ while (my ($tabname, $fielddef) = each %table) {
# Populate groups table
###########################################################################
# We need to add a couple of columns first if this is our first time
# using the new group schema and populate the group ids
# 2002/01/23 dkl@redhat.com
if (&GetFieldDef('groups', 'bit')) {
&AddField('groups', 'group_id', 'mediumint primary key auto_increment not null');
&AddField('profiles', 'admin', 'smallint default 0');
my $currentgroupid = 1;
my $query = "select bit from groups order by bit";
my $sth = $dbh->prepare($query);
$sth->execute();
while (my ($bit) = $sth->fetchrow_array()) {
my $query = "update groups set group_id = $currentgroupid where bit = $bit";
my $sth2 = $dbh->prepare($query);
$sth2->execute();
$currentgroupid++;
}
}
sub GroupDoesExist ($)
{
my ($name) = @_;
@@ -1612,7 +1571,7 @@ sub GroupDoesExist ($)
#
# This subroutine checks if a group exist. If not, it will be automatically
# created with the next available groupid
# created with the next available bit set
#
sub AddGroup {
@@ -1621,17 +1580,26 @@ sub AddGroup {
return if GroupDoesExist($name);
# get highest bit number
my $sth = $dbh->prepare("SELECT bit FROM groups ORDER BY bit DESC");
$sth->execute;
my @row = $sth->fetchrow_array;
# normalize bits
my $bit;
if (defined $row[0]) {
$bit = $row[0] << 1;
} else {
$bit = 1;
}
print "Adding group $name ...\n";
my $sth = $dbh->prepare('INSERT INTO groups
(name, description, userregexp, isbuggroup)
VALUES (?, ?, ?, ?)');
$sth->execute($name, $desc, $userregexp, 0);
$sth = $dbh->prepare("select last_insert_id()");
$sth->execute();
my ($last) = $sth->fetchrow_array();
return $last;
$sth = $dbh->prepare('INSERT INTO groups
(bit, name, description, userregexp, isbuggroup)
VALUES (?, ?, ?, ?, ?)');
$sth->execute($bit, $name, $desc, $userregexp, 0);
return $bit;
}
@@ -1645,27 +1613,24 @@ AddGroup 'creategroups', 'Can create and destroy groups.';
AddGroup 'editcomponents', 'Can create, destroy, and edit components.';
AddGroup 'editkeywords', 'Can create, destroy, and edit keywords.';
# Add the groupset field here because this code is run before the
# code that updates the database structure.
&AddField('profiles', 'groupset', 'bigint not null');
if (!GroupDoesExist("editbugs")) {
my $id = AddGroup('editbugs', 'Can edit all aspects of any bug.', ".*");
my $sth = $dbh->prepare("SELECT userid FROM profiles ORDER BY userid");
$sth->execute();
while ( my ($userid) = $sth->fetchrow_array() ) {
$dbh->do("INSERT INTO user_group_map VALUES ($userid, $id)");
}
$dbh->do("UPDATE profiles SET groupset = groupset | $id");
}
if (!GroupDoesExist("canconfirm")) {
my $id = AddGroup('canconfirm', 'Can confirm a bug.', ".*");
my $sth = $dbh->prepare("SELECT userid FROM profiles ORDER BY userid");
$sth->execute();
while ( my ($userid) = $sth->fetchrow_array() ) {
$dbh->do("INSERT INTO user_group_map VALUES ($userid, $id)");
}
$dbh->do("UPDATE profiles SET groupset = groupset | $id");
}
###########################################################################
# Populate the list of fields.
###########################################################################
@@ -1848,25 +1813,17 @@ CheckEnumField('bugs', 'rep_platform', @my_platforms);
# Prompt the user for the email address and name of an administrator. Create
# that login, if it doesn't exist already, and make it a member of all groups.
my @groups = ();
my $sth = $dbh->prepare("select group_id from groups");
$sth->execute();
while ( my @row = $sth->fetchrow_array() ) {
push (@groups, $row[0]);
}
sub bailout { # this is just in case we get interrupted while getting passwd
system("stty","echo"); # re-enable input echoing
exit 1;
}
$sth = $dbh->prepare(<<_End_Of_SQL_);
SELECT login_name
my $sth = $dbh->prepare(<<_End_Of_SQL_);
SELECT login_name
FROM profiles
WHERE admin = 1
WHERE groupset=9223372036854775807
_End_Of_SQL_
$sth->execute;
# when we have no admin users, prompt for admin email address and password ...
if ($sth->rows == 0) {
my $login = "";
@@ -1999,52 +1956,15 @@ _End_Of_SQL_
$dbh->do(<<_End_Of_SQL_);
INSERT INTO profiles
(login_name, realname, cryptpassword, admin)
VALUES ($login, $realname, $cryptedpassword, 1)
(login_name, realname, cryptpassword, groupset)
VALUES ($login, $realname, $cryptedpassword, 0x7fffffffffffffff)
_End_Of_SQL_
# Put the admin in each group if not already
my $query = "select userid from profiles where login_name = $login";
$sth = $dbh->prepare($query);
$sth->execute();
my ($userid) = $sth->fetchrow_array();
foreach my $group ( @groups ) {
my $query = "SELECT user_id FROM user_group_map WHERE group_id = $group AND user_id = $userid";
$sth = $dbh->prepare($query);
$sth->execute();
if ( !$sth->fetchrow_array() ) {
$dbh->do("INSERT INTO user_group_map VALUES ($userid, $group, 1)");
} else {
$dbh->do("UPDATE user_group_map SET canbless = 1 WHERE user_id = $userid AND group_id = $group");
}
}
} else {
$dbh->do(<<_End_Of_SQL_);
UPDATE profiles
SET admin=1
SET groupset=0x7fffffffffffffff
WHERE login_name=$login
_End_Of_SQL_
# Put the admin in each group if not already
my $query = "SELECT userid FROM profiles WHERE login_name = $login";
$sth = $dbh->prepare($query);
$sth->execute();
my ($userid) = $sth->fetchrow_array();
foreach my $group ( @groups ) {
my $query = "SELECT user_id FROM user_group_map WHERE group_id = $group AND user_id = $userid";
$sth = $dbh->prepare($query);
$sth->execute();
if ( !$sth->fetchrow_array() ) {
$dbh->do("INSERT INTO user_group_map VALUES ($userid, $group, 1)");
} else {
$dbh->do("UPDATE user_group_map SET canbless = 1 WHERE user_id = $userid AND group_id = $group");
}
}
}
print "\n$login is now set up as the administrator account.\n";
}
@@ -2059,7 +1979,7 @@ _End_Of_SQL_
$sth = $dbh->prepare(<<_End_Of_SQL_);
SELECT userid
FROM profiles
WHERE admin=1
WHERE groupset=9223372036854775807
_End_Of_SQL_
$sth->execute;
my ($adminuid) = $sth->fetchrow_array;
@@ -2186,6 +2106,7 @@ sub TableExists ($)
# but aren't in very old bugzilla's (like 2.1)
# Steve Stock (sstock@iconnect-inc.com)
AddField('bugs', 'target_milestone', 'varchar(20) not null default "---"');
AddField('bugs', 'groupset', 'bigint not null');
AddField('bugs', 'qa_contact', 'mediumint not null');
AddField('bugs', 'status_whiteboard', 'mediumtext not null');
AddField('products', 'disallownew', 'tinyint not null');
@@ -2483,7 +2404,7 @@ if (!GetFieldDef('bugs', 'lastdiffed')) {
# in my database. This code detects that, cleans up the duplicates, and
# then tweaks the table to declare the field to be unique. What a pain.
if (GetIndexDef('profiles', 'login_name')) {
if (GetIndexDef('profiles', 'login_name')->[1]) {
print "Searching for duplicate entries in the profiles table ...\n";
while (1) {
# This code is weird in that it loops around and keeps doing this
@@ -2616,6 +2537,7 @@ if (!GetFieldDef('bugs', 'everconfirmed')) {
}
AddField('products', 'maxvotesperbug', 'smallint not null default 10000');
AddField('products', 'votestoconfirm', 'smallint not null');
AddField('profiles', 'blessgroupset', 'bigint not null');
# 2000-03-21 Adding a table for target milestones to
# database - matthew@zeroknowledge.com
@@ -2943,116 +2865,7 @@ AddField("bugs", "cclist_accessible", "tinyint not null default 1");
# using the attachment manager can record changes to attachments.
AddField("bugs_activity", "attach_id", "mediumint null");
# 2002-01-20 dkl@redhat.com 68022
# Drop bit, groupset, blessgroupset fields from certain tables after converting
# all users and bugs to new group schema
if (GetFieldDef('groups', 'bit')) {
print "Converting bug database to new group schema format...\n";
my $superusergroupset = '9223372036854775807';
my %bit_groups = ();
my $sth;
my $sth2;
my $sth3;
$sth = $dbh->prepare("select bit, group_id from groups order by bit");
$sth->execute();
while (my ($bit, $groupid) = $sth->fetchrow_array()) {
$bit_groups{$bit} = $groupid;
}
$sth->finish;
print "Populating user_group_map and bless_group_map table from users in profiles...\n";
foreach my $bit (sort keys %bit_groups) {
# Fix profiles table first
$sth = $dbh->prepare("select userid from profiles where (groupset & $bit) != 0 order by userid");
$sth->execute();
while (my ($userid) = $sth->fetchrow_array()) {
$sth2 = $dbh->prepare("select user_id from user_group_map where " .
"user_id = $userid and group_id = $bit_groups{$bit}");
$sth2->execute();
my ($result) = $sth2->fetchrow_array();
if (!$result) {
$sth2 = $dbh->prepare("insert into user_group_map values ($userid, $bit_groups{$bit}, 0)");
$sth2->execute();
}
$sth2->finish;
}
# Next fix bless group privileges
$sth = $dbh->prepare("select userid from profiles where blessgroupset & $bit != 0 order by userid");
$sth->execute();
while (my ($userid) = $sth->fetchrow_array()) {
$sth2 = $dbh->prepare("select user_id from user_group_map " .
"where user_id = $userid and group_id = $bit_groups{$bit}");
$sth2->execute();
my ($result) = $sth->fetchrow_array();
if (!$result) {
$sth2->prepare("update user_group_map set canbless = 1 where user_id = $userid " .
"and group_id = $bit_groups{$bit}");
$sth2->execute();
} else {
$sth2 = $dbh->prepare("insert into user_group_map values ($userid, $bit_groups{$bit}, 1)");
$sth2->execute();
}
$sth2->finish;
}
$sth->finish;
}
# Fix super users by adding 1 to admin column
# We shouldn't need to add them to any groups since that would have been done earlier.
print "Populating profile's admin column for super users...\n";
$sth = $dbh->prepare("select userid from profiles where groupset = $superusergroupset order by userid");
$sth->execute();
while (my ($userid) = $sth->fetchrow_array()) {
my $sth2 = $dbh->prepare("update profiles set admin = 1 where userid = $userid");
$sth2->execute();
$sth2->finish;
}
$sth->finish;
# Fix bug groupsets
print "Populating bug_group_map table with bugs that are marked private...\n";
foreach my $bit (sort keys %bit_groups) {
$sth = $dbh->prepare("select bug_id from bugs where (groupset & $bit) != 0 order by bug_id");
$sth->execute();
while (my ($id) = $sth->fetchrow_array()) {
$sth2 = $dbh->prepare("select bug_id from bug_group_map " .
"where bug_id = $id and group_id = $bit_groups{$bit}");
$sth2->execute();
my ($result) = $sth->fetchrow_array();
if (!$result) {
$sth2 = $dbh->prepare("insert into bug_group_map values ($id, $bit_groups{$bit})");
$sth2->execute();
}
$sth2->finish;
}
$sth->finish;
}
DropField('bugs', 'groupset');
DropField('profiles', 'groupset');
DropField('profiles', 'blessgroupset');
DropField('groups', 'bit');
}
# Added product_group_map table for product privacy
# Adding product_id column to products table to allow this to work
# 2002/01/24 dkl@redhat.com
if (!GetFieldDef('products', 'product_id')) {
AddField('products', 'product_id', 'mediumint primary key auto_increment not null');
my $sth = $dbh->prepare('select product from products');
$sth->execute();
my $currproductid = 1;
while (my ($name) = $sth->fetchrow_array()) {
my $sth2 = $dbh->prepare("update products set product_id = $currproductid where product = " .
$dbh->quote($name));
$sth2->execute();
$sth2->finish;
}
$sth->finish;
}
# 2001-01-17 bbaetz@student.usyd.edu.au bug 95732
# 2002-02-04 bbaetz@student.usyd.edu.au bug 95732
# Remove logincookies.cryptpassword, and delete entries which become
# invalid
if (GetFieldDef("logincookies", "cryptpassword")) {

View File

@@ -34,38 +34,6 @@ use vars qw(
require "CGI.pl";
# Use the template toolkit (http://www.template-toolkit.org/) to generate
# the user interface (HTML pages and mail messages) using templates in the
# "template/" subdirectory.
use Template;
# Create the global template object that processes templates and specify
# configuration parameters that apply to all templates processed in this script.
my $template = Template->new(
{
# Colon-separated list of directories containing templates.
INCLUDE_PATH => "template/custom:template/default",
# Allow templates to be specified with relative paths.
RELATIVE => 1,
PRE_CHOMP => 1,
});
# Define the global variables and functions that will be passed to the UI
# template. Individual functions add their own values to this hash before
# sending them to the templates they process.
my $vars =
{
# Function for retrieving global parameters.
'Param' => \&Param,
# Function for processing global parameters that contain references
# to other global parameters.
'PerformSubsts' => \&PerformSubsts,
# Function to search an array for a value
'lsearch' => \&lsearch,
};
print "Content-type: text/html\n";
# The master list not only says what fields are possible, but what order

View File

@@ -30,12 +30,13 @@ use strict;
use lib qw(.);
require "CGI.pl";
require "globals.pl";
# Shut up misguided -w warnings about "used only once":
use vars qw(
%FORM
$template
$vars
%FORM
$template
$vars
);
ConnectToDatabase();

View File

@@ -36,15 +36,21 @@ require "CGI.pl";
ConnectToDatabase();
GetVersionTable();
my $userid = quietly_check_login();
if (!defined $::FORM{'product'}) {
# Reference to a subset of %::proddesc, which the user is allowed to see
my %products;
foreach my $p (@::legal_product) {
next if !CanSeeProduct($userid, $p);
$products{$p} = $::proddesc{$p};
if (Param("usebuggroups")) {
# OK, now only add products the user can see
confirm_login();
foreach my $p (@::legal_product) {
if (!GroupExists($p) || UserInGroup($p)) {
$products{$p} = $::proddesc{$p};
}
}
}
else {
%products = %::proddesc;
}
my $prodsize = scalar(keys %products);
@@ -71,21 +77,6 @@ if (!defined $::FORM{'product'}) {
my $product = $::FORM{'product'};
# Make sure the user specified a valid product name. Note that
# if the user specifies a valid product name but is not authorized
# to access that product, they will receive a different error message
# which could enable people guessing product names to determine
# whether or not certain products exist in Bugzilla, even if they
# cannot get any other information about that product.
grep($product eq $_ , @::legal_product)
|| DisplayError("The product name is invalid.")
&& exit;
# Make sure the user is authorized to access this product.
!CanSeeProduct($userid, $product)
&& DisplayError("You are not authorized to access that product.")
&& exit;
# Make sure the user specified a valid product name. Note that
# if the user specifies a valid product name but is not authorized
# to access that product, they will receive a different error message
@@ -96,6 +87,14 @@ grep($product eq $_ , @::legal_product)
|| DisplayError("The product name is invalid.")
&& exit;
# Make sure the user is authorized to access this product.
if (Param("usebuggroups") && GroupExists($product) && !$::userid) {
confirm_login();
UserInGroup($product)
|| DisplayError("You are not authorized to access that product.")
&& exit;
}
######################################################################
# End Data/Security Validation
######################################################################

View File

@@ -32,12 +32,12 @@ use vars qw($vars $template);
ConnectToDatabase();
my $userid = quietly_check_login();
quietly_check_login();
SendSQL("SELECT keyworddefs.name, keyworddefs.description,
COUNT(keywords.bug_id)
FROM keyworddefs LEFT JOIN keywords ON keyworddefs.id=keywords.keywordid
GROUP BY keyworddefs.id
GROUP BY keyworddefs.id, keyworddefs.name, keyworddefs.description, keywords.bug_id
ORDER BY keyworddefs.name");
my @keywords;
@@ -51,7 +51,7 @@ while (MoreSQLData()) {
}
$vars->{'keywords'} = \@keywords;
$vars->{'caneditkeywords'} = UserInGroup($userid, "editkeywords");
$vars->{'caneditkeywords'} = UserInGroup("editkeywords");
print "Content-type: text/html\n\n";
$template->process("reports/keywords.html.tmpl", $vars)

View File

@@ -0,0 +1,306 @@
<HTML
><HEAD
><TITLE
>Template Customisation</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Administering Bugzilla"
HREF="administration.html"><LINK
REL="PREVIOUS"
TITLE="Bugzilla Security"
HREF="security.html"><LINK
REL="NEXT"
TITLE="Integrating Bugzilla with Third-Party Tools"
HREF="integration.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="security.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 5. Administering Bugzilla</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="integration.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="cust-templates">5.7. Template Customisation</H1
><P
>&#13; One of the large changes for 2.16 was the templatisation of the
entire user-facing UI, using the
<A
HREF="http://www.template-toolkit.org"
TARGET="_top"
>Template Toolkit</A
>.
Administrators can now configure the look and feel of Bugzilla without
having to edit Perl files or face the nightmare of massive merge
conflicts when they upgrade to a newer version in the future.
</P
><P
>&#13; Templatisation also makes localised versions of Bugzilla possible,
for the first time. In the future, a Bugzilla installation may
have templates installed for multiple localisations, and select
which ones to use based on the user's browser language setting.
</P
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN1611">5.7.1. What to Edit</H2
><P
>&#13; There are several ways to take advantage of Bugzilla's templates,
and which you use depends on what you want to do. The Bugzilla
template directory structure is that there's a top level directory,
<TT
CLASS="filename"
>template</TT
>, which contains a directory for
each installed localisation. The default English templates are
therefore in <TT
CLASS="filename"
>en</TT
>. Underneath that, there
are two directories - <TT
CLASS="filename"
>default</TT
> and
<TT
CLASS="filename"
>custom</TT
>. The <TT
CLASS="filename"
>default</TT
>
directory contains all the templates shipped with Bugzilla.
</P
><P
>&#13; One method of making customisations is to directly edit the templates
in <TT
CLASS="filename"
>template/en/default</TT
>. This is probably the
best method for small changes, because if you then execute a
<B
CLASS="command"
>cvs update</B
>, any template fixes will get
automagically merged into your modified versions.
</P
><P
>&#13; The other method is to copy the templates into
<TT
CLASS="filename"
>template/en/custom</TT
>. This method is better if
you are going to make major changes, because it is guaranteed that
the contents of this directory will not be touched during an upgrade,
and you can then decide whether to continue using your own templates,
or make the effort to merge your changes into the new versions by
hand.
</P
><P
>&#13; The syntax of the Template Toolkit language is beyond the scope of
this guide. It's reasonably easy to pick up by looking at the current
templates; or, you can read the manual, available on the
<A
HREF="http://www.template-toolkit.org"
TARGET="_top"
>Template Toolkit home
page </A
>.
</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN1626">5.7.2. Particular Templates</H2
><P
>&#13; There are a few templates you may be particularly interested in
customising for your installation.
</P
><P
>&#13; <B
CLASS="command"
>global/header.html.tmpl</B
> and
<B
CLASS="command"
>global/footer.html.tmpl</B
>:
These define the header and footer that go on all Bugzilla pages.
Editing these is a way to quickly get a distinctive look and
feel for your Bugzilla installation.
</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN1632">5.7.3. Template Formats</H2
><P
>&#13; Some CGIs have the ability to use more than one template. For
example, buglist.cgi can output bug lists as RDF or two
different forms of HTML (complex and simple). (Try this out
by appending <TT
CLASS="filename"
>&#38;format=simple</TT
> to a buglist.cgi
URL on your Bugzilla installation.) This
mechanism, called template 'formats', is extensible.
</P
><P
>&#13; To see if a CGI supports multiple output formats, grep the
CGI for "ValidateOutputFormat". If it's not present, adding
multiple format support isn't too hard - see how it's done in
other CGIs.
</P
><P
>&#13; To make a new format template for a CGI which supports this,
open a current template for
that CGI and take note of the INTERFACE comment (if present.) This
comment defines what variables are passed into this template. If
there isn't one, I'm afraid you'll have to read the template and
the code to find out what information you get.
</P
><P
>&#13; Write your template in whatever markup or text style is appropriate.
</P
><P
>&#13; You now need to decide what content type you want your template
served as. Open up the localconfig file and find the $contenttypes
variable. If your content type is not there, add it. Remember
the three- or four-letter tag assigned to you content type.
This tag will be part of the template filename.
</P
><P
>&#13; Save the template as <TT
CLASS="filename"
>&#60;stubname&#62;-&#60;formatname&#62;.&#60;contenttypetag&#62;.tmpl</TT
>.
Try out the template by calling the CGI as
<TT
CLASS="filename"
>&#60;cginame&#62;.cgi?format=&#60;formatname&#62;</TT
> .
</P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="security.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="integration.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Bugzilla Security</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="administration.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Integrating Bugzilla with Third-Party Tools</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,390 @@
<HTML
><HEAD
><TITLE
>Optional Additional Configuration</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Installation"
HREF="installation.html"><LINK
REL="PREVIOUS"
TITLE="Step-by-step Install"
HREF="stepbystep.html"><LINK
REL="NEXT"
TITLE="Win32 Installation Notes"
HREF="win32.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="stepbystep.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 4. Installation</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="win32.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="extraconfig">4.2. Optional Additional Configuration</H1
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN836">4.2.1. Dependency Charts</H2
><P
>As well as the text-based dependency graphs, Bugzilla also
supports dependency graphing, using a package called 'dot'.
Exactly how this works is controlled by the 'webdotbase' parameter.
</P
><P
>(To be written...</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN840">4.2.2. Bug Graphs</H2
><P
>As long as you installed the GD and Graph::Base Perl modules you
might as well turn on the nifty Bugzilla bug reporting graphs.</P
><P
>Add a cron entry like this to run
<TT
CLASS="filename"
>collectstats.pl</TT
>
daily at 5 after midnight:
<P
></P
><TABLE
BORDER="0"
><TBODY
><TR
><TD
>&#13; <TT
CLASS="computeroutput"
>&#13; <TT
CLASS="prompt"
>bash#</TT
>
<B
CLASS="command"
>crontab -e</B
>
</TT
>
</TD
></TR
><TR
><TD
>&#13; <TT
CLASS="computeroutput"
>5 0 * * * cd &#60;your-bugzilla-directory&#62; ;
./collectstats.pl</TT
>
</TD
></TR
></TBODY
></TABLE
><P
></P
>
</P
><P
>After two days have passed you'll be able to view bug graphs from
the Bug Reports page.</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN853">4.2.3. The Whining Cron</H2
><P
>By now you have a fully functional Bugzilla, but what good are
bugs if they're not annoying? To help make those bugs more annoying you
can set up Bugzilla's automatic whining system to complain at engineers
which leave their bugs in the NEW state without triaging them.
</P
><P
>&#13; This can be done by
adding the following command as a daily crontab entry (for help on that
see that crontab man page):
<P
></P
><TABLE
BORDER="0"
><TBODY
><TR
><TD
>&#13; <TT
CLASS="computeroutput"
>&#13; <B
CLASS="command"
>cd &#60;your-bugzilla-directory&#62; ;
./whineatnews.pl</B
>
</TT
>
</TD
></TR
></TBODY
></TABLE
><P
></P
>
</P
><DIV
CLASS="tip"
><P
></P
><TABLE
CLASS="tip"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/tip.gif"
HSPACE="5"
ALT="Tip"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>Depending on your system, crontab may have several manpages.
The following command should lead you to the most useful page for
this purpose:
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="programlisting"
>man 5 crontab</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></TD
></TR
></TABLE
></DIV
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="bzldap">4.2.4. LDAP Authentication</H2
><P
>&#13; <DIV
CLASS="warning"
><P
></P
><TABLE
CLASS="warning"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/warning.gif"
HSPACE="5"
ALT="Warning"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>This information on using the LDAP
authentication options with Bugzilla is old, and the authors do
not know of anyone who has tested it. Approach with caution.
</P
></TD
></TR
></TABLE
></DIV
>
</P
><P
>&#13; The existing authentication
scheme for Bugzilla uses email addresses as the primary user ID, and a
password to authenticate that user. All places within Bugzilla where
you need to deal with user ID (e.g assigning a bug) use the email
address. The LDAP authentication builds on top of this scheme, rather
than replacing it. The initial log in is done with a username and
password for the LDAP directory. This then fetches the email address
from LDAP and authenticates seamlessly in the standard Bugzilla
authentication scheme using this email address. If an account for this
address already exists in your Bugzilla system, it will log in to that
account. If no account for that email address exists, one is created at
the time of login. (In this case, Bugzilla will attempt to use the
"displayName" or "cn" attribute to determine the user's full name.)
After authentication, all other user-related tasks are still handled by
email address, not LDAP username. You still assign bugs by email
address, query on users by email address, etc.
</P
><P
>Using LDAP for Bugzilla authentication requires the
Mozilla::LDAP (aka PerLDAP) Perl module. The
Mozilla::LDAP module in turn requires Netscape's Directory SDK for C.
After you have installed the SDK, then install the PerLDAP module.
Mozilla::LDAP and the Directory SDK for C are both
<A
HREF="http://www.mozilla.org/directory/"
TARGET="_top"
>available for
download</A
> from mozilla.org.
</P
><P
>&#13; Set the Param 'useLDAP' to "On" **only** if you will be using an LDAP
directory for
authentication. Be very careful when setting up this parameter; if you
set LDAP authentication, but do not have a valid LDAP directory set up,
you will not be able to log back in to Bugzilla once you log out. (If
this happens, you can get back in by manually editing the data/params
file, and setting useLDAP back to 0.)
</P
><P
>If using LDAP, you must set the
three additional parameters: Set LDAPserver to the name (and optionally
port) of your LDAP server. If no port is specified, it defaults to the
default port of 389. (e.g "ldap.mycompany.com" or
"ldap.mycompany.com:1234") Set LDAPBaseDN to the base DN for searching
for users in your LDAP directory. (e.g. "ou=People,o=MyCompany") uids
must be unique under the DN specified here. Set LDAPmailattribute to
the name of the attribute in your LDAP directory which contains the
primary email address. On most directory servers available, this is
"mail", but you may need to change this.
</P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="stepbystep.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="win32.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Step-by-step Install</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="installation.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Win32 Installation Notes</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,431 @@
<HTML
><HEAD
><TITLE
>Groups and Group Security</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Administering Bugzilla"
HREF="administration.html"><LINK
REL="PREVIOUS"
TITLE="Voting"
HREF="voting.html"><LINK
REL="NEXT"
TITLE="Bugzilla Security"
HREF="security.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="voting.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 5. Administering Bugzilla</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="security.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="groups">5.5. Groups and Group Security</H1
><P
>Groups can be very useful in bugzilla, because they allow users
to isolate bugs or products that should only be seen by certain people.
Groups can also be a complicated minefield of interdependencies and
weirdness if mismanaged.
<DIV
CLASS="example"
><A
NAME="AEN1521"><P
><B
>Example 5-5. When to Use Group Security</B
></P
><DIV
CLASS="informalexample"
><A
NAME="AEN1523"><P
></P
><P
>Many Bugzilla sites isolate "Security-related" bugs from all
other bugs. This way, they can have a fix ready before the security
vulnerability is announced to the world. You can create a
"Security" product which, by default, has no members, and only add
members to the group (in their individual User page, as described
under User Administration) who should have priveleged access to
"Security" bugs. Alternately, you may create a Group independently
of any Product, and change the Group mask on individual bugs to
restrict access to members only of certain Groups.</P
><P
></P
></DIV
></DIV
>
Groups only work if you enable the "usebuggroups" paramater. In
addition, if the "usebuggroupsentry" parameter is "On", one can
restrict access to products by groups, so that only members of a
product group are able to view bugs within that product. Group security
in Bugzilla can be divided into two categories: Generic and
Product-Based.</P
><DIV
CLASS="note"
><P
></P
><TABLE
CLASS="note"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/note.gif"
HSPACE="5"
ALT="Note"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>Groups in Bugzilla are a complicated beast that evolved out of
very simple user permission bitmasks, apparently itself derived from
common concepts in UNIX access controls. A "bitmask" is a
fixed-length number whose value can describe one, and only one, set
of states. For instance, UNIX file permissions are assigned bitmask
values: "execute" has a value of 1, "write" has a value of 2, and
"read" has a value of 4. Add them together, and a file can be read,
written to, and executed if it has a bitmask of "7". (This is a
simplified example -- anybody who knows UNIX security knows there is
much more to it than this. Please bear with me for the purpose of
this note.) The only way a bitmask scheme can work is by doubling the
bit count for each value. Thus if UNIX wanted to offer another file
permission, the next would have to be a value of 8, then the next 16,
the next 32, etc.</P
><P
>Similarly, Bugzilla offers a bitmask to define group
permissions, with an internal limit of 64. Several are already
occupied by built-in permissions. The way around this limitation is
to avoid assigning groups to products if you have many products,
avoid bloating of group lists, and religiously prune irrelevant
groups. In reality, most installations of Bugzilla support far fewer
than 64 groups, so this limitation has not hit for most sites, but it
is on the table to be revised for Bugzilla 3.0 because it interferes
with the security schemes of some administrators.</P
></TD
></TR
></TABLE
></DIV
><P
>To enable Generic Group Security ("usebuggroups"):</P
><P
></P
><OL
TYPE="1"
><LI
><P
>Turn "On" "usebuggroups" in the "Edit Parameters"
screen.</P
></LI
><LI
><P
>You will generally have no groups set up. Select the "groups"
link in the footer.</P
></LI
><LI
><P
>Take a moment to understand the instructions on the "Edit
Groups" screen. Once you feel confident you understand what is
expected of you, select the "Add Group" link.</P
></LI
><LI
><P
>Fill out the "New Name" (remember, no spaces!), "New
Description", and "New User RegExp" fields. "New User RegExp"
allows you to automatically place all users who fulfill the Regular
Expression into the new group.
<DIV
CLASS="example"
><A
NAME="AEN1538"><P
><B
>Example 5-6. Creating a New Group</B
></P
><DIV
CLASS="informalexample"
><A
NAME="AEN1540"><P
></P
><P
>I created a group called DefaultGroup with a description
of
<SPAN
CLASS="QUOTE"
>"This is simply a group to play with"</SPAN
>
, and a New User RegExp of
<SPAN
CLASS="QUOTE"
>".*@mydomain.tld"</SPAN
>
. This new group automatically includes all Bugzilla users with
"@mydomain.tld" at the end of their user id. When I finished,
my new group was assigned bit #128.</P
><P
></P
></DIV
></DIV
>
When you have finished, select the Add button.</P
></LI
></OL
><P
>To enable Product-Based Group Security
(usebuggroupsentry):</P
><DIV
CLASS="warning"
><P
></P
><TABLE
CLASS="warning"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/warning.gif"
HSPACE="5"
ALT="Warning"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>Don't forget that you only have 64 groups masks available,
total, for your installation of Bugzilla! If you plan on having more
than 50 products in your individual Bugzilla installation, and
require group security for your products, you should consider either
running multiple Bugzillas or using Generic Group Security instead of
Product-Based ("usebuggroupsentry") Group Security.</P
></TD
></TR
></TABLE
></DIV
><P
></P
><OL
TYPE="1"
><LI
><P
>Turn "On" "usebuggroups" and "usebuggroupsentry" in the "Edit
Parameters" screen.</P
><DIV
CLASS="warning"
><P
></P
><TABLE
CLASS="warning"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/warning.gif"
HSPACE="5"
ALT="Warning"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>"usebuggroupsentry" has the capacity to prevent the
administrative user from directly altering bugs because of
conflicting group permissions. If you plan on using
"usebuggroupsentry", you should plan on restricting
administrative account usage to administrative duties only. In
other words, manage bugs with an unpriveleged user account, and
manage users, groups, Products, etc. with the administrative
account.</P
></TD
></TR
></TABLE
></DIV
></LI
><LI
><P
>You will generally have no Groups set up, unless you enabled
"usebuggroupsentry" prior to creating any Products. To create
"Generic Group Security" groups, follow the instructions given
above. To create Product-Based Group security, simply follow the
instructions for creating a new Product. If you need to add users
to these new groups as you create them, you will find the option to
add them to the group available under the "Edit User"
screens.</P
></LI
></OL
><P
>You may find this example illustrative for how bug groups work.
<DIV
CLASS="example"
><A
NAME="AEN1555"><P
><B
>Example 5-7. Bugzilla Groups</B
></P
><P
CLASS="literallayout"
>Bugzilla&nbsp;Groups&nbsp;example&nbsp;-----------------------&nbsp;For<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this&nbsp;example,&nbsp;let&nbsp;us&nbsp;suppose&nbsp;we&nbsp;have&nbsp;four&nbsp;groups,&nbsp;call&nbsp;them&nbsp;Group1,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Group2,&nbsp;Group3,&nbsp;and&nbsp;Group4.&nbsp;We&nbsp;have&nbsp;5&nbsp;users,&nbsp;User1,&nbsp;User2,&nbsp;User3,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User4,&nbsp;User5.&nbsp;We&nbsp;have&nbsp;8&nbsp;bugs,&nbsp;Bug1,&nbsp;...,&nbsp;Bug8.&nbsp;Group&nbsp;membership&nbsp;is<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defined&nbsp;by&nbsp;this&nbsp;chart:&nbsp;(X&nbsp;denotes&nbsp;that&nbsp;user&nbsp;is&nbsp;in&nbsp;that&nbsp;group.)&nbsp;(I<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;apologize&nbsp;for&nbsp;the&nbsp;nasty&nbsp;formatting&nbsp;of&nbsp;this&nbsp;table.&nbsp;Try&nbsp;viewing&nbsp;it&nbsp;in&nbsp;a<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text-based&nbsp;browser&nbsp;or&nbsp;something&nbsp;for&nbsp;now.&nbsp;-MPB)&nbsp;G&nbsp;G&nbsp;G&nbsp;G&nbsp;r&nbsp;r&nbsp;r&nbsp;r&nbsp;o&nbsp;o&nbsp;o<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;o&nbsp;u&nbsp;u&nbsp;u&nbsp;u&nbsp;p&nbsp;p&nbsp;p&nbsp;p&nbsp;1&nbsp;2&nbsp;3&nbsp;4&nbsp;+-+-+-+-+&nbsp;User1|X|&nbsp;|&nbsp;|&nbsp;|&nbsp;+-+-+-+-+&nbsp;User2|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|X|&nbsp;|&nbsp;|&nbsp;+-+-+-+-+&nbsp;User3|X|&nbsp;|X|&nbsp;|&nbsp;+-+-+-+-+&nbsp;User4|X|X|X|&nbsp;|&nbsp;+-+-+-+-+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User5|&nbsp;|&nbsp;|&nbsp;|&nbsp;|&nbsp;+-+-+-+-+&nbsp;Bug&nbsp;restrictions&nbsp;are&nbsp;defined&nbsp;by&nbsp;this&nbsp;chart:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(X&nbsp;denotes&nbsp;that&nbsp;bug&nbsp;is&nbsp;restricted&nbsp;to&nbsp;that&nbsp;group.)&nbsp;G&nbsp;G&nbsp;G&nbsp;G&nbsp;r&nbsp;r&nbsp;r&nbsp;r&nbsp;o&nbsp;o<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;o&nbsp;o&nbsp;u&nbsp;u&nbsp;u&nbsp;u&nbsp;p&nbsp;p&nbsp;p&nbsp;p&nbsp;1&nbsp;2&nbsp;3&nbsp;4&nbsp;+-+-+-+-+&nbsp;Bug1|&nbsp;|&nbsp;|&nbsp;|&nbsp;|&nbsp;+-+-+-+-+&nbsp;Bug2|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|X|&nbsp;|&nbsp;|&nbsp;+-+-+-+-+&nbsp;Bug3|&nbsp;|&nbsp;|X|&nbsp;|&nbsp;+-+-+-+-+&nbsp;Bug4|&nbsp;|&nbsp;|&nbsp;|X|&nbsp;+-+-+-+-+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bug5|X|X|&nbsp;|&nbsp;|&nbsp;+-+-+-+-+&nbsp;Bug6|X|&nbsp;|X|&nbsp;|&nbsp;+-+-+-+-+&nbsp;Bug7|X|X|X|&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+-+-+-+-+&nbsp;Bug8|X|X|X|X|&nbsp;+-+-+-+-+&nbsp;Who&nbsp;can&nbsp;see&nbsp;each&nbsp;bug?&nbsp;Bug1&nbsp;has&nbsp;no<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;group&nbsp;restrictions.&nbsp;Therefore,&nbsp;Bug1&nbsp;can&nbsp;be&nbsp;seen&nbsp;by&nbsp;any&nbsp;user,&nbsp;whatever<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;their&nbsp;group&nbsp;membership.&nbsp;This&nbsp;is&nbsp;going&nbsp;to&nbsp;be&nbsp;the&nbsp;only&nbsp;bug&nbsp;that&nbsp;User5<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;can&nbsp;see,&nbsp;because&nbsp;User5&nbsp;isn't&nbsp;in&nbsp;any&nbsp;groups.&nbsp;Bug2&nbsp;can&nbsp;be&nbsp;seen&nbsp;by<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anyone&nbsp;in&nbsp;Group2,&nbsp;that&nbsp;is&nbsp;User2&nbsp;and&nbsp;User4.&nbsp;Bug3&nbsp;can&nbsp;be&nbsp;seen&nbsp;by&nbsp;anyone<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in&nbsp;Group3,&nbsp;that&nbsp;is&nbsp;User3&nbsp;and&nbsp;User4.&nbsp;Bug4&nbsp;can&nbsp;be&nbsp;seen&nbsp;by&nbsp;anyone&nbsp;in<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Group4.&nbsp;Nobody&nbsp;is&nbsp;in&nbsp;Group4,&nbsp;so&nbsp;none&nbsp;of&nbsp;these&nbsp;users&nbsp;can&nbsp;see&nbsp;Bug4.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bug5&nbsp;can&nbsp;be&nbsp;seen&nbsp;by&nbsp;anyone&nbsp;who&nbsp;is&nbsp;in&nbsp;_both_&nbsp;Group1&nbsp;and&nbsp;Group2.&nbsp;This<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is&nbsp;only&nbsp;User4.&nbsp;User1&nbsp;cannot&nbsp;see&nbsp;it&nbsp;because&nbsp;he&nbsp;is&nbsp;not&nbsp;in&nbsp;Group2,&nbsp;and<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User2&nbsp;cannot&nbsp;see&nbsp;it&nbsp;because&nbsp;she&nbsp;is&nbsp;not&nbsp;in&nbsp;Group1.&nbsp;Bug6&nbsp;can&nbsp;be&nbsp;seen&nbsp;by<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anyone&nbsp;who&nbsp;is&nbsp;in&nbsp;both&nbsp;Group1&nbsp;and&nbsp;Group3.&nbsp;This&nbsp;would&nbsp;include&nbsp;User3&nbsp;and<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User4.&nbsp;Similar&nbsp;to&nbsp;Bug5,&nbsp;User1&nbsp;cannot&nbsp;see&nbsp;Bug6&nbsp;because&nbsp;he&nbsp;is&nbsp;not&nbsp;in<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Group3.&nbsp;Bug7&nbsp;can&nbsp;be&nbsp;seen&nbsp;by&nbsp;anyone&nbsp;who&nbsp;is&nbsp;in&nbsp;Group1,&nbsp;Group2,&nbsp;and<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Group3.&nbsp;This&nbsp;is&nbsp;only&nbsp;User4.&nbsp;All&nbsp;of&nbsp;the&nbsp;others&nbsp;are&nbsp;missing&nbsp;at&nbsp;least<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;one&nbsp;of&nbsp;those&nbsp;group&nbsp;privileges,&nbsp;and&nbsp;thus&nbsp;cannot&nbsp;see&nbsp;the&nbsp;bug.&nbsp;Bug8&nbsp;can<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;be&nbsp;seen&nbsp;by&nbsp;anyone&nbsp;who&nbsp;is&nbsp;in&nbsp;Group1,&nbsp;Group2,&nbsp;Group3,&nbsp;and&nbsp;Group4.&nbsp;There<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is&nbsp;nobody&nbsp;in&nbsp;all&nbsp;four&nbsp;of&nbsp;these&nbsp;groups,&nbsp;so&nbsp;nobody&nbsp;can&nbsp;see&nbsp;Bug8.&nbsp;It<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doesn't&nbsp;matter&nbsp;that&nbsp;User4&nbsp;is&nbsp;in&nbsp;Group1,&nbsp;Group2,&nbsp;and&nbsp;Group3,&nbsp;since&nbsp;he<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isn't&nbsp;in&nbsp;Group4.</P
></DIV
>
</P
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="voting.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="security.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Voting</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="administration.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Bugzilla Security</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,296 @@
<HTML
><HEAD
><TITLE
>Hints and Tips</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Using Bugzilla"
HREF="using.html"><LINK
REL="PREVIOUS"
TITLE="How do I use Bugzilla?"
HREF="how.html"><LINK
REL="NEXT"
TITLE="User Preferences"
HREF="userpreferences.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="how.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 3. Using Bugzilla</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="userpreferences.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="hintsandtips">3.2. Hints and Tips</H1
><P
>This section distills some Bugzilla tips and best practices
that have been developed.</P
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN434">3.2.1. Autolinkification</H2
><P
>Bugzilla comments are plain text - so posting HTML will result
in literal HTML tags rather than being interpreted by a browser.
However, Bugzilla will automatically make hyperlinks out of certain
sorts of text in comments. For example, the text
http://www.bugzilla.org will be turned into
<A
HREF="http://www.bugzilla.org"
TARGET="_top"
>http://www.bugzilla.org</A
>.
Other strings which get linkified in the obvious manner are:
<P
></P
><TABLE
BORDER="0"
><TBODY
><TR
><TD
>bug 12345</TD
></TR
><TR
><TD
>bug 23456, comment 53</TD
></TR
><TR
><TD
>attachment 4321</TD
></TR
><TR
><TD
>mailto:george@example.com</TD
></TR
><TR
><TD
>ftp://ftp.mozilla.org</TD
></TR
><TR
><TD
>Most other sorts of URL</TD
></TR
></TBODY
></TABLE
><P
></P
>
</P
><P
>A corollary here is that if you type a bug number in a comment,
you should put the word "bug" before it, so it gets autolinkified
for the convenience of others.
</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="quicksearch">3.2.2. Quicksearch</H2
><P
>Quicksearch is a single-text-box query tool which uses
metacharacters to indicate what is to be searched. For example, typing
"foo|bar" into Quicksearch would search for "foo" or "bar" in the
summary and status whiteboard of a bug; adding ":BazProduct" would
search only in that product.
</P
><P
>You'll find the Quicksearch box on Bugzilla's
front page, along with a
<A
HREF="../../quicksearch.html"
TARGET="_top"
>Help</A
>
link which details how to use it.</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="commenting">3.2.3. Comments</H2
><P
>If you are changing the fields on a bug, only comment if
either you have something pertinent to say, or Bugzilla requires it.
Otherwise, you may spam people unnecessarily with bug mail.
To take an example: a user sets up their account to filter out messages
where someone just adds themselves to the CC field of a bug
(which happens a lot.) If you come along, add yourself to the CC field,
and add a comment saying "Adding self to CC", then that person
gets a pointless piece of mail they would otherwise have avoided.
</P
><P
>&#13; Don't use sigs in comments. Signing your name ("Bill") is acceptable,
particularly if you do it out of habit, but full mail/news-style
four line ASCII art creations are not.
</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="attachments">3.2.4. Attachments</H2
><P
>&#13; Use attachments, rather than comments, for large chunks of ASCII data,
such as trace, debugging output files, or log files. That way, it doesn't
bloat the bug for everyone who wants to read it, and cause people to
receive fat, useless mails.
</P
><P
>Trim screenshots. There's no need to show the whole screen if
you are pointing out a single-pixel problem.
</P
><P
>Don't attach simple test cases (e.g. one html file and one
css file and one image) as a ZIP file. Instead, upload them in
reverse order and edit the referring file so that they point to the
attached files. This way, the test case works immediately
out of the bug.
</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN460">3.2.5. Filing Bugs</H2
><P
>Try to make sure that everything said in the summary is also
said in the first comment. Summaries are often updated and this will
ensure your original information is easily accessible.
</P
><P
>&#13; You do not need to put "any" or similar strings in the URL field.
If there is no specific URL associated with the bug, leave this
field blank.
</P
><P
>If you feel a bug you filed was incorrectly marked as a
DUPLICATE of another, please question it in your bug, not
the bug it was duped to. Feel free to CC the person who duped it
if they are not already CCed.
</P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="how.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="userpreferences.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>How do I use Bugzilla?</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="using.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>User Preferences</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,153 @@
<HTML
><HEAD
><TITLE
>Introduction</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="PREVIOUS"
TITLE="Document Conventions"
HREF="conventions.html"><LINK
REL="NEXT"
TITLE="What is Bugzilla?"
HREF="whatis.html"></HEAD
><BODY
CLASS="chapter"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="conventions.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="whatis.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="chapter"
><H1
><A
NAME="introduction">Chapter 2. Introduction</H1
><DIV
CLASS="TOC"
><DL
><DT
><B
>Table of Contents</B
></DT
><DT
>2.1. <A
HREF="whatis.html"
>What is Bugzilla?</A
></DT
><DT
>2.2. <A
HREF="why.html"
>Why Should We Use Bugzilla?</A
></DT
></DL
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="conventions.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="whatis.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Document Conventions</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
>&nbsp;</TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>What is Bugzilla?</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,282 @@
<HTML
><HEAD
><TITLE
>Troubleshooting</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Installation"
HREF="installation.html"><LINK
REL="PREVIOUS"
TITLE="General Installation Notes"
HREF="geninstall.html"><LINK
REL="NEXT"
TITLE="Administering Bugzilla"
HREF="administration.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="geninstall.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 4. Installation</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="administration.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="troubleshooting">4.6. Troubleshooting</H1
><P
>This section gives solutions to common Bugzilla installation
problems.
</P
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="AEN1208">4.6.1. DBD::Sponge::db prepare failed</H2
><P
>&#13; The following error message may appear due to a bug in DBD::mysql
(over which the Bugzilla team have no control):
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="programlisting"
> "DBD::Sponge::db prepare failed: Cannot determine NUM_OF_FIELDS at D:/Perl/site/lib/DBD/mysql.pm line 248.
SV = NULL(0x0) at 0x20fc444
REFCNT = 1
FLAGS = (PADBUSY,PADMY)"
</PRE
></FONT
></TD
></TR
></TABLE
><P
>&#13; To fix this, go to &#60;path-to-perl&#62;/lib/DBD/sponge.pm in your Perl
installation and replace
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="programlisting"
> my $numFields;
if ($attribs-&#62;{'NUM_OF_FIELDS'}) {
$numFields = $attribs-&#62;{'NUM_OF_FIELDS'};
} elsif ($attribs-&#62;{'NAME'}) {
$numFields = @{$attribs-&#62;{NAME}};
</PRE
></FONT
></TD
></TR
></TABLE
><P
>&#13; by
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="programlisting"
> my $numFields;
if ($attribs-&#62;{'NUM_OF_FIELDS'}) {
$numFields = $attribs-&#62;{'NUM_OF_FIELDS'};
} elsif ($attribs-&#62;{'NAMES'}) {
$numFields = @{$attribs-&#62;{NAMES}};
</PRE
></FONT
></TD
></TR
></TABLE
><P
>&#13; (note the S added to NAME.)
</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="paranoid-security">4.6.2. cannot chdir(/var/spool/mqueue)</H2
><P
>If you are installing Bugzilla on SuSE Linux, or some other
distributions with
<SPAN
CLASS="QUOTE"
>"paranoid"</SPAN
>
security options, it is possible that the checksetup.pl script may fail
with the error:
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="programlisting"
>cannot chdir(/var/spool/mqueue): Permission denied
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>&#13; This is because your
<TT
CLASS="filename"
>/var/spool/mqueue</TT
>
directory has a mode of
<SPAN
CLASS="QUOTE"
>"drwx------"</SPAN
>. Type
<B
CLASS="command"
>chmod 755
<TT
CLASS="filename"
>/var/spool/mqueue</TT
>
</B
>
as root to fix this problem.
</P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="geninstall.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="administration.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>General Installation Notes</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="installation.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Administering Bugzilla</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,241 @@
<HTML
><HEAD
><TITLE
>User Preferences</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Using Bugzilla"
HREF="using.html"><LINK
REL="PREVIOUS"
TITLE="Hints and Tips"
HREF="hintsandtips.html"><LINK
REL="NEXT"
TITLE="Installation"
HREF="installation.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="hintsandtips.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 3. Using Bugzilla</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="installation.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="userpreferences">3.3. User Preferences</H1
><P
>Once you have logged in, you can customise various aspects of
Bugzilla via the "Edit prefs" link in the page footer.
The preferences are split into four tabs:</P
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="accountsettings">3.3.1. Account Settings</H2
><P
>On this tab, you can change your basic account information,
including your password, email address and real name. For security
reasons, in order to change anything on this page you must type your
<EM
>current</EM
>
password into the
<SPAN
CLASS="QUOTE"
>"Password"</SPAN
>
field at the top of the page.
If you attempt to change your email address, a confirmation
email is sent to both the old and new addresses, with a link to use to
confirm the change. This helps to prevent account hijacking.</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="emailsettings">3.3.2. Email Settings</H2
><P
>On this tab you can reduce or increase the amount of email sent
you from Bugzilla, opting in our out depending on your relationship to
the bug and the change that was made to it. (Note that you can also do
client-side filtering using the X-Bugzilla-Reason header which Bugzilla
adds to all bugmail.)</P
><P
>By entering user email names, delineated by commas, into the
"Users to watch" text entry box you can receive a copy of all the
bugmail of other users (security settings permitting.) This powerful
functionality enables seamless transitions as developers change
projects, managers wish to get in touch with the issues faced by their
direct reports, or users go on vacation.</P
><DIV
CLASS="note"
><P
></P
><TABLE
CLASS="note"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/note.gif"
HSPACE="5"
ALT="Note"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>The ability to watch other users may not be available in all
Bugzilla installations. If you can't see it, ask your
administrator.</P
></TD
></TR
></TABLE
></DIV
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="footersettings">3.3.3. Page Footer</H2
><P
>On the Search page, you can store queries in Bugzilla, so if you
regularly run a particular query it is just a drop-down menu away.
Once you have a stored query, you can come
here to request that it also be displayed in your page footer.</P
></DIV
><DIV
CLASS="section"
><H2
CLASS="section"
><A
NAME="permissionsettings">3.3.4. Permissions</H2
><P
>This is a purely informative page which outlines your current
permissions on this installation of Bugzilla - what product groups you
are in, and whether you can edit bugs or perform various administration
functions.</P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="hintsandtips.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="installation.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Hints and Tips</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="using.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Installation</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,235 @@
<HTML
><HEAD
><TITLE
>Voting</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
"><LINK
REL="HOME"
TITLE="The Bugzilla Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Administering Bugzilla"
HREF="administration.html"><LINK
REL="PREVIOUS"
TITLE="Product, Component, Milestone, and Version Administration"
HREF="programadmin.html"><LINK
REL="NEXT"
TITLE="Groups and Group Security"
HREF="groups.html"></HEAD
><BODY
CLASS="section"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>The Bugzilla Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="programadmin.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 5. Administering Bugzilla</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="groups.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="section"
><H1
CLASS="section"
><A
NAME="voting">5.4. Voting</H1
><P
>The concept of "voting" is a poorly understood, yet powerful
feature for the management of open-source projects. Each user is
assigned so many Votes per product, which they can freely reassign (or
assign multiple votes to a single bug). This allows developers to gauge
user need for a particular enhancement or bugfix. By allowing bugs with
a certain number of votes to automatically move from "UNCONFIRMED" to
"NEW", users of the bug system can help high-priority bugs garner
attention so they don't sit for a long time awaiting triage.</P
><P
>The daunting challenge of Votes is deciding where you draw the
line for a "vocal majority". If you only have a user base of 100 users,
setting a low threshold for bugs to move from UNCONFIRMED to NEW makes
sense. As the Bugzilla user base expands, however, these thresholds
must be re-evaluated. You should gauge whether this feature is worth
the time and close monitoring involved, and perhaps forego
implementation until you have a critical mass of users who demand
it.</P
><P
>To modify Voting settings:</P
><P
></P
><OL
TYPE="1"
><LI
><P
>Navigate to the "Edit product" screen for the Product you
wish to modify</P
></LI
><LI
><P
>Set "Maximum Votes per person" to your calculated value.
Setting this field to "0" disables voting.</P
></LI
><LI
><P
>Set "Maximum Votes a person can put on a single bug" to your
calculated value. It should probably be some number lower than the
"Maximum votes per person". Setting this field to "0" disables
voting, but leaves the voting options open to the user. This is
confusing.</P
></LI
><LI
><P
>Set "Number of votes a bug in this product needs to
automatically get out of the UNCONFIRMED state" to your calculated
number. Setting this field to "0" disables the automatic move of
bugs from UNCONFIRMED to NEW. Some people advocate leaving this at
"0", but of what use are Votes if your Bugzilla user base is unable
to affect which bugs appear on Development radar?
<DIV
CLASS="tip"
><P
></P
><TABLE
CLASS="tip"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/tip.gif"
HSPACE="5"
ALT="Tip"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>You should probably set this number to higher than a small
coalition of Bugzilla users can influence it. Most sites use this
as a "referendum" mechanism -- if users are able to vote a bug
out of UNCONFIRMED, it is a
<EM
>really</EM
>
bad bug!</P
></TD
></TR
></TABLE
></DIV
>
</P
></LI
><LI
><P
>Once you have adjusted the values to your preference, select
the "Update" button.</P
></LI
></OL
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="programadmin.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="groups.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Product, Component, Milestone, and Version Administration</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="administration.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Groups and Group Security</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>

View File

@@ -0,0 +1,22 @@
--- GD-1.33/Makefile.PL Fri Aug 4 16:59:22 2000
+++ GD-1.33-darwin/Makefile.PL Tue Jun 26 01:29:32 2001
@@ -3,8 +3,8 @@
warn "NOTICE: This module requires libgd 1.8.3 or higher (shared library version 4.X).\n";
# =====> PATHS: CHECK AND ADJUST <=====
-my @INC = qw(-I/usr/local/include -I/usr/local/include/gd);
-my @LIBPATH = qw(-L/usr/lib/X11 -L/usr/X11R6/lib -L/usr/X11/lib -L/usr/local/lib );
+my @INC = qw(-I/sw/include -I/sw/include/gd -I/usr/local/include -I/usr/local/include/gd);
+my @LIBPATH = qw(-L/usr/lib/X11 -L/usr/X11R6/lib -L/usr/X11/lib -L/sw/lib -L/usr/local/lib);
my @LIBS = qw(-lgd -lpng -lz);
# FEATURE FLAGS
@@ -23,7 +23,7 @@
push @LIBS,'-lttf' if $TTF;
push @LIBS,'-ljpeg' if $JPEG;
-push @LIBS, '-lm' unless $^O eq 'MSWin32';
+push @LIBS, '-lm' unless ($^O =~ /^MSWin32|darwin$/);
# FreeBSD 3.3 with libgd built from ports croaks if -lXpm is specified
if ($^O ne 'freebsd' && $^O ne 'MSWin32') {

View File

@@ -0,0 +1,146 @@
<chapter id="introduction">
<title>Introduction</title>
<section id="whatis">
<title>What is Bugzilla?</title>
<para>
Bugzilla is a bug- or issue-tracking system. Bug-tracking
systems allow individual or groups of developers effectively to keep track
of outstanding problems with their product.
Bugzilla was originally
written by Terry Weissman in a programming language called TCL, to
replace a rudimentary bug-tracking database used internally by Netscape
Communications. Terry later ported Bugzilla to Perl from TCL, and in Perl
it remains to this day. Most commercial defect-tracking software vendors
at the time charged enormous licensing fees, and Bugzilla quickly became
a favorite of the open-source crowd (with its genesis in the open-source
browser project, Mozilla). It is now the de-facto standard
defect-tracking system against which all others are measured.
</para>
<para>Bugzilla boasts many advanced features. These include:
<itemizedlist>
<listitem>
<para>Powerful searching</para>
</listitem>
<listitem>
<para>User-configurable email notifications of bug changes</para>
</listitem>
<listitem>
<para>Full change history</para>
</listitem>
<listitem>
<para>Inter-bug dependency tracking and graphing</para>
</listitem>
<listitem>
<para>Excellent attachment management</para>
</listitem>
<listitem>
<para>Integrated, product-based, granular security schema</para>
</listitem>
<listitem>
<para>Fully security-audited, and runs under Perl's taint mode</para>
</listitem>
<listitem>
<para>A robust, stable RDBMS back-end</para>
</listitem>
<listitem>
<para>Web, XML, email and console interfaces</para>
</listitem>
<listitem>
<para>Completely customisable and/or localisable web user
interface</para>
</listitem>
<listitem>
<para>Extensive configurability</para>
</listitem>
<listitem>
<para>Smooth upgrade pathway between versions</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="why">
<title>Why Should We Use Bugzilla?</title>
<para>For many years, defect-tracking software has remained principally
the domain of large software development houses. Even then, most shops
never bothered with bug-tracking software, and instead simply relied on
shared lists and email to monitor the status of defects. This procedure
is error-prone and tends to cause those bugs judged least significant by
developers to be dropped or ignored.</para>
<para>These days, many companies are finding that integrated
defect-tracking systems reduce downtime, increase productivity, and raise
customer satisfaction with their systems. Along with full disclosure, an
open bug-tracker allows manufacturers to keep in touch with their clients
and resellers, to communicate about problems effectively throughout the
data management chain. Many corporations have also discovered that
defect-tracking helps reduce costs by providing IT support
accountability, telephone support knowledge bases, and a common,
well-understood system for accounting for unusual system or software
issues.</para>
<para>But why should
<emphasis>you</emphasis>
use Bugzilla?</para>
<para>Bugzilla is very adaptable to various situations. Known uses
currently include IT support queues, Systems Administration deployment
management, chip design and development problem tracking (both
pre-and-post fabrication), and software and hardware bug tracking for
luminaries such as Redhat, NASA, Linux-Mandrake, and VA Systems.
Combined with systems such as CVS, Bonsai, or Perforce SCM, Bugzilla
provides a powerful, easy-to-use solution to configuration management and
replication problems.</para>
<para>Bugzilla can dramatically increase the productivity and
accountability of individual employees by providing a documented workflow
and positive feedback for good performance. How many times do you wake up
in the morning, remembering that you were supposed to do
<emphasis>something</emphasis>
today, but you just can't quite remember? Put it in Bugzilla, and you
have a record of it from which you can extrapolate milestones, predict
product versions for integration, and follow the discussion trail
that led to critical decisions.</para>
<para>Ultimately, Bugzilla puts the power in your hands to improve your
value to your employer or business while providing a usable framework for
your natural attention to detail and knowledge store to flourish.</para>
</section>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-always-quote-attributes:t
sgml-auto-insert-required-elements:t
sgml-balanced-tag-edit:t
sgml-exposed-tags:nil
sgml-general-insert-case:lower
sgml-indent-data:t
sgml-indent-step:2
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
sgml-minimize-attributes:nil
sgml-namecase-general:t
sgml-omittag:t
sgml-parent-document:("Bugzilla-Guide.sgml" "book" "chapter")
sgml-shorttag:t
sgml-tag-region-if-active:t
End:
-->

View File

@@ -34,11 +34,11 @@ use vars %::param,
@::param_list;
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
if (!UserInGroup($userid, "tweakparams")) {
if (!UserInGroup("tweakparams")) {
print "<H1>Sorry, you aren't a member of the 'tweakparams' group.</H1>\n";
print "And so, you aren't allowed to edit the parameters.\n";
PutFooter();

View File

@@ -39,7 +39,7 @@ use vars qw($template $vars);
ConnectToDatabase(1);
GetVersionTable();
my $userid = quietly_check_login();
quietly_check_login();
use vars qw (%FORM $userid $usergroupset @legal_product);
@@ -151,21 +151,18 @@ my @bugs;
my @bug_ids;
my $loop = 0;
# Determine which bugs we are allowed to see
my @canseebugs = keys %count;
my $canseeref = CanSeeBug(\@canseebugs, $userid);
my $canseeref = CanSeeBug(\@canseebugs, $userid, $usergroupset);
foreach my $id (keys(%count)) {
# Skip if we cannot see this bug
next if !$canseeref->{$id};
# Maximum row count is dealt with in the template.
# If there's a buglist, restrict the bugs to that list.
next if $sortvisible && $buglist[0] && (lsearch(\@buglist, $id) == -1);
SendSQL(SelectVisible("$generic_query bugs.bug_id = $id",
$userid,
$usergroupset));
# Skip if we cannot see this bug
next if !$canseeref->{$id};
SendSQL("$generic_query bugs.bug_id = $id");
next unless MoreSQLData();
my ($component, $bug_severity, $op_sys, $target_milestone,

View File

@@ -44,9 +44,8 @@ ConnectToDatabase();
# Make sure the user is logged in and is allowed to edit products
# (i.e. the user has "editcomponents" privileges), since attachment
# statuses are product-specific.
my $userid = confirm_login();
UserInGroup($userid, "editcomponents")
confirm_login();
UserInGroup("editcomponents")
|| DisplayError("You are not authorized to administer attachment statuses.")
&& exit;
@@ -184,7 +183,7 @@ sub list
SendSQL("SELECT id, name, description, sortkey, product, count(statusid)
FROM attachstatusdefs LEFT JOIN attachstatuses
ON attachstatusdefs.id=attachstatuses.statusid
GROUP BY id
GROUP BY id, name, description, sortkey, product
ORDER BY sortkey");
my @statusdefs;
while ( MoreSQLData() )
@@ -239,13 +238,17 @@ sub insert
my $desc = SqlQuote($::FORM{'desc'});
my $product = SqlQuote($::FORM{'product'});
SendSQL("LOCK TABLES attachstatusdefs WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachstatusdefs WRITE");
}
SendSQL("SELECT MAX(id) FROM attachstatusdefs");
my $id = FetchSQLData() || 0;
$id++;
SendSQL("INSERT INTO attachstatusdefs (id, name, description, sortkey, product)
VALUES ($id, $name, $desc, $::FORM{'sortkey'}, $product)");
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
# Display the "administer attachment status flags" page
# along with a message that the flag has been created.
@@ -287,7 +290,9 @@ sub update
my $name = SqlQuote($::FORM{'name'});
my $desc = SqlQuote($::FORM{'desc'});
SendSQL("LOCK TABLES attachstatusdefs WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachstatusdefs WRITE");
}
SendSQL("
UPDATE attachstatusdefs
SET name = $name ,
@@ -295,7 +300,9 @@ sub update
sortkey = $::FORM{'sortkey'}
WHERE id = $::FORM{'id'}
");
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
# Display the "administer attachment status flags" page
# along with a message that the flag has been updated.
@@ -333,11 +340,14 @@ sub confirmDelete
sub deleteStatus
{
# Delete an attachment status flag from the database.
SendSQL("LOCK TABLES attachstatusdefs WRITE, attachstatuses WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachstatusdefs WRITE, attachstatuses WRITE");
}
SendSQL("DELETE FROM attachstatuses WHERE statusid = $::FORM{'id'}");
SendSQL("DELETE FROM attachstatusdefs WHERE id = $::FORM{'id'}");
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
# Display the "administer attachment status flags" page
# along with a message that the flag has been deleted.

View File

@@ -188,11 +188,11 @@ sub PutTrailer (@)
# Preliminary checks:
#
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
unless (UserInGroup($userid, "editcomponents")) {
unless (UserInGroup("editcomponents")) {
PutHeader("Not allowed");
print "Sorry, you aren't a member of the 'editcomponents' group.\n";
print "And so, you aren't allowed to add, modify or delete components.\n";
@@ -227,7 +227,7 @@ unless ($product) {
SendSQL("SELECT products.product,products.description,COUNT(bug_id)
FROM products LEFT JOIN bugs
ON products.product=bugs.product
GROUP BY products.product
GROUP BY products.product, products.description
ORDER BY products.product");
} else {
SendSQL("SELECT products.product,products.description
@@ -275,12 +275,13 @@ unless ($action) {
FROM components LEFT JOIN bugs
ON components.program=bugs.product AND components.value=bugs.component
WHERE program=" . SqlQuote($product) . "
GROUP BY value");
GROUP BY value, description, initialowner, initialqacontact
ORDER BY value");
} else {
SendSQL("SELECT value,description,initialowner,initialqacontact
FROM components
WHERE program=" . SqlQuote($product) . "
GROUP BY value");
ORDER BY value");
}
print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR BGCOLOR=\"#6666FF\">\n";
print " <TH ALIGN=\"left\">Edit component ...</TH>\n";
@@ -579,12 +580,13 @@ if ($action eq 'delete') {
CheckComponent($product,$component);
# lock the tables before we start to change everything:
SendSQL("LOCK TABLES attachments WRITE,
bugs WRITE,
bugs_activity WRITE,
components WRITE,
dependencies WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachments WRITE,
bugs WRITE,
bugs_activity WRITE,
components WRITE,
dependencies WRITE");
}
# According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y,
# so I have to iterate over bugs and delete all the indivial entries
@@ -619,8 +621,9 @@ if ($action eq 'delete') {
WHERE program=" . SqlQuote($product) . "
AND value=" . SqlQuote($component));
print "Components deleted.<P>\n";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
unlink "data/versioncache";
PutTrailer($localtrailer);
exit;
@@ -714,15 +717,18 @@ if ($action eq 'update') {
# Note that the order of this tests is important. If you change
# them, be sure to test for WHERE='$component' or WHERE='$componentold'
SendSQL("LOCK TABLES bugs WRITE,
components WRITE, profiles READ");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES bugs WRITE,
components WRITE, profiles READ");
}
if ($description ne $descriptionold) {
unless ($description) {
print "Sorry, I can't delete the description.";
PutTrailer($localtrailer);
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
exit;
}
SendSQL("UPDATE components
@@ -735,8 +741,10 @@ if ($action eq 'update') {
if ($initialowner ne $initialownerold) {
unless ($initialowner) {
print "Sorry, I can't delete the initial owner.";
SendSQL("UNLOCK TABLES");
print "Sorry, I can't delete the initial owner.";
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
@@ -744,7 +752,9 @@ if ($action eq 'update') {
my $initialownerid = DBname_to_id($initialowner);
unless ($initialownerid) {
print "Sorry, you must use an existing Bugzilla account as initial owner.";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
@@ -760,7 +770,9 @@ if ($action eq 'update') {
my $initialqacontactid = DBname_to_id($initialqacontact);
if (!$initialqacontactid && $initialqacontact ne '') {
print "Sorry, you must use an existing Bugzilla account as initial QA contact.";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
@@ -777,13 +789,17 @@ if ($action eq 'update') {
unless ($component) {
print "Sorry, I can't delete the product name.";
PutTrailer($localtrailer);
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
exit;
}
if (TestComponent($product,$component)) {
print "Sorry, component name '$component' is already in use.";
PutTrailer($localtrailer);
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
exit;
}
@@ -800,7 +816,9 @@ if ($action eq 'update') {
unlink "data/versioncache";
print "Updated product name.<BR>\n";
}
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;

View File

@@ -29,11 +29,11 @@ use lib ".";
require "CGI.pl";
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
if (!UserInGroup($userid, "creategroups")) {
if (!UserInGroup("creategroups")) {
PutHeader("Not Authorized","Edit Groups","","Not Authorized for this function!");
print "<H1>Sorry, you aren't a member of the 'creategroups' group.</H1>\n";
print "And so, you aren't allowed to edit the groups.\n";
@@ -102,7 +102,7 @@ unless ($action) {
print "<form method=post action=editgroups.cgi>\n";
print "<table border=1>\n";
print "<tr>";
print "<th>ID</th>";
print "<th>Bit</th>";
print "<th>Name</th>";
print "<th>Description</th>";
print "<th>User RegExp</th>";
@@ -110,24 +110,24 @@ unless ($action) {
print "<th>Action</th>";
print "</tr>\n";
SendSQL("SELECT group_id,name,description,userregexp,isactive " .
SendSQL("SELECT group_bit,name,description,userregexp,isactive " .
"FROM groups " .
"WHERE isbuggroup != 0 " .
"ORDER BY group_id");
"ORDER BY group_bit");
while (MoreSQLData()) {
my ($id, $name, $desc, $regexp, $isactive) = FetchSQLData();
my ($bit, $name, $desc, $regexp, $isactive) = FetchSQLData();
print "<tr>\n";
print "<td valign=middle>$id</td>\n";
print "<td><input size=20 name=\"name-$id\" value=\"$name\">\n";
print "<input type=hidden name=\"oldname-$id\" value=\"$name\"></td>\n";
print "<td><input size=40 name=\"desc-$id\" value=\"$desc\">\n";
print "<input type=hidden name=\"olddesc-$id\" value=\"$desc\"></td>\n";
print "<td><input size=30 name=\"regexp-$id\" value=\"$regexp\">\n";
print "<input type=hidden name=\"oldregexp-$id\" value=\"$regexp\"></td>\n";
print "<td><input type=\"checkbox\" name=\"isactive-$id\" value=\"1\"" . ($isactive ? " checked" : "") . ">\n";
print "<input type=hidden name=\"oldisactive-$id\" value=\"$isactive\"></td>\n";
print "<td align=center valign=middle><a href=\"editgroups.cgi?action=del&group=$id\">Delete</a></td>\n";
print "<td valign=middle>$bit</td>\n";
print "<td><input size=20 name=\"name-$bit\" value=\"$name\">\n";
print "<input type=hidden name=\"oldname-$bit\" value=\"$name\"></td>\n";
print "<td><input size=40 name=\"desc-$bit\" value=\"$desc\">\n";
print "<input type=hidden name=\"olddesc-$bit\" value=\"$desc\"></td>\n";
print "<td><input size=30 name=\"regexp-$bit\" value=\"$regexp\">\n";
print "<input type=hidden name=\"oldregexp-$bit\" value=\"$regexp\"></td>\n";
print "<td><input type=\"checkbox\" name=\"isactive-$bit\" value=\"1\"" . ($isactive ? " checked" : "") . ">\n";
print "<input type=hidden name=\"oldisactive-$bit\" value=\"$isactive\"></td>\n";
print "<td align=center valign=middle><a href=\"editgroups.cgi?action=del&group=$bit\">Delete</a></td>\n";
print "</tr>\n";
}
@@ -163,27 +163,27 @@ sake of convience.<p>";
print "<table border=1>\n";
print "<tr>";
print "<th>ID</th>";
print "<th>Bit</th>";
print "<th>Name</th>";
print "<th>Description</th>";
print "<th>User RegExp</th>";
print "</tr>\n";
SendSQL("SELECT group_id,name,description,userregexp " .
SendSQL("SELECT group_bit,name,description,userregexp " .
"FROM groups " .
"WHERE isbuggroup = 0 " .
"ORDER BY group_id");
"ORDER BY group_bit");
while (MoreSQLData()) {
my ($id, $name, $desc, $regexp) = FetchSQLData();
my ($bit, $name, $desc, $regexp) = FetchSQLData();
print "<tr>\n";
print "<td>$id</td>\n";
print "<td>$bit</td>\n";
print "<td>$name</td>\n";
print "<input type=hidden name=\"name-$id\" value=\"$name\">\n";
print "<input type=hidden name=\"oldname-$id\" value=\"$name\">\n";
print "<input type=hidden name=\"name-$bit\" value=\"$name\">\n";
print "<input type=hidden name=\"oldname-$bit\" value=\"$name\">\n";
print "<td>$desc</td>\n";
print "<td><input type=text size=30 name=\"regexp-$id\" value=\"$regexp\"></td>\n";
print "<input type=hidden name=\"oldregexp-$id\" value=\"$regexp\">\n";
print "<td><input type=text size=30 name=\"regexp-$bit\" value=\"$regexp\"></td>\n";
print "<input type=hidden name=\"oldregexp-$bit\" value=\"$regexp\">\n";
print "</tr>\n";
}
@@ -287,10 +287,54 @@ if ($action eq 'new') {
exit;
}
# Major hack for bit values... perl can't handle 64-bit ints, so I can't
# just do the math to get the next available bit number, gotta handle
# them as strings... also, we're actually only going to allow 63 bits
# because that's all that opblessgroupset masks for (the high bit is off
# to avoid signing issues).
my @bitvals = ('1','2','4','8','16','32','64','128','256','512','1024',
'2048','4096','8192','16384','32768',
'65536','131072','262144','524288','1048576','2097152',
'4194304','8388608','16777216','33554432','67108864',
'134217728','268435456','536870912','1073741824',
'2147483648',
'4294967296','8589934592','17179869184','34359738368',
'68719476736','137438953472','274877906944',
'549755813888','1099511627776','2199023255552',
'4398046511104','8796093022208','17592186044416',
'35184372088832','70368744177664','140737488355328',
'281474976710656','562949953421312','1125899906842624',
'2251799813685248','4503599627370496','9007199254740992',
'18014398509481984','36028797018963968','72057594037927936',
'144115188075855872','288230376151711744',
'576460752303423488','1152921504606846976',
'2305843009213693952','4611686018427387904');
# First the next available bit
my $bit = "";
foreach (@bitvals) {
if ($bit eq "") {
SendSQL("SELECT group_bit FROM groups WHERE group_bit=" . SqlQuote($_));
if (!FetchOneColumn()) { $bit = $_; }
}
}
if ($bit eq "") {
ShowError("Sorry, you already have the maximum number of groups " .
"defined.<BR><BR>You must delete a group first before you " .
"can add any more.</B>");
PutTrailer("<a href=editgroups.cgi>Back to the group list</a>");
exit;
}
# Add the new group
SendSQL("INSERT INTO groups ( " .
"name, description, isbuggroup, userregexp, isactive" .
" ) VALUES ( " .
"group_bit, name, description, isbuggroup, userregexp, isactive" .
" ) VALUES (" .
$bit . "," .
SqlQuote($name) . "," .
SqlQuote($desc) . "," .
"1," .
@@ -298,30 +342,7 @@ if ($action eq 'new') {
$isactive . ")" );
print "OK, done.<p>\n";
SendSQL("SELECT group_id FROM groups where name = " . SqlQuote($name));
my $id = FetchOneColumn();
print "Your new group was assigned id # $id.<p>";
# Add each user designated as an Admin to the new group
my @adminlist = ();
SendSQL("SELECT userid FROM profiles WHERE admin = 1");
while (my @row = FetchSQLData()) {
push(@adminlist, $row[0]);
}
foreach my $userid (@adminlist) {
SendSQL("INSERT INTO user_group_map (user_id, group_id) VALUES ($userid, $id)");
}
# Add people who match regular expression to this group.
my $count = 0;
SendSQL("SELECT userid FROM profiles WHERE admin != 1 ORDER BY userid");
while (my ($userid) = FetchSQLData()) {
if ($userid =~ /$regexp/) {
PushGlobalSQLState();
SendSQL("INSERT INTO user_group_map (user_id, group_id) VALUES ($userid, $id)");
PopGlobalSQLState();
}
}
print "Your new group was assigned bit #$bit.<p>";
PutTrailer("<a href=\"editgroups.cgi?action=add\">Add another group</a>",
"<a href=\"editgroups.cgi\">Back to the group list</a>");
exit;
@@ -335,15 +356,15 @@ if ($action eq 'new') {
if ($action eq 'del') {
PutHeader("Delete group");
my $id = trim($::FORM{group} || '');
unless ($id) {
my $bit = trim($::FORM{group} || '');
unless ($bit) {
ShowError("No group specified.<BR>" .
"Click the <b>Back</b> button and try again.");
PutFooter();
exit;
}
SendSQL("SELECT group_id FROM groups WHERE group_id = " . SqlQuote($id));
if (!FetchOneColumn()) {
SendSQL("SELECT group_bit FROM groups WHERE group_bit=" . SqlQuote($bit));
if (!FetchOneColumn()) {
ShowError("That group doesn't exist.<BR>" .
"Click the <b>Back</b> button and try again.");
PutFooter();
@@ -351,17 +372,17 @@ if ($action eq 'del') {
}
SendSQL("SELECT name,description " .
"FROM groups " .
"WHERE group_id = " . SqlQuote($id));
"WHERE group_bit = " . SqlQuote($bit));
my ($name, $desc) = FetchSQLData();
print "<table border=1>\n";
print "<tr>";
print "<th>ID</th>";
print "<th>Bit</th>";
print "<th>Name</th>";
print "<th>Description</th>";
print "</tr>\n";
print "<tr>\n";
print "<td>$id</td>\n";
print "<td>$bit</td>\n";
print "<td>$name</td>\n";
print "<td>$desc</td>\n";
print "</tr>\n";
@@ -369,19 +390,38 @@ if ($action eq 'del') {
print "<FORM METHOD=POST ACTION=editgroups.cgi>\n";
my $cantdelete = 0;
SendSQL("SELECT COUNT(user_id) FROM user_group_map WHERE group_id = $id");
my $usergroupcount = FetchOneColumn();
if ($usergroupcount) {
$cantdelete = 1;
print "
if ($::driver eq 'mysql') {
SendSQL("SELECT login_name FROM profiles WHERE " .
"(groupset & $bit) OR (blessgroupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT login_name FROM profiles WHERE " .
"(groupset & int8($bit)) != 0 OR (blessgroupset & int8($bit)) != 0");
}
if (!FetchOneColumn()) {} else {
$cantdelete = 1;
if ($::driver eq 'mysql') {
print "
<B>One or more users belong to this group. You cannot delete this group while
there are users in it.</B><BR>
<A HREF=\"editusers.cgi?action=list&group=$id\">
Show me which users.</A> - <INPUT TYPE=CHECKBOX NAME=\"removeusers\">Remove all users from
<A HREF=\"editusers.cgi?action=list&query=" .
url_quote("(groupset & $bit) OR (blessgroupset & $bit)") . "\">Show me which users.</A> - <INPUT TYPE=CHECKBOX NAME=\"removeusers\">Remove all users from
this group for me<P>
";
} elsif ($::driver eq 'Pg') {
print "
<B>One or more users belong to this group. You cannot delete this group while
there are users in it.</B><BR>
<A HREF=\"editusers.cgi?action=list&query=" .
url_quote("(groupset & int8($bit)) != 0 OR (blessgroupset & int8($bit)) != 0") . "\">Show me which users.</A> - <INPUT TYPE=CHECKBOX NAME=\"removeusers\">Remove all users from
this group for me<P>
";
}
}
if ($::driver eq 'mysql') {
SendSQL("SELECT bug_id FROM bugs WHERE (groupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT bug_id FROM bugs WHERE (groupset & int8($bit)) != 0");
}
SendSQL("SELECT bug_id FROM bug_group_map WHERE group_id = $id ORDER BY bug_id");
if (MoreSQLData()) {
$cantdelete = 1;
my $buglist = "0";
@@ -419,7 +459,7 @@ You cannot delete this group while it is tied to a product.</B><BR>
}
print "<P><INPUT TYPE=SUBMIT VALUE=\"Yes, delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"delete\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"group\" VALUE=\"$id\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"group\" VALUE=\"$bit\">\n";
print "</FORM>";
PutTrailer("<a href=editgroups.cgi>No, go back to the group list</a>");
@@ -432,8 +472,8 @@ You cannot delete this group while it is tied to a product.</B><BR>
if ($action eq 'delete') {
PutHeader("Deleting group");
my $groupid = trim($::FORM{group} || '');
unless ($groupid) {
my $bit = trim($::FORM{group} || '');
unless ($bit) {
ShowError("No group specified.<BR>" .
"Click the <b>Back</b> button and try again.");
PutFooter();
@@ -441,24 +481,49 @@ if ($action eq 'delete') {
}
SendSQL("SELECT name " .
"FROM groups " .
"WHERE group_id = " . SqlQuote($groupid));
"WHERE group_bit = " . SqlQuote($bit));
my ($name) = FetchSQLData();
my $cantdelete = 0;
SendSQL("SELECT COUNT(user_id) FROM user_group_map WHERE group_id = $groupid");
if (FetchOneColumn()) {
if (!defined $::FORM{'removeusers'}) {
$cantdelete = 1;
}
my $opblessgroupset = '9223372036854775807';
if ($::driver eq 'mysql') {
SendSQL("SELECT userid FROM profiles " .
"WHERE (groupset & $opblessgroupset) = $opblessgroupset");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT userid FROM profiles " .
"WHERE (groupset & int8($opblessgroupset)) = $opblessgroupset");
}
my @opusers = ();
while (MoreSQLData()) {
my ($userid) = FetchSQLData();
push @opusers, $userid; # cache a list of the users with admin powers
}
if ($::driver eq 'mysql') {
SendSQL("SELECT login_name FROM profiles WHERE " .
"(groupset & $bit) = $bit OR (blessgroupset & $bit) = $bit");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT login_name FROM profiles WHERE " .
"(groupset & int8($bit)) = $bit OR (blessgroupset & int8($bit)) = $bit");
}
if (FetchOneColumn()) {
if (!defined $::FORM{'removeusers'}) {
$cantdelete = 1;
}
}
if ($::driver eq 'mysql') {
SendSQL("SELECT bug_id FROM bugs WHERE (groupset & $bit) = $bit");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT bug_id FROM bugs WHERE (groupset & int8($bit)) = $bit");
}
SendSQL("SELECT COUNT(bug_id) FROM bug_group_map WHERE group_id = $groupid");
if (FetchOneColumn()) {
if (!defined $::FORM{'removebugs'}) {
$cantdelete = 1;
}
}
SendSQL("SELECT product FROM products WHERE product = " . SqlQuote($name));
SendSQL("SELECT product FROM products WHERE product=" . SqlQuote($name));
if (FetchOneColumn()) {
if (!defined $::FORM{'unbind'}) {
$cantdelete = 1;
@@ -470,27 +535,68 @@ if ($action eq 'delete') {
"records in the database which refer to it. All child records " .
"must be removed or altered to remove the reference to this " .
"group before the group can be deleted.");
print "<A HREF=\"editgroups.cgi?action=del&group=$groupid\">" .
print "<A HREF=\"editgroups.cgi?action=del&group=$bit\">" .
"View the list of which records are affected</A><BR>";
PutTrailer("<a href=editgroups.cgi>Back to group list</a>");
exit;
}
if ($::driver eq 'mysql') {
SendSQL("SELECT login_name, groupset, blessgroupset FROM profiles WHERE " .
"(groupset & $bit) OR (blessgroupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT login_name, groupset, blessgroupset FROM profiles WHERE " .
"(groupset & int8($bit)) != 0 OR (blessgroupset & int8($bit)) != 0 ");
}
if (FetchOneColumn()) {
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles SET groupset = (groupset - $bit) " .
"WHERE (groupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles SET groupset = (groupset - int8($bit)) " .
"WHERE (groupset & int8($bit)) != 0");
}
print "All users have been removed from group $bit.<BR>";
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles SET blessgroupset = (blessgroupset - $bit) " .
"WHERE (blessgroupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles SET blessgroupset = (blessgroupset - int8($bit)) " .
"WHERE (blessgroupset & int8($bit)) != 0");
}
print "All users with authority to add users to group $bit have " .
"had that authority removed.<BR>";
}
if ($::driver eq 'mysql') {
SendSQL("SELECT bug_id FROM bugs WHERE (groupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT bug_id FROM bugs WHERE (groupset & int8($bit)) != 0");
}
if (FetchOneColumn()) {
if ($::driver eq 'mysql') {
SendSQL("UPDATE bugs SET groupset = (groupset - $bit), delta_ts=delta_ts" .
"WHERE (groupset & $bit)");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE bugs SET groupset = (groupset - int8($bit)), delta_ts=now() " .
"WHERE (groupset & int8($bit)) != 0");
}
print "All bugs have had group bit $bit cleared. Any of these " .
"bugs that were not also in another group are now " .
"publicly visible.<BR>";
}
SendSQL("DELETE FROM groups WHERE group_bit = $bit");
print "<B>Group $bit has been deleted.</B><BR>";
SendSQL("SELECT COUNT(user_id) FROM user_group_map " .
"WHERE group_id = $groupid");
if (FetchOneColumn()) {
SendSQL("DELETE FROM user_group_map WHERE group_id = $groupid");
print "All users have been removed from group $groupid.<BR>";
foreach my $userid (@opusers) {
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles SET groupset = $opblessgroupset " .
"WHERE userid = $userid");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles SET groupset = int8($opblessgroupset) " .
"WHERE userid = $userid");
}
print "Group bits restored for " . DBID_to_name($userid) .
" (maintainer)<BR>\n";
}
SendSQL("SELECT COUNT(bug_id) FROM bug_group_map WHERE group_id = $groupid");
if (FetchOneColumn()) {
SendSQL("DELETE FROM bug_group_map WHERE group_id = $groupid");
print "All bugs have had group bit $groupid cleared. Any of these " .
"bugs that were not also in another group are now " .
"publicly visible.<BR>";
}
SendSQL("DELETE FROM groups WHERE group_id = $groupid");
print "<B>Group $groupid has been deleted.</B><BR>";
PutTrailer("<a href=editgroups.cgi>Back to group list</a>");
exit;
@@ -516,16 +622,19 @@ if ($action eq 'update') {
if ($::FORM{"oldname-$v"} ne $::FORM{"name-$v"}) {
$chgs = 1;
SendSQL("SELECT name FROM groups WHERE name = " . SqlQuote($::FORM{"name-$v"}));
SendSQL("SELECT name FROM groups WHERE name=" .
SqlQuote($::FORM{"name-$v"}));
if (!FetchOneColumn()) {
SendSQL("SELECT name FROM groups WHERE name = " . SqlQuote($::FORM{"oldname-$v"}) .
" AND isbuggroup = 0");
SendSQL("SELECT name FROM groups WHERE name=" .
SqlQuote($::FORM{"oldname-$v"}) .
" && isbuggroup = 0");
if (FetchOneColumn()) {
ShowError("You cannot update the name of a " .
"system group. Skipping $v");
} else {
SendSQL("UPDATE groups SET name = " . SqlQuote($::FORM{"name-$v"}) .
" WHERE group_id = " . SqlQuote($v));
SendSQL("UPDATE groups SET name = " .
SqlQuote($::FORM{"name-$v"}) .
" WHERE group_bit = " . SqlQuote($v));
print "Group $v name updated.<br>\n";
}
} else {
@@ -536,10 +645,11 @@ if ($action eq 'update') {
}
if ($::FORM{"olddesc-$v"} ne $::FORM{"desc-$v"}) {
$chgs = 1;
SendSQL("SELECT description FROM groups WHERE description = " . SqlQuote($::FORM{"desc-$v"}));
SendSQL("SELECT description FROM groups WHERE description=" .
SqlQuote($::FORM{"desc-$v"}));
if (!FetchOneColumn()) {
SendSQL("UPDATE groups SET description = " . SqlQuote($::FORM{"desc-$v"}) .
" WHERE group_id = " . SqlQuote($v));
SendSQL("UPDATE groups SET description=" .
" WHERE group_bit=" . SqlQuote($v));
print "Group $v description updated.<br>\n";
} else {
ShowError("Duplicate description '" . $::FORM{"desc-$v"} .
@@ -549,9 +659,9 @@ if ($action eq 'update') {
}
if ($::FORM{"oldregexp-$v"} ne $::FORM{"regexp-$v"}) {
$chgs = 1;
SendSQL("UPDATE groups SET userregexp = " .
SendSQL("UPDATE groups SET userregexp=" .
SqlQuote($::FORM{"regexp-$v"}) .
" WHERE group_id = " . SqlQuote($v));
" WHERE group_bit=" . SqlQuote($v));
print "Group $v user regexp updated.<br>\n";
}
# convert an undefined value in the inactive field to zero
@@ -561,8 +671,8 @@ if ($action eq 'update') {
if ($::FORM{"oldisactive-$v"} != $isactive) {
$chgs = 1;
if ($isactive == 0 || $isactive == 1) {
SendSQL("UPDATE groups SET isactive = $isactive " .
"WHERE group_id = " . SqlQuote($v));
SendSQL("UPDATE groups SET isactive=$isactive" .
" WHERE group_bit=" . SqlQuote($v));
print "Group $v active flag updated.<br>\n";
} else {
ShowError("The value '" . $isactive .

View File

@@ -108,11 +108,11 @@ sub Validate ($$) {
# Preliminary checks:
#
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
unless (UserInGroup($userid, "editkeywords")) {
unless (UserInGroup("editkeywords")) {
PutHeader("Not allowed");
print "Sorry, you aren't a member of the 'editkeywords' group.\n";
print "And so, you aren't allowed to add, modify or delete keywords.\n";
@@ -142,7 +142,7 @@ if ($action eq "") {
SendSQL("SELECT keyworddefs.id, keyworddefs.name, keyworddefs.description,
COUNT(keywords.bug_id), keywords.bug_id
FROM keyworddefs LEFT JOIN keywords ON keyworddefs.id = keywords.keywordid
GROUP BY keyworddefs.id
GROUP BY keyworddefs.id, keyworddefs.name, keyworddefs.description, keywords.bug_id
ORDER BY keyworddefs.name");
while (MoreSQLData()) {
my ($id, $name, $description, $bugs, $onebug) = FetchSQLData();

View File

@@ -146,11 +146,11 @@ sub PutTrailer (@)
# Preliminary checks:
#
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
unless (UserInGroup($userid, "editcomponents")) {
unless (UserInGroup("editcomponents")) {
PutHeader("Not allowed");
print "Sorry, you aren't a member of the 'editcomponents' group.\n";
print "And so, you aren't allowed to add, modify or delete milestones.\n";

View File

@@ -32,11 +32,11 @@ require "defparams.pl";
use vars @::param_desc,
@::param_list;
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
if (!UserInGroup($userid, "tweakparams")) {
if (!UserInGroup("tweakparams")) {
print "<H1>Sorry, you aren't a member of the 'tweakparams' group.</H1>\n";
print "And so, you aren't allowed to edit the parameters.\n";
PutFooter();

View File

@@ -42,7 +42,6 @@ sub sillyness {
$zz = $::unconfirmedstate;
}
my $userid = 0;
# TestProduct: just returns if the specified product does exists
# CheckProduct: same check, optionally emit an error text
@@ -74,12 +73,6 @@ sub CheckProduct ($)
PutTrailer();
exit;
}
unless (CanSeeProduct($userid, $prod)) {
print "Sorry, You do not have permission to modify product '$prod'.";
PutTrailer();
exit;
}
}
@@ -87,9 +80,9 @@ sub CheckProduct ($)
# Displays the form to edit a products parameters
#
sub EmitFormElements ($$$$$$$$)
sub EmitFormElements ($$$$$$$$$)
{
my ($product, $description, $milestoneurl, $disallownew,
my ($product, $description, $milestoneurl, $userregexp, $disallownew,
$votesperuser, $maxvotesperbug, $votestoconfirm, $defaultmilestone)
= @_;
@@ -118,6 +111,14 @@ sub EmitFormElements ($$$$$$$$)
print qq{<INPUT TYPE=HIDDEN NAME="defaultmilestone" VALUE="$defaultmilestone">\n};
}
# Added -JMR, 2/16/00
if (Param("usebuggroups")) {
$userregexp = value_quote($userregexp);
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">User Regexp for Bug Group:</TH>\n";
print " <TD><INPUT TYPE=TEXT SIZE=64 MAXLENGTH=255 NAME=\"userregexp\" VALUE=\"$userregexp\"></TD>\n";
}
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Closed for bug entry:</TH>\n";
my $closed = $disallownew ? "CHECKED" : "";
@@ -134,46 +135,6 @@ sub EmitFormElements ($$$$$$$$)
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Number of votes a bug in this product needs to automatically get out of the <A HREF=\"bug_status.html#status\">UNCONFIRMED</A> state:</TH>\n";
print " <TD><INPUT SIZE=5 MAXLENGTH=5 NAME=\"votestoconfirm\" VALUE=\"$votestoconfirm\"></TD>\n";
# Find list of groups that this product can be marked private to
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Group Access:</TH>\n";
print " <TD><TABLE>\n";
my $productid = 0;
my %productbelongs = ();
if ($product) {
SendSQL("select product_id from products where product = " . SqlQuote($product));
$productid = FetchOneColumn();
SendSQL("select groups.group_id from groups, product_group_map " .
"where groups.group_id = product_group_map.group_id ".
"and product_group_map.product_id = $productid");
while (my ($groupid) = FetchSQLData()) {
$productbelongs{$groupid} = 1;
}
}
my %groupsbelong = ();
my %groupsbelongname = ();
my %groupsbelongdesc = ();
SendSQL("select groups.group_id, groups.name, groups.description " .
"from groups, user_group_map " .
"where groups.group_id = user_group_map.group_id " .
"and groups.isbuggroup = 1 " .
"and user_group_map.user_id = $userid");
while (my ($groupid, $name, $desc) = FetchSQLData()) {
$groupsbelong{$groupid} = 1;
$groupsbelongname{$groupid} = $name;
$groupsbelongdesc{$groupid} = $desc;
}
foreach my $groupid (keys %groupsbelong) {
my $checked = defined ($productbelongs{$groupid}) ? "CHECKED" : "";
print "<TR>\n<TD><INPUT TYPE=\"checkbox\" NAME=\"group_$groupid\" VALUE=\"$groupid\" $checked></TD>\n";
print "<TD><B>" . ucfirst($groupsbelongname{$groupid}) . ":</B> ";
print "$groupsbelongdesc{$groupid}</TD>\n</TR>\n";
}
print "</TABLE>\n</TD>\n";
print "<INPUT TYPE=\"hidden\" NAME=\"productid\" VALUE=\"$productid\">\n";
}
@@ -214,11 +175,11 @@ sub PutTrailer (@)
# Preliminary checks:
#
$userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
unless (UserInGroup($userid, "editcomponents")) {
unless (UserInGroup("editcomponents")) {
PutHeader("Not allowed");
print "Sorry, you aren't a member of the 'editcomponents' group.\n";
print "And so, you aren't allowed to add, modify or delete products.\n";
@@ -248,7 +209,8 @@ unless ($action) {
votesperuser,maxvotesperbug,votestoconfirm,COUNT(bug_id)
FROM products LEFT JOIN bugs
ON products.product=bugs.product
GROUP BY products.product
GROUP BY products.product,description,disallownew,
votesperuser,maxvotesperbug,votestoconfirm
ORDER BY products.product");
print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR BGCOLOR=\"#6666FF\">\n";
print " <TH ALIGN=\"left\">Edit product ...</TH>\n";
@@ -263,10 +225,6 @@ unless ($action) {
while ( MoreSQLData() ) {
my ($product, $description, $disallownew, $votesperuser,
$maxvotesperbug, $votestoconfirm, $bugs) = FetchSQLData();
# Skip this product if user cannot see it
next if !CanSeeProduct($userid, $product);
$description ||= "<FONT COLOR=\"red\">missing</FONT>";
$disallownew = $disallownew ? 'closed' : 'open';
$bugs ||= 'none';
@@ -307,7 +265,7 @@ if ($action eq 'add') {
print "<FORM METHOD=POST ACTION=editproducts.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements('', '', '', 0, 0, 10000, 0, "---");
EmitFormElements('', '', '', '', 0, 0, 10000, 0, "---");
print "</TR><TR>\n";
print " <TH ALIGN=\"right\">Version:</TH>\n";
@@ -359,6 +317,7 @@ if ($action eq 'new') {
my $description = trim($::FORM{description} || '');
my $milestoneurl = trim($::FORM{milestoneurl} || '');
my $userregexp = trim($::FORM{userregexp} || '');
my $disallownew = 0;
$disallownew = 1 if $::FORM{disallownew};
my $votesperuser = $::FORM{votesperuser};
@@ -380,40 +339,65 @@ if ($action eq 'new') {
$disallownew . "," .
"$votesperuser, $maxvotesperbug, $votestoconfirm, " .
SqlQuote($defaultmilestone) . ")");
SendSQL("SELECT LAST_INSERT_ID()");
my $productid = FetchOneColumn();
SendSQL("INSERT INTO versions ( " .
"value, program" .
" ) VALUES ( " .
SqlQuote($version) . "," .
SqlQuote($product) . ")" );
SendSQL("INSERT INTO milestones (product, value) VALUES (" .
SqlQuote($product) . ", " . SqlQuote($defaultmilestone) . ")");
SendSQL("INSERT INTO milestones (product, value, sortkey) VALUES (" .
SqlQuote($product) . ", " . SqlQuote($defaultmilestone) . ", 1)");
# Update group permissions for this product
my %newgroups = ();
foreach (keys %::FORM) {
next unless /^group_/;
detaint_natural($::FORM{$_});
$newgroups{$::FORM{$_}} = 1;
}
# 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")) {
# First we need to figure out the bit for this group. We'll simply
# use the next highest bit available. We'll use a minimum bit of 256,
# to leave room for a few more Bugzilla operation groups at the bottom.
SendSQL("SELECT MAX(group_bit) FROM groups");
my $bit = FetchOneColumn();
if($bit < 256) {
$bit = 256;
} else {
$bit = $bit * 2;
}
# Next we insert into the groups table
SendSQL("INSERT INTO groups " .
"(group_bit, name, description, isbuggroup, userregexp) " .
"VALUES (" .
$bit . ", " .
SqlQuote($product) . ", " .
SqlQuote($product . " Bugs Access") . ", " .
"1, " .
SqlQuote($userregexp) . ")");
# And last, we need to add any existing users that match the regexp
# to the group.
# There may be a better way to do this in MySql, but I need to compare
# the login_names to this regexp, and the only way I can think of to
# do that is to get the list of login_names, and then update them
# one by one if they match. Furthermore, I need to do it with two
# separate loops, since opening a new SQL statement to do the update
# seems to clobber the previous one.
my %groupsbelong = ();
SendSQL("select groups.group_id from groups, user_group_map " .
"where groups.group_id = user_group_map.group_id " .
"and groups.isbuggroup = 1 " .
"and user_group_map.user_id = $userid");
while (my ($groupid) = FetchSQLData()) {
$groupsbelong{$groupid} = 1;
}
foreach my $groupid (keys %groupsbelong) {
if ($newgroups{$groupid}) {
SendSQL("INSERT INTO product_group_map (product_id, group_id) VALUES ($productid, $groupid)");
# Modified, 7/17/00, Joe Robins
# If the userregexp is left empty, then no users should be added to
# the bug group. As is, it was adding all users, since they all
# matched the empty pattern.
# In addition, I've replaced the rigamarole I was going through to
# find matching users with a much simpler statement that lets the
# mySQL database do the work.
unless($userregexp eq "") {
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles ".
"SET groupset = groupset | " . $bit . " " .
"WHERE " . SqlRegEx("login_name", SqlQuote($userregexp)));
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles ".
"SET groupset = groupset | int8(" . $bit . ") " .
"WHERE " . SqlRegEx("login_name", SqlQuote($userregexp)));
}
}
}
@@ -466,6 +450,23 @@ if ($action eq 'del') {
print " <TD VALIGN=\"top\">$milestonelink</TD>\n";
}
# Added -JMR, 2/16/00
if(Param('usebuggroups')) {
# Get the regexp for this product.
SendSQL("SELECT userregexp
FROM groups
WHERE name=" . SqlQuote($product));
my $userregexp = FetchOneColumn();
if(!defined $userregexp) {
$userregexp = "<FONT COLOR=\"red\">undefined</FONT>";
} elsif ($userregexp eq "") {
$userregexp = "<FONT COLOR=\"blue\">blank</FONT>";
}
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">User Regexp for Bug Group:</TD>\n";
print " <TD VALIGN=\"top\">$userregexp</TD>\n";
}
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">Closed for bugs:</TD>\n";
print " <TD VALIGN=\"top\">$disallownew</TD>\n";
@@ -583,23 +584,20 @@ one.";
if ($action eq 'delete') {
PutHeader("Deleting product");
CheckProduct($product);
# lock the tables before we start to change everything:
SendSQL("LOCK TABLES attachments WRITE,
bugs WRITE,
bugs_activity WRITE,
components WRITE,
dependencies WRITE,
versions WRITE,
products WRITE,
groups WRITE,
profiles WRITE,
milestones WRITE,
product_group_map WRITE");
SendSQL("SELECT product_id FROM products WHERE product = " . SqlQuote($product));
my $productid = FetchOneColumn();
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachments WRITE,
bugs WRITE,
bugs_activity WRITE,
components WRITE,
dependencies WRITE,
versions WRITE,
products WRITE,
groups WRITE,
profiles WRITE,
milestones WRITE");
}
# According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y,
# so I have to iterate over bugs and delete all the indivial entries
@@ -641,16 +639,45 @@ if ($action eq 'delete') {
WHERE product=" . SqlQuote($product));
print "Milestones deleted.<BR>\n";
# Deleting any product groups
SendSQL("DELETE FROM product_group_map WHERE product_id = $productid");
print "Product groups deleted.<BR>\n";
SendSQL("DELETE FROM products
WHERE product=" . SqlQuote($product));
print "Product '$product' deleted.<BR>\n";
SendSQL("UNLOCK TABLES");
# Added -JMR, 2/16/00
if (Param("usebuggroups")) {
# We need to get the bit of the group from the table, then update the
# groupsets of members of that group and remove the group.
SendSQL("SELECT group_bit, description FROM groups " .
"WHERE name = " . SqlQuote($product));
my ($bit, $group_desc) = FetchSQLData();
# Make sure there is a group before we try to do any deleting...
if($bit) {
# I'm kludging a bit so that I don't break superuser access;
# I'm merely checking to make sure that the groupset is not
# the superuser groupset in doing this update...
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles " .
"SET groupset = groupset - $bit " .
"WHERE (groupset & $bit) " .
"AND (groupset != 9223372036854710271)");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles " .
"SET groupset = groupset - $bit " .
"WHERE (groupset & int8($bit)) " .
"AND (groupset != int8(9223372036854710271))");
}
print "Users dropped from group '$group_desc'.<BR>\n";
SendSQL("DELETE FROM groups " .
"WHERE group_bit = $bit");
print "Group '$group_desc' deleted.<BR>\n";
}
}
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
unlink "data/versioncache";
PutTrailer($localtrailer);
exit;
@@ -688,7 +715,7 @@ if ($action eq 'edit') {
print "<FORM METHOD=POST ACTION=editproducts.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements($product, $description, $milestoneurl,
EmitFormElements($product, $description, $milestoneurl, $userregexp,
$disallownew, $votesperuser, $maxvotesperbug,
$votestoconfirm, $defaultmilestone);
@@ -774,6 +801,10 @@ if ($action eq 'edit') {
value_quote($description) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"milestoneurlold\" VALUE=\"" .
value_quote($milestoneurl) . "\">\n";
if(Param("usebuggroups")) {
print "<INPUT TYPE=HIDDEN NAME=\"userregexpold\" VALUE=\"" .
value_quote($userregexp) . "\">\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";
@@ -799,8 +830,7 @@ if ($action eq 'edit') {
if ($action eq 'update') {
PutHeader("Update product");
my $productid = trim($::FORM{productid} || 0);
my $productold = trim($::FORM{productold} || '');
my $description = trim($::FORM{description} || '');
my $descriptionold = trim($::FORM{descriptionold} || '');
@@ -810,6 +840,8 @@ if ($action eq 'update') {
my $milestoneurlold = trim($::FORM{milestoneurlold} || '');
my $votesperuser = trim($::FORM{votesperuser} || 0);
my $votesperuserold = trim($::FORM{votesperuserold} || 0);
my $userregexp = trim($::FORM{userregexp} || '');
my $userregexpold = trim($::FORM{userregexpold} || '');
my $maxvotesperbug = trim($::FORM{maxvotesperbug} || 0);
my $maxvotesperbugold = trim($::FORM{maxvotesperbugold} || 0);
my $votestoconfirm = trim($::FORM{votestoconfirm} || 0);
@@ -829,16 +861,15 @@ if ($action eq 'update') {
# Note that the order of this tests is important. If you change
# them, be sure to test for WHERE='$product' or WHERE='$productold'
SendSQL("LOCK TABLES bugs WRITE,
components WRITE,
products WRITE,
versions WRITE,
groups WRITE,
profiles WRITE,
milestones WRITE,
user_group_map WRITE,
product_group_map WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES bugs WRITE,
components WRITE,
products WRITE,
versions WRITE,
groups WRITE,
profiles WRITE,
milestones WRITE");
}
if ($disallownew ne $disallownewold) {
$disallownew ||= 0;
@@ -851,7 +882,9 @@ if ($action eq 'update') {
if ($description ne $descriptionold) {
unless ($description) {
print "Sorry, I can't delete the description.";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
@@ -868,6 +901,75 @@ if ($action eq 'update') {
print "Updated mile stone URL.<BR>\n";
}
# Added -JMR, 2/16/00
if (Param("usebuggroups") && $userregexp ne $userregexpold) {
# This will take a little bit of work here, since there may not be
# an existing bug group for this product, and we will also have to
# update users groupsets.
# First we find out if there's an existing group for this product, and
# get its bit if there is.
SendSQL("SELECT group_bit " .
"FROM groups " .
"WHERE name = " . SqlQuote($productold));
my $bit = FetchOneColumn();
if($bit) {
# Group exists, so we do an update statement.
SendSQL("UPDATE groups " .
"SET userregexp = " . SqlQuote($userregexp) . " " .
"WHERE name = " . SqlQuote($productold));
print "Updated user regexp for bug group.<BR>\n";
} else {
# Group doesn't exist. Let's make it, the same way as we make a
# group for a new product above.
SendSQL("SELECT MAX(group_bit) FROM groups");
my $tmp_bit = FetchOneColumn();
if($tmp_bit < 256) {
$bit = 256;
} else {
$bit = $tmp_bit * 2;
}
SendSQL("INSERT INTO groups " .
"(group_bit, name, description, isbuggroup, userregexp) " .
"values (" . $bit . ", " .
SqlQuote($productold) . ", " .
SqlQuote($productold . " Bugs Access") . ", " .
"1, " .
SqlQuote($userregexp) . ")");
print "Created bug group.<BR>\n";
}
# And now we have to update the profiles again to add any users who
# match the new regexp to the group. I'll do this the same way as
# when I create a new group above. Note that I'm not taking out
# users who matched the old regexp and not the new one; that would
# be insanely messy. Use the group administration page for that
# instead.
SendSQL("SELECT login_name FROM profiles");
my @login_list = ();
my $this_login;
while($this_login = FetchOneColumn()) {
push @login_list, $this_login;
}
my $updated_profiles = 0;
foreach $this_login (@login_list) {
if($this_login =~ /$userregexp/) {
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles " .
"SET groupset = groupset | " . $bit . " " .
"WHERE login_name = " . SqlQuote($this_login));
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles " .
"SET groupset = groupset | int8(" . $bit . ") " .
"WHERE login_name = " . SqlQuote($this_login));
}
$updated_profiles = 1;
}
}
if($updated_profiles) {
print "Added users matching regexp to group.<BR>\n";
}
}
if ($votesperuser ne $votesperuserold) {
SendSQL("UPDATE products
SET votesperuser=$votesperuser
@@ -901,7 +1003,9 @@ if ($action eq 'update') {
" AND product = " . SqlQuote($productold));
if (!FetchOneColumn()) {
print "Sorry, the milestone $defaultmilestone must be defined first.";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
@@ -917,13 +1021,17 @@ if ($action eq 'update') {
if ($product ne $productold) {
unless ($product) {
print "Sorry, I can't delete the product name.";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
if (TestProduct($product)) {
print "Sorry, product name '$product' is already in use.";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;
}
@@ -933,47 +1041,21 @@ if ($action eq 'update') {
SendSQL("UPDATE products SET product=$qp WHERE product=$qpold");
SendSQL("UPDATE versions SET program=$qp WHERE program=$qpold");
SendSQL("UPDATE milestones SET product=$qp WHERE product=$qpold");
# 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
# update statement will do nothing, so no harm done. -JMR, 3/8/00
SendSQL("UPDATE groups " .
"SET name=$qp, " .
"description=".SqlQuote($product." Bugs Access")." ".
"WHERE name=$qpold");
print "Updated product name.<BR>\n";
}
# Update group permissions for this product
my %newgroups = ();
foreach (keys %::FORM) {
next unless /^group_/;
detaint_natural($::FORM{$_});
$newgroups{$::FORM{$_}} = 1;
}
my %groupsbelong = ();
SendSQL("select groups.group_id from groups, user_group_map " .
"where groups.group_id = user_group_map.group_id " .
"and groups.isbuggroup = 1 " .
"and user_group_map.user_id = $userid");
while (my ($groupid) = FetchSQLData()) {
$groupsbelong{$groupid} = 1;
}
my %oldgroups = ();
SendSQL("select groups.group_id from groups, product_group_map " .
"where groups.group_id = product_group_map.group_id " .
"and product_group_map.product_id = $productid");
while (my ($groupid) = FetchSQLData()) {
$oldgroups{$groupid} = 1;
}
foreach my $groupid (keys %groupsbelong) {
if (!$oldgroups{$groupid} && $newgroups{$groupid}) {
SendSQL("INSERT INTO product_group_map (product_id, group_id) VALUES ($productid, $groupid)");
}
if ($oldgroups{$groupid} && !$newgroups{$groupid}) {
SendSQL("DELETE FROM product_group_map WHERE product_id = $productid AND group_id = $groupid");
}
}
print "Updated product permissions<br>\n";
unlink "data/versioncache";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
if ($checkvotes) {
print "Checking existing votes in this product for anybody who now has too many votes.";

View File

@@ -37,8 +37,16 @@ require "globals.pl";
# Shut up misguided -w warnings about "used only once". "use vars" just
# doesn't work for me.
sub sillyness {
my $zz;
$zz = $::userid;
$zz = $::superusergroupset;
}
my $editall;
my %blessgroupset = (); # Groups that this viewer can bless someone into
my $opblessgroupset = '72057594037927935'; # This is all 64 bits.
# TestUser: just returns if the specified user does exists
# CheckUser: same check, optionally emit an error text
@@ -47,7 +55,7 @@ sub TestUser ($)
{
my $user = shift;
# does the product exist?
# does the user exist?
SendSQL("SELECT login_name
FROM profiles
WHERE login_name=" . SqlQuote($user));
@@ -58,7 +66,7 @@ sub CheckUser ($)
{
my $user = shift;
# do we have a product?
# do we have a user?
unless ($user) {
print "Sorry, you haven't specified a user.";
PutTrailer();
@@ -90,9 +98,9 @@ sub EmitElement ($$)
# Displays the form to edit a user parameters
#
sub EmitFormElements ($$$$$$)
sub EmitFormElements ($$$$$)
{
my ($user, $realname, $disabledtext, $groups_belong, $can_bless, $adminuser) = @_;
my ($user, $realname, $groupset, $blessgroupset, $disabledtext) = @_;
print " <TH ALIGN=\"right\">Login name:</TH>\n";
EmitElement("user", $user);
@@ -126,84 +134,58 @@ sub EmitFormElements ($$$$$$)
if($user ne "") {
# Select admin access if user has 'edituser' privileges
if ($editall) {
my $adminchecked = $adminuser ? "CHECKED" : "";
print "</TR><TR><TH VALIGN=TOP ALIGN=RIGHT>Admin User:</TH>\n";
print "<TD><INPUT TYPE=\"checkbox\" NAME=\"admin\" VALUE=\"1\" $adminchecked></TD>\n";
print "</TR><TR><TH VALIGN=TOP ALIGN=RIGHT>Group Access:</TH><TD><TABLE><TR>";
if ($::driver eq 'mysql') {
SendSQL("SELECT group_bit, name, description, (group_bit & $groupset) != 0, " .
" (group_bit & $blessgroupset) != 0 " .
"FROM groups " .
"WHERE (group_bit & $opblessgroupset) != 0 AND isbuggroup != 0 " .
"ORDER BY name");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit, name, description, (group_bit & int8($groupset)) != 0, " .
" (group_bit & int8($blessgroupset)) != 0 " .
"FROM groups " .
"WHERE (group_bit & int8($opblessgroupset)) != 0 AND isbuggroup != 0 " .
"ORDER BY name");
}
# Edit bug groups
print "</TR><TR><TH VALIGN=TOP ALIGN=RIGHT>Group Access:</TH><TD>\n<TABLE><TR>\n";
SendSQL("SELECT group_id, name, description FROM groups " .
"WHERE isbuggroup = 1 ORDER BY name");
if (MoreSQLData()) {
if ($editall) {
print "<TD COLSPAN=3 ALIGN=LEFT><B>Can turn this on for other users</B></TD>\n";
print "</TR><TR>\n";
print "<TD ALIGN=CENTER><B>|</B></TD>\n";
} else {
print "<TD COLSPAN=3></TD>\n</TR><TR>\n<TD></TD>\n";
print "<TD COLSPAN=3 ALIGN=LEFT><B>Can turn this bit on for other users</B></TD>\n";
print "</TR><TR>\n<TD ALIGN=CENTER><B>|</B></TD>\n";
}
print "<TD COLSPAN=2 ALIGN=LEFT><B>User is a member of these groups</B></TD>\n";
}
while (MoreSQLData()) {
my ($groupid, $name, $description) = FetchSQLData();
my ($bless_checked, $group_checked);
my $canedit = 0;
print "</TR><TR>\n";
if ($editall) {
$bless_checked = $can_bless->{$groupid} ? "CHECKED" : "";
print "<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"bless_$name\" $bless_checked VALUE=\"$groupid\"></TD>";
} else {
print "<TD></TD>\n";
}
if ($editall || $blessgroupset{$groupid}) {
$group_checked = $groups_belong->{$groupid} ? "CHECKED" : "";
print "<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"group_$name\" $group_checked VALUE=\"$groupid\"></TD>";
$canedit = 1;
} else {
print "<TD></TD>\n";
}
if ($canedit) {
while (MoreSQLData()) {
my ($bit,$name,$description,$checked,$blchecked) = FetchSQLData();
print "</TR><TR>\n";
if ($editall) {
$blchecked = ($blchecked) ? "CHECKED" : "";
print "<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"blbit_$name\" $blchecked VALUE=\"$bit\"></TD>";
}
$checked = ($checked) ? "CHECKED" : "";
print "<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"bit_$name\" $checked VALUE=\"$bit\"></TD>";
print "<TD><B>" . ucfirst($name) . "</B>: $description</TD>\n";
}
}
print "</TR></TABLE></TD>\n";
# Non non bug groups
print "</TR><TR><TH VALIGN=TOP ALIGN=RIGHT>Privileges:</TH><TD>\n<TABLE><TR>\n";
SendSQL("SELECT group_id, name, description " .
"FROM groups " .
"WHERE isbuggroup != 1 " .
"ORDER BY name");
print "</TR><TR><TH VALIGN=TOP ALIGN=RIGHT>Privileges:</TH><TD><TABLE><TR>";
if ($::driver eq 'mysql') {
SendSQL("SELECT group_bit, name, description, (group_bit & $groupset) != 0, " .
" (group_bit & $blessgroupset) != 0 " .
"FROM groups " .
"WHERE (group_bit & $opblessgroupset) != 0 AND isbuggroup = 0 " .
"ORDER BY name");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit, name, description, (group_bit & int8($groupset)) != 0, " .
" (group_bit & int8($blessgroupset)) != 0 " .
"FROM groups " .
"WHERE (group_bit & int8($opblessgroupset)) != 0 AND isbuggroup = 0 " .
"ORDER BY name");
}
if (MoreSQLData()) {
if ($editall) {
print "<TD COLSPAN=3 ALIGN=LEFT><B>Can turn this on for other users</B></TD>\n";
print "</TR><TR>\n";
print "<TD ALIGN=CENTER><B>|</B></TD>\n";
} else {
print "<TD COLSPAN=3></TD>\n</TR><TR>\n<TD></TD>\n";
}
print "<TD COLSPAN=2 ALIGN=LEFT><B>User has these privileges.</B></TD>\n";
}
while (MoreSQLData()) {
my ($groupid, $name, $description) = FetchSQLData();
my ($bless_checked, $group_checked);
my $canedit = 0;
print "</TR><TR>\n";
if ($editall) {
$bless_checked = $can_bless->{$groupid} ? "CHECKED" : "";
print "<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"bless_$name\" $bless_checked VALUE=\"$groupid\"></TD>";
} else {
print "<TD></TD>\n";
}
if ($editall || $blessgroupset{$groupid}) {
$group_checked = $groups_belong->{$groupid} ? "CHECKED" : "";
print "<TD ALIGN=CENTER><INPUT TYPE=CHECKBOX NAME=\"group_$name\" $group_checked VALUE=\"$groupid\"></TD>";
$canedit = 1;
print "<TD COLSPAN=3 ALIGN=LEFT><B>Can turn this bit on for other users</B></TD>\n";
print "</TR><TR>\n<TD ALIGN=CENTER><B>|</B></TD>\n";
}
print "<TD COLSPAN=2 ALIGN=LEFT><B>User has these privileges</B></TD>\n";
while (MoreSQLData()) {
@@ -265,19 +247,16 @@ sub PutTrailer (@)
# Preliminary checks:
#
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
$editall = UserInGroup($userid, "editusers");
$editall = UserInGroup("editusers");
if (!$editall) {
SendSQL("SELECT group_id FROM user_group_map WHERE user_id = $userid AND canbless = 1");
while (my @row = FetchSQLData()) {
$blessgroupset{$row[0]} = 1;
}
unless (%blessgroupset) {
SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $::userid");
$opblessgroupset = FetchOneColumn();
if (!$opblessgroupset) {
PutHeader("Not allowed");
print "Sorry, you aren't a member of the 'editusers' group, and you\n";
print "don't have permissions to put people in or out of any group.\n";
@@ -333,34 +312,23 @@ if ($action eq 'list') {
my $query = "";
my $matchstr = $::FORM{'matchstr'};
if (exists $::FORM{'matchtype'}) {
$query = "SELECT login_name,realname,disabledtext " .
"FROM profiles " .
"WHERE login_name ";
if ($::FORM{'matchtype'} eq 'substr') {
$query .= "like";
$matchstr = '%' . $matchstr . '%';
} elsif ($::FORM{'matchtype'} eq 'regexp') {
$query .= "regexp";
$matchstr = '.'
unless $matchstr;
} elsif ($::FORM{'matchtype'} eq 'notregexp') {
$query .= "not regexp";
$matchstr = '.'
unless $matchstr;
} else {
die "Unknown match type";
}
$query .= SqlQuote($matchstr) . " ORDER BY login_name";
$query = "SELECT login_name,realname,disabledtext " .
"FROM profiles WHERE ";
if ($::FORM{'matchtype'} eq 'substr') {
$query .= "login_name like '%" . $::FORM{'matchstr'} . "%'";
} elsif ($::FORM{'matchtype'} eq 'regexp') {
$query .= SqlRegEx("login_name", SqlQuote($::FORM{'matchstr'}));
} elsif ($::FORM{'matchtype'} eq 'notregexp') {
$query .= SqlRegEx("login_name", SqlQuote($::FORM{'matchstr'}), "not");
} else {
die "Unknown match type";
}
$query .= " ORDER BY login_name";
} elsif (exists $::FORM{'query'}) {
$query = "SELECT login_name,realname,disabledtext " .
"FROM profiles WHERE " . $::FORM{'query'} . " ORDER BY login_name";
} elsif (exists $::FORM{'group'}) {
$query = "SELECT login_name,realname,disabledtext " .
"FROM profiles, user_group_map " .
"WHERE profiles.userid = user_group_map.user_id " .
"AND user_group_map.group_id = " . $::FORM{'group'} . " ORDER BY login_name";
$query = "SELECT login_name,realname,disabledtext " .
"FROM profiles WHERE " . $::FORM{'query'} . " ORDER BY login_name";
} else {
die "Missing parameters";
die "Missing parameters";
}
SendSQL($query);
@@ -439,7 +407,7 @@ if ($action eq 'add') {
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements('', '', '', '', '', 0);
EmitFormElements('', '', 0, 0, '');
print "</TR></TABLE>\n<HR>\n";
print "<INPUT TYPE=SUBMIT VALUE=\"Add\">\n";
@@ -512,40 +480,38 @@ if ($action eq 'new') {
# their initial group membership.
# We also keep a list of groups the user was added to for display on the
# confirmation page.
my @groupidlist = ();
my @groupnamelist = ();
SendSQL("select group_id, name, userregexp from groups where userregexp != ''");
my $bits = "0";
my @grouplist = ();
SendSQL("select group_bit, name, userregexp from groups where userregexp != ''");
while (MoreSQLData()) {
my @row = FetchSQLData();
if ($user =~ m/$row[2]/i) {
push(@groupidlist, $row[0]);
push(@groupnamelist, $row[1]);
$bits .= "+ $row[0]"; # Silly hack to let MySQL do the math,
# not Perl, since we're dealing with 64
# bit ints here, and I don't *think* Perl
# does that.
push(@grouplist, $row[1]);
}
}
# Add the new user
SendSQL("INSERT INTO profiles ( " .
"login_name, cryptpassword, realname, disabledtext" .
"login_name, cryptpassword, realname, groupset, " .
"disabledtext" .
" ) VALUES ( " .
SqlQuote($user) . "," .
SqlQuote(Crypt($password)) . "," .
SqlQuote($realname) . "," .
$bits . "," .
SqlQuote($disabledtext) . ")" );
#+++ send e-mail away
# grab userid and place user in each of the groups they need to be
SendSQL("select userid from profiles where login_name = " . SqlQuote($user));
my $userid = FetchOneColumn();
foreach my $groupid ( @groupidlist ) {
SendSQL("INSERT INTO user_group_map (user_id, group_id) VALUES ($userid, $groupid)");
}
print "OK, done.<br>\n";
if($#groupidlist > -1) {
if($#grouplist > -1) {
print "New user added to these groups based on group regexps:\n";
print "<ul>\n";
foreach (@groupnamelist) {
foreach (@grouplist) {
print "<li>$_</li>\n";
}
print "</ul>\n";
@@ -583,9 +549,10 @@ if ($action eq 'del') {
CheckUser($user);
# display some data about the user
SendSQL("SELECT userid, realname FROM profiles
WHERE login_name=" . SqlQuote($user));
my ($theuserid, $realname) = FetchSQLData();
SendSQL("SELECT realname, groupset FROM profiles
WHERE login_name=" . SqlQuote($user));
my ($realname, $groupset) =
FetchSQLData();
$realname ||= "<FONT COLOR=\"red\">missing</FONT>";
print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>\n";
@@ -604,11 +571,17 @@ if ($action eq 'del') {
print "</TR><TR>\n";
print " <TD VALIGN=\"top\">Group set:</TD>\n";
print " <TD VALIGN=\"top\">";
SendSQL("SELECT name
FROM groups, user_group_map
WHERE groups.group_id = user_group_map.group_id
AND user_group_map.user_id = $theuserid
ORDER BY isbuggroup, name");
if ($::driver eq 'mysql') {
SendSQL("SELECT name
FROM groups
WHERE bit & $groupset = bit
ORDER BY isbuggroup, name");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT name
FROM groups
WHERE (group_bit & int8($groupset)) = group_bit
ORDER BY isbuggroup, name");
}
my $found = 0;
while ( MoreSQLData() ) {
my ($name) = FetchSQLData();
@@ -721,8 +694,6 @@ if ($action eq 'delete') {
WHERE login_name=" . SqlQuote($user));
SendSQL("DELETE FROM logincookies
WHERE userid=" . $userid);
SendSQL("DELETE FROM user_group_map
WHERE user_id=" . $userid);
print "User deleted.<BR>\n";
PutTrailer($localtrailer);
@@ -742,38 +713,23 @@ if ($action eq 'edit') {
CheckUser($user);
# get data of user
SendSQL("SELECT userid, realname, disabledtext
FROM profiles
WHERE login_name = " . SqlQuote($user));
my ($userid, $realname, $disabledtext) = FetchSQLData();
# find out which groups belong to
my $groups_belong = {};
SendSQL("SELECT group_id FROM user_group_map WHERE user_id = $userid");
while ( my @row = FetchSQLData() ) {
$groups_belong->{$row[0]} = 1;
}
# find out when groups this person can bless others into
my $bless_belong = {};
SendSQL("SELECT group_id FROM user_group_map WHERE user_id = $userid AND canbless = 1");
while ( my @row = FetchSQLData() ) {
$bless_belong->{$row[0]} = 1;
}
# Determine if this user is an administrator or not
SendSQL("SELECT admin FROM profiles WHERE userid = $userid");
my $adminuser = FetchOneColumn();
SendSQL("SELECT realname, groupset, blessgroupset, disabledtext
FROM profiles
WHERE login_name=" . SqlQuote($user));
my ($realname, $groupset, $blessgroupset,
$disabledtext) = FetchSQLData();
print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
EmitFormElements($user, $realname, $disabledtext, $groups_belong, $bless_belong, $adminuser);
EmitFormElements($user, $realname, $groupset, $blessgroupset, $disabledtext);
print "</TR></TABLE>\n";
print "<INPUT TYPE=HIDDEN NAME=\"userold\" VALUE=\"$user\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"realnameold\" VALUE=\"$realname\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"groupsetold\" VALUE=\"$groupset\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"blessgroupsetold\" VALUE=\"$blessgroupset\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"disabledtextold\" VALUE=\"" .
value_quote($disabledtext) . "\">\n";
print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"update\">\n";
@@ -800,80 +756,75 @@ if ($action eq 'update') {
my $password = $::FORM{password} || '';
my $disabledtext = trim($::FORM{disabledtext} || '');
my $disabledtextold = trim($::FORM{disabledtextold} || '');
my $groupsetold = trim($::FORM{groupsetold} || '0');
my $blessgroupsetold = trim($::FORM{blessgroupsetold} || '0');
my %groupset = ();
my %blessgroupset = ();
my $groupset = "0";
foreach (keys %::FORM) {
next unless /^group_/;
next unless /^bit_/;
#print "$_=$::FORM{$_}<br>\n";
detaint_natural($::FORM{$_}) || die "Groupset field tampered with";
$groupset{$::FORM{$_}} = 1;
$groupset .= " + $::FORM{$_}";
}
my $blessgroupset = "0";
foreach (keys %::FORM) {
next unless /^bless_/;
next unless /^blbit_/;
#print "$_=$::FORM{$_}<br>\n";
detaint_natural($::FORM{$_}) || die "Blessgroupset field tampered with";
$blessgroupset{$::FORM{$_}} = 1;
$blessgroupset .= " + $::FORM{$_}";
}
CheckUser($userold);
SendSQL("SELECT userid, admin FROM profiles WHERE login_name = " . SqlQuote($userold));
my ($userid, $isadmin) = FetchSQLData();
# Note that the order of this tests is important. If you change
# them, be sure to test for WHERE='$product' or WHERE='$productold'
if ($editall) {
if (defined $::FORM{'admin'} && $::FORM{'admin'} == 1 && !$isadmin) {
SendSQL("UPDATE profiles SET admin = 1 WHERE login_name = " . SqlQuote($userold));
print "Added administrator status.<br>\n";
} elsif (!defined $::FORM{'admin'} && $::FORM{'admin'} == 0 && $isadmin) {
SendSQL("UPDATE profiles SET admin = 0 WHERE login_name = " . SqlQuote($userold));
print "Removed administrator status.<br>\n";
}
if ($groupset ne $groupsetold) {
SendSQL("SELECT groupset FROM profiles WHERE login_name=" .
SqlQuote($userold));
$groupsetold = FetchOneColumn();
# Updated, 5/7/00, Joe Robins
# We don't want to change the groupset of a superuser.
if($groupsetold eq $::superusergroupset) {
print "Cannot change permissions of superuser.\n";
} else {
if ($::driver eq 'mysql') {
SendSQL("UPDATE profiles
SET groupset =
groupset - (groupset & $opblessgroupset) +
(($groupset) & $opblessgroupset)
WHERE login_name=" . SqlQuote($userold));
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE profiles
SET groupset =
groupset - (groupset & int8($opblessgroupset)) +
((int8($groupset)) & int8($opblessgroupset))
WHERE login_name=" . SqlQuote($userold));
}
# I'm paranoid that someone who I give the ability to bless people
# will start misusing it. Let's log who blesses who (even though
# nothing actually uses this log right now).
my $fieldid = GetFieldID("groupset");
SendSQL("SELECT userid, groupset FROM profiles WHERE login_name=" .
SqlQuote($userold));
my $u;
($u, $groupset) = (FetchSQLData());
if ($groupset ne $groupsetold) {
SendSQL("INSERT INTO profiles_activity " .
"(userid,who,profiles_when,fieldid,oldvalue,newvalue) " .
"VALUES " .
"($u, $::userid, now(), $fieldid, " .
" '$groupsetold', '$groupset')");
}
print "Updated permissions.\n";
}
}
if (!$isadmin || $editall) {
my %groups = ();
my %oldgroupset = ();
my %oldblessgroupset = ();
SendSQL("SELECT group_id, canbless FROM user_group_map WHERE user_id = $userid");
while (my @row = FetchSQLData()) {
$oldgroupset{$row[0]} = 1;
$oldblessgroupset{$row[0]} = $row[1];
}
SendSQL("SELECT group_id FROM groups");
while (my @row = FetchSQLData()) {
$groups{$row[0]} = 1;
}
foreach my $groupid (keys %groups) {
if ($editall || $blessgroupset{$groupid}) {
if (!$oldgroupset{$groupid} && $groupset{$groupid}) {
SendSQL("INSERT INTO user_group_map (user_id, group_id, canbless) VALUES ($userid, $groupid, 0)");
}
if ($oldgroupset{$groupid} && !$groupset{$groupid}) {
SendSQL("DELETE FROM user_group_map WHERE user_id = $userid AND group_id = $groupid");
}
}
}
print "Updated permissions<br>\n";
foreach my $groupid (keys %groups) {
if ($editall) {
if (!$oldblessgroupset{$groupid} && $blessgroupset{$groupid}) {
SendSQL("UPDATE user_group_map SET canbless = 1 WHERE user_id = $userid AND group_id = $groupid");
}
if ($oldblessgroupset{$groupid} && !$blessgroupset{$groupid}) {
SendSQL("UPDATE user_group_map SET canbless = 0 WHERE user_id = $userid AND group_id = $groupid");
}
}
}
print "Updated ability to tweak permissions of other users.<br>\n";
} else {
print "Cannot change permissions of a superuser.<br>\n";
if ($editall && $blessgroupset ne $blessgroupsetold) {
SendSQL("UPDATE profiles
SET blessgroupset=" . $blessgroupset . "
WHERE login_name=" . SqlQuote($userold));
print "Updated ability to tweak permissions of other users.\n";
}
# Update the database with the user's new password if they changed it.

View File

@@ -74,7 +74,7 @@ sub TestVersion ($$)
{
my ($prod,$ver) = @_;
# does the product exist?
# does the version exist?
SendSQL("SELECT program,value
FROM versions
WHERE program=" . SqlQuote($prod) . " and value=" . SqlQuote($ver));
@@ -155,11 +155,11 @@ sub PutTrailer (@)
# Preliminary checks:
#
my $userid = confirm_login();
confirm_login();
print "Content-type: text/html\n\n";
unless (UserInGroup($userid, "editcomponents")) {
unless (UserInGroup("editcomponents")) {
PutHeader("Not allowed");
print "Sorry, you aren't a member of the 'editcomponents' group.\n";
print "And so, you aren't allowed to add, modify or delete versions.\n";
@@ -417,12 +417,13 @@ if ($action eq 'delete') {
CheckVersion($product,$version);
# lock the tables before we start to change everything:
SendSQL("LOCK TABLES attachments WRITE,
bugs WRITE,
bugs_activity WRITE,
versions WRITE,
dependencies WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES attachments WRITE,
bugs WRITE,
bugs_activity WRITE,
versions WRITE,
dependencies WRITE");
}
# According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y,
# so I have to iterate over bugs and delete all the indivial entries
@@ -458,7 +459,9 @@ if ($action eq 'delete') {
WHERE program=" . SqlQuote($product) . "
AND value=" . SqlQuote($version));
print "Version deleted.<P>\n";
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
unlink "data/versioncache";
PutTrailer($localtrailer);
@@ -512,21 +515,26 @@ if ($action eq 'update') {
# Note that the order of this tests is important. If you change
# them, be sure to test for WHERE='$version' or WHERE='$versionold'
SendSQL("LOCK TABLES bugs WRITE,
versions WRITE");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES bugs WRITE,
versions WRITE");
}
if ($version ne $versionold) {
unless ($version) {
print "Sorry, I can't delete the version text.";
PutTrailer($localtrailer);
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
exit;
}
if (TestVersion($product,$version)) {
print "Sorry, version '$version' is already in use.";
PutTrailer($localtrailer);
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
exit;
}
SendSQL("UPDATE bugs
@@ -541,7 +549,9 @@ if ($action eq 'update') {
unlink "data/versioncache";
print "Updated version.<BR>\n";
}
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
PutTrailer($localtrailer);
exit;

View File

@@ -35,7 +35,6 @@
use diagnostics;
use strict;
use lib qw(.);
require "CGI.pl";
@@ -52,7 +51,6 @@ use vars qw(
@legal_severity
%MFORM
%versions
%proddesc
);
# We have to connect to the database, even though we don't use it in this code,
@@ -61,21 +59,21 @@ use vars qw(
ConnectToDatabase();
# If we're using bug groups to restrict bug entry, we need to know who the
# user is right from the start.
my $userid = confirm_login();
# user is right from the start.
confirm_login() if (Param("usebuggroupsentry"));
if (!defined $::FORM{'product'}) {
GetVersionTable();
quietly_check_login();
my %products;
foreach my $p (@enterable_products) {
# If we're using bug groups to restrict entry on products, and
# this product is private to one or more bug groups and the user is not
# in one of those groups group, we don't want to include that product in this list.
next if !CanSeeProduct($userid, $p);
$products{$p} = $proddesc{$p};
if (!(Param("usebuggroupsentry")
&& GroupExists($p)
&& !UserInGroup($p)))
{
$products{$p} = $::proddesc{$p};
}
}
my $prodsize = scalar(keys %products);
@@ -217,19 +215,20 @@ sub pickos {
# default
return "other";
}
##############################################################################
# End of subroutines
##############################################################################
if (!CanSeeProduct($userid, $product)) {
print "Content-type: text/html\n\n";
PutHeader("Permission denied.", "Enter Bug", "This page lets you enter a new bug into Bugzilla.");
print "<H1>Permission denied.</H1>\n";
print "Sorry; you do not have the permissions necessary to enter\n";
print "a bug against this product.\n";
print "<P>\n";
PutFooter();
confirm_login() if (!(Param("usebuggroupsentry")));
# If the usebuggroupsentry parameter is set, 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))
{
DisplayError("Sorry; you do not have the permissions necessary to " .
"enter a bug against this product.\n");
exit;
}
@@ -243,16 +242,19 @@ if (lsearch(\@::enterable_products, $product) == -1) {
if (0 == @{$::components{$product}}) {
my $error = "Sorry; there needs to be at least one component for this " .
"product in order to create a new bug. ";
if (UserInGroup($userid, 'editcomponents')) {
if (UserInGroup('editcomponents')) {
$error .= "<a href=\"editcomponents.cgi\">" .
"Create a new component</a>\n";
} else {
}
else {
$error .= "Please contact " . Param("maintainer") . ", detailing " .
"the product in which you tried to create a new bug.\n";
}
}
DisplayError($error);
exit;
} elsif (1 == @{$::components{$product}}) {
}
elsif (1 == @{$::components{$product}}) {
# Only one component; just pick it.
$::FORM{'component'} = $::components{$product}->[0];
}
@@ -305,57 +307,83 @@ $default{'version'} = $vars->{'version'}->[$#{$vars->{'version'}}];
# There must be at least one status in @status.
my @status = "NEW";
if (UserInGroup($userid, "editbugs") || UserInGroup($userid, "canconfirm")) {
SendSQL("SELECT votestoconfirm FROM products WHERE product = " . SqlQuote($product));
if (UserInGroup("editbugs") || UserInGroup("canconfirm")) {
SendSQL("SELECT votestoconfirm FROM products WHERE product = " .
SqlQuote($product));
push(@status, $unconfirmedstate) if (FetchOneColumn());
}
$vars->{'bug_status'} = \@status;
$default{'bug_status'} = $status[0];
my %productgroups = ();
my @groups;
# 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.
if ($::usergroupset ne '0') {
# First we get the bit and description for the group.
my $group_bit = '0';
SendSQL("SELECT product_group_map.group_id FROM products, product_group_map " .
"WHERE products.product_id = product_group_map.product_id " .
"AND products.product = " . SqlQuote($product) .
" ORDER BY products.product_id");
while (MoreSQLData()) {
my ($prodid) = FetchSQLData();
$productgroups{$prodid} = 1;
}
SendSQL("SELECT groups.group_id, groups.description " .
"FROM groups, user_group_map " .
"WHERE groups.group_id = user_group_map.group_id " .
"AND user_group_map.user_id = $userid " .
"AND isbuggroup != 0 AND isactive = 1 ORDER BY description");
while (MoreSQLData()) {
my ($group_id, $description) = (FetchSQLData());
my $check;
# If this is the group for this product, make it checked.
if (formvalue("maketemplate") eq "Remember values as bookmarkable template") {
# If this is a bookmarked template, then we only want to set the
# bit for those bits set in the template.
$check = formvalue("group-$group_id", 0);
} elsif ($productgroups{$group_id}) {
$check = 1;
if(Param("usebuggroups") && GroupExists($product)) {
if ($::driver eq 'mysql') {
SendSQL("SELECT bit FROM groups ".
"WHERE name = " . SqlQuote($product) . " " .
"AND isbuggroup != 0");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit FROM groups ".
"WHERE name = " . SqlQuote($product) . " " .
"AND isbuggroup != 0");
}
($group_bit) = FetchSQLData();
}
my $group =
{
'bit' => $group_id,
'checked' => $check ,
'description' => $description
};
if ($::driver eq 'mysql') {
SendSQL("SELECT bit, name, description FROM groups " .
"WHERE (group_bit & $::usergroupset) != 0 " .
" AND isbuggroup != 0 AND isactive = 1 ORDER BY description");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit, name, description FROM groups " .
"WHERE (group_bit & int8($::usergroupset)) != 0 " .
" AND isbuggroup != 0 AND isactive = 1 ORDER BY description");
}
push @groups, $group;
}
my @groups;
while (MoreSQLData()) {
my ($bit, $prodname, $description) = FetchSQLData();
# Don't want to include product groups other than this product.
next unless($prodname eq $product ||
!defined($::proddesc{$prodname}));
my $check;
# If this is the group for this product, make it checked.
if(formvalue("maketemplate") eq
"Remember values as bookmarkable template")
{
# If this is a bookmarked template, then we only want to set the
# bit for those bits set in the template.
$check = formvalue("bit-$bit", 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_bit == $bit);
}
my $group =
{
'bit' => $bit ,
'checked' => $check ,
'description' => $description
};
push @groups, $group;
}
$vars->{'group'} = \@groups;
}
$vars->{'group'} = \@groups;
$vars->{'default'} = \%default;
my $format = ValidateOutputFormat($::FORM{'format'}, "create", "bug/create");

View File

@@ -54,8 +54,9 @@ sub globals_pl_sillyness {
$zz = @main::milestoneurl;
$zz = %main::proddesc;
$zz = @main::prodmaxvotes;
$zz = $main::superusergroupset;
$zz = $main::template;
$zz = $main::template;
$zz = $main::userid;
$zz = $main::vars;
}
@@ -65,11 +66,15 @@ sub globals_pl_sillyness {
#
$::db_host = "localhost";
$::db_port = 3306;
$::db_port = 5432;
$::db_name = "bugs";
$::db_user = "bugs";
$::db_pass = "";
# Set this your database driver
# $::driver = "mysql";
$::driver = "Pg";
do 'localconfig';
use DBI;
@@ -105,6 +110,10 @@ $::defaultqueryname = "(Default query)";
$::unconfirmedstate = "UNCONFIRMED";
$::dbwritesallowed = 1;
# Adding a global variable for the value of the superuser groupset.
# Joe Robins, 7/5/00
$::superusergroupset = "72057594037927935";
#sub die_with_dignity {
# my ($err_msg) = @_;
# print $err_msg;
@@ -132,16 +141,25 @@ sub ConnectToDatabase {
$name = Param("shadowdb");
$::dbwritesallowed = 0;
}
$::db = DBI->connect("DBI:mysql:host=$::db_host;database=$name;port=$::db_port", $::db_user, $::db_pass)
my $connectstring = "DBI:$::driver:";
if ($::driver eq 'mysql') {
$connectstring .= "database=$name;port=$::db_port";
} elsif ($::driver eq 'Pg') {
$connectstring .= "dbname=$name";
}
$connectstring .= ";host=$::db_host";
$::db = DBI->connect($connectstring, $::db_user, $::db_pass)
|| die "Bugzilla is currently broken. Please try again later. " .
"If the problem persists, please contact " . Param("maintainer") .
". The error you should quote is: " . $DBI::errstr;
"If the problem persists, please contact " . Param("maintainer") .
". The error you should quote is: " . $DBI::errstr;
}
}
sub ReconnectToShadowDatabase {
if (Param("shadowdb") && Param("queryagainstshadowdb")) {
SendSQL("USE " . Param("shadowdb"));
if ($::driver eq 'mysql') {
SendSQL("USE " . Param("shadowdb"));
}
$::dbwritesallowed = 0;
}
}
@@ -319,8 +337,74 @@ sub FetchOneColumn {
return $row[0];
}
# subroutine: SqlRegEx
# description: Outputs SQL syntax for doing regular expressions searches in format
# suitable for a given database.
# Params: $field = name of db field regular expression applied against (scalar)
# $pattern = regular express search pattern (scalar)
# $not = return if not within search patter (scalar)
# Returns: formatted SQL for regular expression search (scalar)
sub SqlRegEx {
my ($field, $pattern, $not) = @_;
if ($::driver eq 'mysql') {
if (!$not) {
return " LOWER($field) REGEXP $pattern ";
} else {
return " LOWER($field) NOT REGEXP $pattern ";
}
} elsif ($::driver eq 'Pg') {
if (!$not) {
return " LOWER($field) ~ $pattern ";
} else {
return " LOWER($field) !~ $pattern ";
}
}
}
# subroutine: SqlStrSearch
# description: Outputs SQL syntax for doing string searches in format
# suitable for a given database.
# Params: $field = name of db field containing string search for (scalar)
# $str = string to search for (scalar)
# $lower = whether the search is case sensitive or not (scalar)
# $not = return SQL for when string is NOT in searched field (scalar)
# Returns: formatted SQL for regular expression search (scalar)
sub SqlStrSearch {
my ($field, $str, $lower, $not) = @_;
if ($::driver eq 'mysql') {
if (!$lower) {
if (!$not) {
return " INSTR($field, $str) != 0 ";
} else {
return " INSTR($field, $str) = 0 ";
}
} else {
if (!$not) {
return " INSTR(LOWER($field), " . lc($str) . ") != 0 ";
} else {
return " INSTR(LOWER($field), " . lc($str) . ") = 0 ";
}
}
} elsif ($::driver eq 'Pg') {
if (!$lower) {
if (!$not) {
return " STRPOS($field, $str) != 0 ";
} else {
return " STRPOS($field, $str) = 0 ";
}
} else {
if (!$not) {
return " STRPOS(LOWER($field), " . lc($str) . ") != 0 ";
} else {
return " STRPOS(LOWER($field), " . lc($str) . ") = 0 ";
}
}
}
}
@::default_column_list = ("severity", "priority", "platform", "owner",
"status", "resolution", "summary");
@@ -346,9 +430,13 @@ sub GetFieldID {
my $fieldid = FetchOneColumn();
if (!$fieldid) {
my $q = SqlQuote($f);
SendSQL("REPLACE INTO fielddefs (name, description) VALUES ($q, $q)");
SendSQL("SELECT LAST_INSERT_ID()");
$fieldid = FetchOneColumn();
if ($::driver eq 'mysql') {
SendSQL("REPLACE INTO fielddefs (name, description) VALUES ($q, $q)");
} elsif ($::driver eq 'Pg') {
SendSQL("INSERT INTO fielddefs (name, description, sortkey) " .
"VALUES ($q, $q, 1)");
}
$fieldid = CurrId("fielddefs_fieldid_seq");
}
return $fieldid;
}
@@ -492,12 +580,21 @@ sub GenerateVersionTable {
}
@::log_columns = (sort(@::log_columns));
@::legal_priority = SplitEnumType($cols->{"priority,type"});
@::legal_severity = SplitEnumType($cols->{"bug_severity,type"});
@::legal_platform = SplitEnumType($cols->{"rep_platform,type"});
@::legal_opsys = SplitEnumType($cols->{"op_sys,type"});
@::legal_bug_status = SplitEnumType($cols->{"bug_status,type"});
@::legal_resolution = SplitEnumType($cols->{"resolution,type"});
if ($::driver eq 'mysql') {
@::legal_priority = SplitEnumType($cols->{"priority,type"});
@::legal_severity = SplitEnumType($cols->{"bug_severity,type"});
@::legal_platform = SplitEnumType($cols->{"rep_platform,type"});
@::legal_opsys = SplitEnumType($cols->{"op_sys,type"});
@::legal_bug_status = SplitEnumType($cols->{"bug_status,type"});
@::legal_resolution = SplitEnumType($cols->{"resolution,type"});
} elsif ($::driver eq 'Pg') {
@::legal_priority = SplitTableValues("priority");
@::legal_severity = SplitTableValues("bug_severity");
@::legal_platform = SplitTableValues("rep_platform");
@::legal_opsys = SplitTableValues("op_sys");
@::legal_bug_status = SplitTableValues("bug_status");
@::legal_resolution = SplitTableValues("resolution");
}
# 'settable_resolution' is the list of resolutions that may be set
# directly by hand in the bug form. Start with the list of legal
@@ -669,39 +766,32 @@ sub InsertNewUser {
my $password = GenerateRandomPassword();
my $cryptpassword = Crypt($password);
PushGlobalSQLState();
# Insert the new user record into the database.
$username = SqlQuote($username);
$realname = SqlQuote($realname);
$cryptpassword = SqlQuote($cryptpassword);
SendSQL("INSERT INTO profiles (login_name, realname, cryptpassword)
VALUES ($username, $realname, $cryptpassword)");
# Find the user's new id for adding to groups
SendSQL("select LAST_INSERT_ID()");
my $userid = FetchOneColumn();
# Determine what groups the user should be in by default
# and add them to those groups.
SendSQL("select group_id, userregexp from groups where userregexp != ''");
my @groups = ();
PushGlobalSQLState();
SendSQL("select group_bit, userregexp from groups where userregexp != ''");
my $groupset = "0";
while (MoreSQLData()) {
my @row = FetchSQLData();
# Modified -Joe Robins, 2/17/00
# Making this case insensitive, since usernames are email addresses,
# and could be any case.
if ($username =~ m/$row[1]/i) {
push(@groups, $row[0]);
$groupset .= "+ $row[0]"; # Silly hack to let MySQL do the math,
# not Perl, since we're dealing with 64
# bit ints here, and I don't *think* Perl
# does that.
}
}
foreach my $groupid (@groups) {
SendSQL("INSERT INTO user_group_map VALUES ($userid, $groupid)");
}
# Insert the new user record into the database.
$username = SqlQuote($username);
$realname = SqlQuote($realname);
$cryptpassword = SqlQuote($cryptpassword);
SendSQL("INSERT INTO profiles (login_name, realname, cryptpassword, groupset)
VALUES ($username, $realname, $cryptpassword, $groupset)");
PopGlobalSQLState();
# Return the password to the calling code so it can be included
# in an email sent to the user.
return $password;
@@ -743,13 +833,96 @@ sub GenerateRandomPassword {
return $password;
}
sub SelectVisible {
my ($query, $userid, $usergroupset) = @_;
# Run the SQL $query with the additional restriction that
# the bugs can be seen by $userid. $usergroupset is provided
# as an optimisation when this is already known, eg from CGI.pl
# If not present, it will be obtained from the db.
# Assumes that 'bugs' is mentioned as a table name. You should
# also make sure that bug_id is qualified bugs.bug_id!
# Your query must have a WHERE clause. This is unlikely to be a problem.
# Also, note that mySQL requires aliases for tables to be locked, as well
# This means that if you change the name from selectVisible_cc (or add
# additional tables), you will need to update anywhere which does a
# LOCK TABLE, and then calls routines which call this
$usergroupset = 0 unless $userid;
unless (defined($usergroupset)) {
PushGlobalSQLState();
SendSQL("SELECT groupset FROM profiles WHERE userid = $userid");
$usergroupset = FetchOneColumn();
PopGlobalSQLState();
}
# Users are authorized to access bugs if they are a member of all
# groups to which the bug is restricted. User group membership and
# bug restrictions are stored as bits within bitsets, so authorization
# can be determined by comparing the intersection of the user's
# bitset with the bug's bitset. If the result matches the bug's bitset
# the user is a member of all groups to which the bug is restricted
# and is authorized to access the bug.
# A user is also authorized to access a bug if she is the reporter,
# or member of the cc: list of the bug and the bug allows users in those
# roles to see the bug. The boolean fields reporter_accessible and
# cclist_accessible identify whether or not those roles can see the bug.
# Bit arithmetic is performed by MySQL instead of Perl because bitset
# fields in the database are 64 bits wide (BIGINT), and Perl installations
# may or may not support integers larger than 32 bits. Using bitsets
# and doing bitset arithmetic is probably not cross-database compatible,
# however, so these mechanisms are likely to change in the future.
my $replace = " ";
if ($userid) {
$replace .= ", bugs AS selectVisible_bugs LEFT JOIN cc selectVisible_cc ON
selectVisible_bugs.bug_id = selectVisible_cc.bug_id AND
selectVisible_cc.who = $userid "
}
if ($::driver eq 'mysql') {
$replace .= "WHERE ((bugs.groupset & $usergroupset) = bugs.groupset ";
} elsif ($::driver eq 'Pg') {
$replace .= "WHERE ((bugs.groupset & int8($usergroupset)) = bugs.groupset ";
}
if ($userid) {
# There is a mysql bug affecting v3.22 and 3.23 (at least), where this will
# cause all rows to be returned! We work arround this by adding an not isnull
# test to the JOINed cc table. See http://lists.mysql.com/cgi-ez/ezmlm-cgi?9:mss:11417
# Its needed, even though it shouldn't be
$replace .= "OR (bugs.reporter_accessible = 1 AND bugs.reporter = $userid) ";
if ($::driver eq 'mysql') {
$replace .= "
OR (bugs.cclist_accessible = 1 AND selectVisible_cc.who = $userid AND not isnull(selectVisible_cc.who))";
} elsif ($::driver eq 'Pg') {
$replace .= "
OR (bugs.cclist_accessible = 1 AND selectVisible_cc.who = $userid AND selectVisible_cc.who IS NOT NULL)";
}
}
$replace .= ") AND ";
$query =~ s/\sWHERE\s/$replace/i;
return $query;
}
sub CanSeeBug {
my ($bugs, $userid) = @_;
# Note that we pass in the usergroupset, since this is known
# in most cases (ie viewing bugs). Maybe make this an optional
# parameter?
my ($bugs, $userid, $usergroupset) = @_;
my %cansee;
my @buglist;
my @groups;
if(ref($bugs)) {
if (ref($bugs)) {
@buglist = @{$bugs};
} else {
push(@buglist, $bugs);
@@ -758,86 +931,83 @@ sub CanSeeBug {
if (@buglist < 1) {
return 0;
}
# Query the database for the bug, retrieving a boolean value that
# represents whether or not the user is authorized to access the bug.
# Users are authorized to access bugs if they are a member of all
# groups to which the bug is restricted. User group membership and
# bug restrictions are stored as bits within bitsets, so authorization
# can be determined by comparing the intersection of the user's
# bitset with the bug's bitset. If the result matches the bug's bitset
# the user is a member of all groups to which the bug is restricted
# and is authorized to access the bug.
# A user is also authorized to access a bug if she is the reporter,
# assignee, QA contact, or member of the cc: list of the bug and the bug
# allows users in those roles to see the bug. The boolean fields
# reporter_accessible, assignee_accessible, qacontact_accessible, and
# cclist_accessible identify whether or not those roles can see the bug.
# Bit arithmetic is performed by MySQL instead of Perl because bitset
# fields in the database are 64 bits wide (BIGINT), and Perl installations
# may or may not support integers larger than 32 bits. Using bitsets
# and doing bitset arithmetic is probably not cross-database compatible,
# however, so these mechanisms are likely to change in the future.
# Get data from the database about whether or not the user belongs to
# all groups the bug is in, and who are the bug's reporter and qa_contact
# along with which roles can always access the bug.
PushGlobalSQLState();
if ($userid) {
SendSQL("SELECT group_id FROM user_group_map WHERE user_id = $userid");
while (my @row = FetchSQLData()) {
# Processmail requires everything coming out of the db to be detainted
detaint_natural($row[0]);
push(@groups, $row[0]);
if ($::driver eq 'mysql') {
SendSQL("SELECT bug_id, ((groupset & $usergroupset) = groupset),
reporter, reporter_accessible , cclist_accessible
FROM bugs
WHERE bug_id IN (" . join(',', @buglist) . ")");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT bug_id, CASE WHEN ((groupset & int8($usergroupset)) = groupset)
THEN 1 ELSE 0 END as isauthorized,
reporter, reporter_accessible, cclist_accessible
FROM bugs
WHERE bug_id IN (" . join(',', @buglist) . ")");
}
while (my @row = FetchSQLData()) {
my ($bug_id, $isauthorized, $reporter, $reporter_accessible, $cclist_accessible) = @row;
# Record bug number and continue if the user is a member of all groups to which the bug belongs.
if ($isauthorized) {
$cansee{$bug_id} = 1;
next;
}
} else {
$userid = 0;
}
my $query = "
SELECT
bugs.bug_id,
COUNT(bug_group_map.group_id)
FROM
bugs LEFT JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id
LEFT JOIN cc ON bugs.bug_id = cc.bug_id
WHERE
bugs.bug_id IN (" . join(',', @buglist) . ")
AND ((";
if ($#groups >= 0) {
$query .= "bug_group_map.group_id IN (" . join(',', @groups) . ") OR ";
}
$query .= "bug_group_map.group_id IS NULL) ";
if ($userid) {
$query .= "OR (bugs.reporter_accessible = 1 AND bugs.reporter = $userid)
OR (bugs.cclist_accessible = 1 AND cc.who = $userid)";
}
$query .= ") GROUP BY bugs.bug_id";
# Record bug number and continue if the user is in a role that has access to the bug.
if ($reporter_accessible && $reporter == $userid) {
$cansee{$bug_id} = 1;
next;
}
SendSQL($query);
while (my ($id, $count) = FetchSQLData()) {
$cansee{$id} = 1 + $count;
}
# Try to authorize the user one more time by seeing if they are on
# the cc: list. If so, finish validation and return.
if ($cclist_accessible) {
PushGlobalSQLState();
detaint_natural($bug_id) || die "CanSeeBug() called with non-integer bug number";
detaint_natural($userid) || die "CanSeeBug() called with non-integer bug number";
SendSQL("SELECT who FROM cc WHERE bug_id = $bug_id AND who = $userid");
my $ccwho = FetchOneColumn();
# more efficient to just check the var here instead of
# creating a potentially huge array to grep against
if ($ccwho) {
$cansee{$bug_id} = 1;
}
PopGlobalSQLState();
}
}
PopGlobalSQLState();
if ((keys %cansee) < 1) {
return 0;
} else {
return \%cansee;
}
}
sub CanSeeProduct {
my ($userid, $product) = (@_);
my @groups = ();
ConnectToDatabase();
PushGlobalSQLState();
SendSQL("SELECT groups.group_id FROM groups " .
"LEFT JOIN user_group_map ON groups.group_id = user_group_map.group_id " .
"WHERE user_group_map.user_id = $userid");
while (MoreSQLData()) {
my ($groupid) = FetchSQLData();
push (@groups, $groupid);
}
if (@groups < 1) {
@groups = (0);
}
SendSQL("
SELECT
products.product_id
FROM
products
LEFT JOIN product_group_map ON products.product_id = product_group_map.product_id
WHERE
products.product = " . SqlQuote($product) . "
AND (product_group_map.group_id IN (" . join(",", @groups) . ") OR product_group_map.group_id IS NULL)");
my $result = FetchOneColumn();
PopGlobalSQLState();
return 1 if $result;
return 0;
return \%cansee;
}
sub ValidatePassword {
@@ -1023,7 +1193,7 @@ sub quoteUrls {
my $item = $&;
my $bugnum = $2;
my $comnum = $4;
$item = GetBugLink($bugnum, $item, $::userid);
$item = GetBugLink($bugnum, $item);
$item =~ s/(id=\d+)/$1#c$comnum/;
$things[$count++] = $item;
}
@@ -1037,7 +1207,7 @@ sub quoteUrls {
while ($text =~ s/\bbug(\s|%\#)*(\d+)/"##$count##"/ei) {
my $item = $&;
my $num = $2;
$item = GetBugLink($num, $item, $::userid);
$item = GetBugLink($num, $item);
$things[$count++] = $item;
}
while ($text =~ s/\b(Created an )?attachment(\s|%\#)*(\(id=)?(\d+)\)?/"##$count##"/ei) {
@@ -1052,7 +1222,7 @@ sub quoteUrls {
my $item = $&;
my $num = $1;
my $bug_link;
$bug_link = GetBugLink($num, $num, $::userid);
$bug_link = GetBugLink($num, $num);
$item =~ s@\d+@$bug_link@;
$things[$count++] = $item;
}
@@ -1072,12 +1242,12 @@ sub quoteUrls {
}
# This is a new subroutine written 12/20/00 for the purpose of processing a
# link to a bug. It can be called using "GetBugLink (<BugNumber>, <LinkText>, <userid>);"
# link to a bug. It can be called using "GetBugLink (<BugNumber>, <LinkText>);"
# Where <BugNumber> is the number of the bug and <LinkText> is what apprears
# between '<a>' and '</a>'.
sub GetBugLink {
my ($bug_num, $link_text, $userid) = (@_);
my ($bug_num, $link_text) = (@_);
detaint_natural($bug_num) || die "GetBugLink() called with non-integer bug number";
# If we've run GetBugLink() for this bug number before, %::buglink
@@ -1088,12 +1258,12 @@ sub GetBugLink {
# is saved off rather than overwritten
PushGlobalSQLState();
SendSQL("SELECT bugs.bug_status, resolution, short_desc " .
"FROM bugs WHERE bugs.bug_id = $bug_num");
SendSQL("SELECT bugs.bug_status, resolution, short_desc, groupset " .
"FROM bugs WHERE bugs.bug_id = $bug_num");
# If the bug exists, save its data off for use later in the sub
if (MoreSQLData()) {
my ($bug_state, $bug_res, $bug_desc) = FetchSQLData();
my ($bug_state, $bug_res, $bug_desc, $bug_grp) = FetchSQLData();
# Initialize these variables to be "" so that we don't get warnings
# if we don't change them below (which is highly likely).
my ($pre, $title, $post) = ("", "", "");
@@ -1108,7 +1278,7 @@ sub GetBugLink {
$title .= " $bug_res";
$post = "</strike>";
}
if (CanSeeBug($bug_num, $userid)) {
if ($bug_grp == 0 || CanSeeBug($bug_num, $::userid, $::usergroupset)) {
$title .= " - $bug_desc";
}
$::buglink{$bug_num} = [$pre, value_quote($title), $post];
@@ -1139,20 +1309,43 @@ sub GetLongDescriptionAsText {
my ($id, $start, $end) = (@_);
my $result = "";
my $count = 0;
my ($query) = ("SELECT profiles.login_name, longdescs.bug_when, " .
" longdescs.thetext " .
"FROM longdescs, profiles " .
"WHERE profiles.userid = longdescs.who " .
"AND longdescs.bug_id = $id ");
my $query = "
SELECT
profiles.login_name, ";
if ($::driver eq 'mysql') {
$query .= "
longdescs.bug_when, ";
} elsif ($::driver eq 'Pg') {
$query .= "
TO_CHAR(longdescs.bug_when, 'YYYY-MM-DD HH24:MI:SS'), ";
}
$query .= "
longdescs.thetext
FROM
longdescs,
profiles
WHERE
profiles.userid = longdescs.who
AND longdescs.bug_id = $id ";
if ($start && $start =~ /[1-9]/) {
# If the start is all zeros, then don't do this (because we want to
# not emit a leading "Additional Comments" line in that case.)
$query .= "AND longdescs.bug_when > '$start'";
if ($::driver eq 'mysql') {
$query .= "AND longdescs.bug_when > '$start' ";
} elsif ($::driver eq 'Pg') {
$query .= "AND TO_CHAR(longdescs.bug_when, 'YYYYMMDDHH24MISS') > '$start' ";
}
$count = 1;
}
if ($end) {
$query .= "AND longdescs.bug_when <= '$end'";
if ($::driver eq 'mysql') {
$query .= "AND longdescs.bug_when <= '$end' ";
} elsif ($::driver eq 'Pg') {
$query .= "AND TO_CHAR(longdescs.bug_when, 'YYYYMMDDHH24MISS') <= '$end' ";
}
}
$query .= "ORDER BY longdescs.bug_when";
@@ -1173,14 +1366,21 @@ sub GetLongDescriptionAsText {
sub GetComments {
my ($id) = (@_);
my @comments;
SendSQL("SELECT profiles.realname, profiles.login_name,
date_format(longdescs.bug_when,'%Y-%m-%d %H:%i'),
longdescs.thetext
my $query = "SELECT profiles.realname, profiles.login_name, ";
if ($::driver eq 'mysql') {
$query .= "date_format(longdescs.bug_when,'%Y-%m-%d %H:%i'), ";
} elsif ($::driver eq 'Pg') {
$query .= "TO_CHAR(longdescs.bug_when,'YYYY-MM-DD HH24:MI'), ";
}
$query .= "longdescs.thetext
FROM longdescs, profiles
WHERE profiles.userid = longdescs.who
AND longdescs.bug_id = $id
ORDER BY longdescs.bug_when");
ORDER BY longdescs.bug_when";
SendSQL($query);
while (MoreSQLData()) {
my %comment;
@@ -1204,13 +1404,23 @@ sub GetComments {
sub LearnAboutColumns {
my ($table) = (@_);
my %a;
SendSQL("show columns from $table");
my @list = ();
my @row;
while (@row = FetchSQLData()) {
my ($name,$type) = (@row);
$a{"$name,type"} = $type;
push @list, $name;
if ($::driver eq 'mysql') {
SendSQL("show columns from $table");
while ( my @row = FetchSQLData() ) {
my ($name,$type) = (@row);
$a{"$name,type"} = $type;
push (@list, $name);
}
} elsif ($::driver eq 'Pg') {
my $ref = $::db->func($table, "table_attributes");
for my $index ( 0..@{$ref} ) {
next if !$ref->[$index]->{'NAME'};
my $name = $ref->[$index]->{'NAME'};
my $type = $ref->[$index]->{'TYPE'};
$a{"$name,type"} = $type;
push (@list, $name);
}
}
$a{"-list-"} = \@list;
return \%a;
@@ -1234,6 +1444,21 @@ sub SplitEnumType {
return @result;
}
# subroutine: SplitTableValues
# description: This will take a table of values that were previously enum data types and return
# the legal values
sub SplitTableValues {
my ($str) = (@_);
my @result = ();
my @row = ();
my $query = "select value from $str";
SendSQL($query);
while (@row = FetchSQLData()) {
push (@result, $row[0]);
}
return @result;
}
# This routine is largely copied from Mysql.pm.
@@ -1252,28 +1477,32 @@ sub SqlQuote {
sub UserInGroup {
my ($userid, $groupname) = (@_);
if (!$userid) {
my ($groupname) = (@_);
if ($::usergroupset eq "0") {
return 0;
}
ConnectToDatabase();
SendSQL("SELECT user_id FROM user_group_map, groups
WHERE user_group_map.group_id = groups.group_id
AND groups.name = " . SqlQuote($groupname) .
" AND user_id = $userid");
my $result = FetchOneColumn();
return 1 if $result;
PushGlobalSQLState();
if ($::driver eq 'mysql') {
SendSQL("select (group_bit & $::usergroupset) != 0 " .
"from groups where name = " . SqlQuote($groupname));
} elsif ($::driver eq 'Pg') {
SendSQL("select (group_bit & int8($::usergroupset)) != 0 " .
"from groups where name = " . SqlQuote($groupname));
}
my $bit = FetchOneColumn();
PopGlobalSQLState();
if ($bit) {
return 1;
}
return 0;
}
sub BugInGroup {
my ($bugid, $groupname) = (@_);
my $groupid = GroupNameToId($groupname);
my $groupbit = GroupNameToBit($groupname);
PushGlobalSQLState();
SendSQL("SELECT bug_id FROM bug_group_map
WHERE bug_group_map.group_id = groups.group_id
AND groups.name = " . SqlQuote($groupname) .
" AND bug_id = $bugid");
SendSQL("SELECT (bugs.groupset & $groupbit) != 0 FROM bugs WHERE bugs.bug_id = $bugid");
my $bugingroup = FetchOneColumn();
PopGlobalSQLState();
return $bugingroup;
@@ -1282,7 +1511,7 @@ sub BugInGroup {
sub GroupExists {
my ($groupname) = (@_);
ConnectToDatabase();
SendSQL("select count(*) from groups where name=" . SqlQuote($groupname));
SendSQL("select count(*) from groups where name = " . SqlQuote($groupname));
my $count = FetchOneColumn();
return $count;
}
@@ -1290,24 +1519,28 @@ sub GroupExists {
# Given the name of an existing group, returns the bit associated with it.
# If the group does not exist, returns 0.
# !!! Remove this function when the new group system is implemented!
sub GroupNameToId {
sub GroupNameToBit {
my ($groupname) = (@_);
ConnectToDatabase();
PushGlobalSQLState();
SendSQL("SELECT group_id FROM groups WHERE name = " . SqlQuote($groupname));
my $id = FetchOneColumn() || 0;
if ($::driver eq 'mysql') {
SendSQL("SELECT bit FROM groups WHERE name = " . SqlQuote($groupname));
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit FROM groups WHERE name = " . SqlQuote($groupname));
}
my $bit = FetchOneColumn() || 0;
PopGlobalSQLState();
return $id;
return $bit;
}
# Determines whether or not a group is active by checking
# the "isactive" column for the group in the "groups" table.
# Note: This function selects groups by bit rather than by name.
sub GroupIsActive {
my ($groupid) = (@_);
$groupid ||= 0;
my ($groupbit) = (@_);
$groupbit ||= 0;
ConnectToDatabase();
SendSQL("select isactive from groups where group_id=$groupid");
SendSQL("select isactive from groups where group_bit = $groupbit");
my $isactive = FetchOneColumn();
return $isactive;
}
@@ -1423,7 +1656,7 @@ sub RemoveVotes {
SendSQL("SELECT SUM(count) FROM votes WHERE bug_id = $id");
my $v = FetchOneColumn();
$v ||= 0;
SendSQL("UPDATE bugs SET votes = $v, delta_ts = delta_ts " .
SendSQL("UPDATE bugs SET votes = $v, delta_ts = now() " .
"WHERE bug_id = $id");
}
}
@@ -1521,6 +1754,30 @@ sub trim {
return $str;
}
# Returns current value from a given sequence or auto_increment depending
# on the database being used.
sub CurrId {
my $seqname = shift;
if ($::driver eq 'mysql') {
SendSQL("select LAST_INSERT_ID()");
return FetchOneColumn();
} elsif ($::driver eq 'Pg') {
if ( !$seqname ) {
return 0;
}
SendSQL("SELECT last_value FROM $seqname");
return FetchOneColumn();
} elsif ($::driver eq 'Oracle') {
if (!$seqname) {
return 0;
}
SendSQL("select $seqname.currval from dual");
return FetchOneColumn();
} else {
return 0;
}
}
###############################################################################
# Global Templatization Code

View File

@@ -45,9 +45,9 @@ use vars qw(
# Establish a connection to the database backend.
ConnectToDatabase();
# Check whether or not the user is logged in
$::userid = 0;
$::userid = quietly_check_login();
# Check whether or not the user is logged in and, if so, set the $::userid
# and $::usergroupset variables.
quietly_check_login();
###############################################################################
# Main Body Execution

View File

@@ -33,48 +33,48 @@ use vars qw($userid $usergroupset @legal_keywords %FORM);
use vars qw($template $vars);
ConnectToDatabase();
my $userid = quietly_check_login();
quietly_check_login();
GetVersionTable();
my $generic_query = "
select
bugs.bug_id,
bugs.product,
bugs.version,
bugs.rep_platform,
bugs.op_sys,
bugs.bug_status,
bugs.bug_severity,
bugs.priority,
bugs.resolution,
assign.login_name,
report.login_name,
bugs.component,
bugs.bug_file_loc,
bugs.short_desc,
bugs.target_milestone,
bugs.qa_contact,
bugs.status_whiteboard,
bugs.keywords
from bugs,profiles assign,profiles report
where assign.userid = bugs.assigned_to and report.userid = bugs.reporter";
my $generic_query = "
SELECT
bugs.bug_id,
bugs.product,
bugs.version,
bugs.rep_platform,
bugs.op_sys,
bugs.bug_status,
bugs.resolution,
bugs.priority,
bugs.bug_severity,
bugs.component,
assign.login_name,
report.login_name,
bugs.bug_file_loc,
bugs.short_desc,
bugs.target_milestone,
bugs.qa_contact,
bugs.status_whiteboard,
bugs.keywords
FROM bugs,profiles assign,profiles report
WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter";
my $buglist = $::FORM{'buglist'} ||
$::FORM{'bug_id'} ||
$::FORM{'id'} || "";
my @buglist = ();
foreach my $bug (split(/[:,]/, $buglist)) {
detaint_natural($bug) || next;
push(@buglist, $bug);
}
my $canseeref = CanSeeBug(\@buglist, $userid);
my @bugs;
foreach my $bug_id (@buglist) {
trick_taint($buglist);
my @canseebugs = split(/[:,]/, $buglist);
my $canseeref = CanSeeBug(\@canseebugs, $::userid, $::usergroupset);
foreach my $bug_id (split(/[:,]/, $buglist)) {
detaint_natural($bug_id) || next;
# Skip if we cannot see this bug
next if !$canseeref->{$bug_id};
SendSQL("$generic_query AND bugs.bug_id = $bug_id");

View File

@@ -80,7 +80,7 @@ if ( !defined $::FORM{'buglist'} ) {
exit;
}
my $userid = confirm_login();
confirm_login();
my $exporter = $::COOKIE{"Bugzilla_login"};
my $movers = Param("movers");
$movers =~ s/\w?,\w?/|/g;
@@ -98,7 +98,7 @@ $xml .= Bug::XML_Header( Param("urlbase"), $::param{'version'},
Param("maintainer"), $exporter );
print "<P>\n";
foreach my $id (split(/:/, $::FORM{'buglist'})) {
my $bug = new Bug($id, $userid);
my $bug = new Bug($id, $::userid);
$xml .= $bug->emitXML;
if (!$bug->error) {
my $exporterid = DBNameToIdAndCheck($exporter);

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,7 @@ require "bug_form.pl";
sub sillyness {
my $zz;
$zz = $::buffer;
$zz = $::usergroupset;
$zz = %::COOKIE;
$zz = %::components;
$zz = %::versions;
@@ -49,7 +50,8 @@ sub sillyness {
# Use global template variables.
use vars qw($vars $template);
my $userid = confirm_login();
confirm_login();
# The format of the initial comment can be structured by adding fields to the
# enter_bug template and then referencing them in the comment template.
@@ -94,7 +96,7 @@ ConnectToDatabase();
# Some sanity checking
if(Param("usebuggroupsentry") && GroupExists($product)) {
if(!UserInGroup($userid, $product)) {
if(!UserInGroup($product)) {
DisplayError("Sorry; you do not have the permissions necessary to enter
a bug against this product.", "Permission Denied");
exit;
@@ -141,7 +143,9 @@ if (Param("useqacontact")) {
$::FORM{'qa_contact'} = $qa_contact;
push(@bug_fields, "qa_contact");
}
}
} # else {
# $::FORM{'qa_contact'} = "0";
#}
if (exists $::FORM{'bug_status'}) {
# Ignore the given status, so that we can set it to UNCONFIRMED
@@ -149,7 +153,7 @@ if (exists $::FORM{'bug_status'}) {
# unconfirmed (so that a user can't override the below check), or if
# the user doesn't have permission to change the default status anyway
if ($::FORM{'bug_status'} eq $::unconfirmedstate
|| (!UserInGroup($userid, "canedit") && !UserInGroup($userid, "canconfirm"))) {
|| (!UserInGroup("canedit") && !UserInGroup("canconfirm"))) {
delete $::FORM{'bug_status'};
}
}
@@ -203,7 +207,7 @@ if (exists $::FORM{'bug_status'}
# Build up SQL string to add bug.
my $sql = "INSERT INTO bugs " .
"(" . join(",", @used_fields) . ", reporter, creation_ts) " .
"(" . join(",", @used_fields) . ", reporter, creation_ts, groupset) " .
"VALUES (";
foreach my $field (@used_fields) {
@@ -216,14 +220,16 @@ $comment = trim($comment);
# OK except for the fact that it causes e-mail to be suppressed.
$comment = $comment ? $comment : " ";
$sql .= "$userid, now())";
my @groupids;
if ($::driver eq 'mysql') {
$sql .= "$::userid, now(), (0";
} elsif ($::driver eq 'Pg') {
$sql .= "$::userid, now(), (int8(0)";
}
# Groups
foreach my $b (grep(/^group-\d*$/, keys %::FORM)) {
foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) {
if ($::FORM{$b}) {
my $v = substr($b, 6);
my $v = substr($b, 4);
$v =~ /^(\d+)$/
|| ThrowCodeError("One of the group bits submitted was invalid.",
undef, "abort");
@@ -233,27 +239,32 @@ foreach my $b (grep(/^group-\d*$/, keys %::FORM)) {
# hacked the "enter bug" form since otherwise the UI
# for adding the bug to the group won't appear on that form.
ThrowCodeError("Attempted to add bug to an inactive group, " .
"identified by the id '$v'.", undef, "abort");
"identified by the bit '$v'.", undef, "abort");
}
push @groupids, $v;
$sql .= " + $v"; # Carefully written so that the math is
# done by MySQL, which can handle 64-bit math,
# and not by Perl, which I *think* can not.
}
}
if ($::driver eq 'mysql') {
$sql .= ") & $::usergroupset)\n";
} elsif ($::driver eq 'Pg') {
$sql .= " & int8($::usergroupset)))\n";
}
# Add the bug report to the DB.
SendSQL($sql);
my $id = CurrId("bugs_bug_id_seq");
# Lock tables before inserting records for the new bug into the database
# if we are using a shadow database to prevent shadow database corruption
# when two bugs get created at the same time.
SendSQL("LOCK TABLES bugs WRITE, longdescs WRITE, cc WRITE, bug_group_map WRITE") if Param("shadowdb");
# Add the bug report to the DB.
SendSQL($sql);
# Get the bug ID back.
SendSQL("select LAST_INSERT_ID()");
my $id = FetchOneColumn();
SendSQL("LOCK TABLES bugs WRITE, longdescs WRITE, cc WRITE") if Param("shadowdb") && $::driver eq 'mysql';
# Add the comment
SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext)
VALUES ($id, $userid, now(), " . SqlQuote($comment) . ")");
VALUES ($id, $::userid, now(), " . SqlQuote($comment) . ")");
my %ccids;
my $ccid;
@@ -273,11 +284,7 @@ if (defined $::FORM{'cc'}) {
}
}
foreach my $group (@groupids) {
SendSQL("INSERT INTO bug_group_map (bug_id, group_id) VALUES ($id, $group)");
}
SendSQL("UNLOCK TABLES") if Param("shadowdb");
SendSQL("UNLOCK TABLES") if Param("shadowdb") && $::driver eq 'mysql';
# Assemble the -force* strings so this counts as "Added to this capacity"
my @ARGLIST = ();
@@ -291,7 +298,7 @@ if (defined $::FORM{'qa_contact'}) {
push (@ARGLIST, "-forceqacontact", DBID_to_name($::FORM{'qa_contact'}));
}
push (@ARGLIST, "-forcereporter", DBID_to_name($userid));
push (@ARGLIST, "-forcereporter", DBID_to_name($::userid));
push (@ARGLIST, $id, $::COOKIE{'Bugzilla_login'});

View File

@@ -27,8 +27,8 @@
use diagnostics;
use strict;
my $hasEditGroup = -1;
my $hasCanConfirmGroup = -1;
my $UserInEditGroupSet = -1;
my $UserInCanConfirmGroupSet = -1;
use lib qw(.);
@@ -51,6 +51,7 @@ use vars qw(%versions
%target_milestone
%legal_severity
%superusergroupset
$userid
$next_bug);
my $whoid = confirm_login();
@@ -71,13 +72,13 @@ use vars qw($template $vars);
# representing an existing bug that the user is authorized to access.
my @idlist;
if (defined $::FORM{'id'}) {
ValidateBugID($::FORM{'id'}, $whoid);
ValidateBugID($::FORM{'id'});
push @idlist, $::FORM{'id'};
} else {
foreach my $i (keys %::FORM) {
if ($i =~ /^id_([1-9][0-9]*)/) {
my $id = $1;
ValidateBugID($id, $whoid);
ValidateBugID($id);
push @idlist, $id;
}
}
@@ -91,7 +92,7 @@ scalar(@idlist)
# If we are duping bugs, let's also make sure that we can change
# the original. This takes care of issue A on bug 96085.
if (defined $::FORM{'dup_id'} && $::FORM{'knob'} eq "duplicate") {
ValidateBugID($::FORM{'dup_id'}, $whoid);
ValidateBugID($::FORM{'dup_id'});
# Also, let's see if the reporter has authorization to see the bug
# to which we are duping. If not we need to prompt.
@@ -152,6 +153,7 @@ sub CheckonComment( $ ) {
return( ! $ret ); # Return val has to be inverted
}
# Figure out whether or not the user is trying to change the product
# (either the "product" variable is not set to "don't change" or the
# user is changing a single bug and has changed the bug's product),
@@ -249,10 +251,10 @@ sub CheckCanChangeField {
if ($f eq "resolution") { # always OK this. if they really can't,
return 1; # it'll flag it when "status" is checked.
}
if ($hasEditGroup < 0) {
$hasEditGroup = UserInGroup($whoid, "editbugs");
if ($UserInEditGroupSet < 0) {
$UserInEditGroupSet = UserInGroup("editbugs");
}
if ($hasEditGroup) {
if ($UserInEditGroupSet) {
return 1;
}
if ($lastbugid != $bugid) {
@@ -274,10 +276,10 @@ sub CheckCanChangeField {
# group? Or, has it ever been confirmed? If not, then this
# isn't legal.
if ($hasCanConfirmGroup < 0) {
$hasCanConfirmGroup = UserInGroup($whoid, "canconfirm");
if ($UserInCanConfirmGroupSet < 0) {
$UserInCanConfirmGroupSet = UserInGroup("canconfirm");
}
if ($hasCanConfirmGroup) {
if ($UserInCanConfirmGroupSet) {
return 1;
}
SendSQL("SELECT everconfirmed FROM bugs WHERE bug_id = $bugid");
@@ -289,7 +291,7 @@ sub CheckCanChangeField {
$qacontactid eq $whoid) {
return 1;
}
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
$oldvalue = html_quote($oldvalue);
$newvalue = html_quote($newvalue);
ThrowUserError("You tried to change the <strong>$f</strong> field
@@ -307,11 +309,13 @@ sub DuplicateUserConfirm {
my $dupe = trim($::FORM{'id'});
my $original = trim($::FORM{'dup_id'});
SendSQL("SELECT reporter FROM bugs WHERE bug_id = " . SqlQuote($dupe));
my $reporter = FetchOneColumn();
SendSQL("SELECT profiles.groupset FROM profiles WHERE profiles.userid =".SqlQuote($reporter));
my $reportergroupset = FetchOneColumn();
if (CanSeeBug($original, $reporter)) {
if (CanSeeBug($original, $reporter, $reportergroupset)) {
$::FORM{'confirm_add_duplicate'} = "1";
return;
}
@@ -379,13 +383,13 @@ sub DoComma {
}
sub DoConfirm {
if ($hasEditGroup < 0) {
$hasEditGroup = UserInGroup($whoid, "editbugs");
if ($UserInEditGroupSet < 0) {
$UserInEditGroupSet = UserInGroup("editbugs");
}
if ($hasCanConfirmGroup < 0) {
$hasCanConfirmGroup = UserInGroup($whoid, "canconfirm");
if ($UserInCanConfirmGroupSet < 0) {
$UserInCanConfirmGroupSet = UserInGroup("canconfirm");
}
if ($hasEditGroup || $hasCanConfirmGroup) {
if ($UserInEditGroupSet || $UserInCanConfirmGroupSet) {
DoComma();
$::query .= "everconfirmed = 1";
}
@@ -407,9 +411,19 @@ sub ChangeStatus {
# to handle that.
my @open_state = map(SqlQuote($_), OpenStates());
my $open_state = join(", ", @open_state);
$::query .= "bug_status = IF(bug_status IN($open_state), '$str', bug_status)";
if ($::driver eq 'mysql') {
$::query .= "bug_status = IF(bug_status IN ($open_state), '$str', bug_status)";
} elsif ($::driver eq 'Pg') {
$::query .= "bug_status = CASE WHEN bug_status IN ($open_state) " .
"THEN '$str' ELSE bug_status END ";
}
} elsif (IsOpenedState($str)) {
$::query .= "bug_status = IF(everconfirmed = 1, '$str', '$::unconfirmedstate')";
if ($::driver eq 'mysql') {
$::query .= "bug_status = IF(everconfirmed = 1, '$str', '$::unconfirmedstate')";
} elsif ($::driver eq 'Pg') {
$::query .= "bug_status = CASE WHEN (select everconfirmed from bugs where bug_id = $::FORM{'id'}) = 1 " .
"THEN '$str' ELSE '$::unconfirmedstate' END ";
}
} else {
$::query .= "bug_status = '$str'";
}
@@ -427,6 +441,55 @@ sub ChangeResolution {
}
}
# Changing this so that it will process groups from checkboxes instead of
# 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
if($::usergroupset ne '0') {
my $groupAdd = "0";
my $groupDel = "0";
if ($::driver eq 'mysql') {
SendSQL("SELECT bit, isactive FROM groups WHERE " .
"isbuggroup != 0 AND bit & $::usergroupset != 0 ORDER BY bit");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit, isactive FROM groups WHERE " .
"isbuggroup != 0 AND group_bit & int8($::usergroupset) != 0 ORDER BY group_bit");
}
while (my ($b, $isactive) = FetchSQLData()) {
# The multiple change page may not show all groups a bug is in
# (eg product groups when listing more than one product)
# Only consider groups which were present on the form. We can't do this
# for single bug changes because non-checked checkboxes aren't present.
# All the checkboxes should be shown in that case, though, so its not
# an issue there
if ($::FORM{'id'} || exists $::FORM{"bit-$b"}) {
if (!$::FORM{"bit-$b"}) {
$groupDel .= "+$b";
} elsif ($::FORM{"bit-$b"} == 1 && $isactive) {
$groupAdd .= "+$b";
}
}
}
if ($groupAdd ne "0" || $groupDel ne "0") {
DoComma();
# mysql < 3.23.5 doesn't support the ~ operator, even though
# the docs say that it does
if ($::driver eq 'mysql') {
$::query .= "groupset = ((groupset & ($::superusergroupset - ($groupDel))) | ($groupAdd))";
} elsif ($::driver eq 'Pg') {
$::query .= "groupset = ((groupset & (int8($::superusergroupset) - (int8($groupDel)))) | (int8($groupAdd)))";
}
}
}
foreach my $field ("rep_platform", "priority", "bug_severity",
"summary", "component", "bug_file_loc", "short_desc",
"product", "version", "op_sys",
@@ -459,9 +522,15 @@ if (defined $::FORM{'qa_contact'}) {
# and cc list can see the bug even if they are not members of all groups
# to which the bug is restricted.
if ( $::FORM{'id'} ) {
SendSQL("SELECT count(*) FROM bug_group_map WHERE bug_id = $::FORM{'id'}");
my $groups = FetchOneColumn();
if ( $groups ) {
if ($::driver eq 'mysql') {
SendSQL("SELECT bit FROM groups WHERE bit & $::usergroupset != 0
AND isbuggroup != 0 AND isactive = 1");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT group_bit FROM groups WHERE (group_bit & int8($::usergroupset)) != 0
AND isbuggroup != 0 AND isactive = 1");
}
my ($groupbits) = FetchSQLData();
if ( $groupbits ) {
DoComma();
$::FORM{'reporter_accessible'} = $::FORM{'reporter_accessible'} ? '1' : '0';
$::query .= "reporter_accessible = $::FORM{'reporter_accessible'}";
@@ -689,8 +758,13 @@ my $delta_ts;
sub SnapShotBug {
my ($id) = (@_);
SendSQL("select delta_ts, " . join(',', @::log_columns) .
" from bugs where bug_id = $id");
if ($::driver eq 'mysql') {
SendSQL("select delta_ts, " . join(',', @::log_columns) .
" from bugs where bug_id = $id");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT TO_CHAR(delta_ts, 'YYYYMMDDHH24MISS'), " . join(',', @::log_columns) .
" FROM bugs WHERE bug_id = $id");
}
my @row = FetchSQLData();
$delta_ts = shift @row;
@@ -788,11 +862,13 @@ foreach my $id (@idlist) {
$bug_changed = 0;
my $write = "WRITE"; # Might want to make a param to control
# whether we do LOW_PRIORITY ...
SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " .
"profiles $write, dependencies $write, votes $write, " .
"keywords $write, longdescs $write, fielddefs $write, " .
"keyworddefs READ, groups READ, attachments READ, products READ, " .
"user_group_map READ, bug_group_map WRITE, product_group_map READ");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES bugs $write, bugs_activity $write, cc $write, " .
"cc AS selectVisible_cc $write, " .
"profiles $write, dependencies $write, votes $write, " .
"keywords $write, longdescs $write, fielddefs $write, " .
"keyworddefs READ, groups READ, attachments READ, products READ");
}
my @oldvalues = SnapShotBug($id);
my %oldhash;
my $i = 0;
@@ -807,11 +883,12 @@ foreach my $id (@idlist) {
my $value = $::FORM{'target_milestone'};
if (!defined $value || $value eq $::dontchange) {
$value = $oldhash{'target_milestone'};
$value = $oldhash{'target_milestone'};
}
SendSQL("SELECT defaultmilestone FROM products WHERE product = " .
SqlQuote($oldhash{'product'}));
if ($value eq FetchOneColumn()) {
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
ThrowUserError("You must determine a target milestone for bug $id
if you are going to accept it. Part of accepting
a bug is giving an estimate of when it will be fixed.",
@@ -831,7 +908,7 @@ foreach my $id (@idlist) {
$vars->{'bug_id'} = $id;
$vars->{'quoteUrls'} = \&quoteUrls;
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
# Warn the user about the mid-air collision and ask them what to do.
$template->process("bug/process/midair.html.tmpl", $vars)
@@ -1064,6 +1141,76 @@ foreach my $id (@idlist) {
}
}
# When a bug changes products and the old or new product is associated
# with a bug group, it may be necessary to remove the bug from the old
# 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.
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'}
&& $::FORM{'product'} ne $::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'
&& GroupNameToBit($oldhash{'product'}) & $oldhash{'groupset'}))
# 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(GroupNameToBit($::FORM{'product'}))
) {
# Add the bug to the group associated with its new product.
my $groupbit = GroupNameToBit($::FORM{'product'});
if ($::driver eq 'mysql') {
SendSQL("UPDATE bugs SET groupset = groupset + $groupbit WHERE bug_id = $id");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE bugs SET groupset = groupset + int8($groupbit) WHERE bug_id = $id");
}
}
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 $groupbit = GroupNameToBit($oldhash{'product'});
if ($::driver eq 'mysql') {
SendSQL("UPDATE bugs SET groupset = groupset - $groupbit WHERE bug_id = $id");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE bugs SET groupset = groupset - int8($groupbit) WHERE bug_id = $id");
}
}
}
# get a snapshot of the newly set values out of the database,
# and then generate any necessary bug activity entries by seeing
# what has changed since before we wrote out the new values.
@@ -1119,46 +1266,15 @@ foreach my $id (@idlist) {
}
}
if ($bug_changed) {
SendSQL("UPDATE bugs SET delta_ts = " . SqlQuote($timestamp) . " WHERE bug_id = $id");
if ($::driver eq 'mysql') {
SendSQL("UPDATE bugs SET delta_ts = " . SqlQuote($timestamp) . " WHERE bug_id = $id");
} elsif ($::driver eq 'Pg') {
SendSQL("UPDATE bugs SET delta_ts = TO_DATE(" . SqlQuote($timestamp) .
", 'YYYYMMDDHH24MISS') WHERE bug_id = $id");
}
}
# Make necessary group membership changes
# Changing this so that it will process groups from checkboxes instead of
# select lists. This means that instead of looking for the group-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.
# Also, we don't want to clobber existing groups.
if ($whoid) {
my %buggroups = ();
# First, find out what groups this bug is currently private to.
SendSQL("SELECT group_id FROM bug_group_map WHERE bug_id = $id");
while (my ($groupid) = FetchSQLData()) {
$buggroups{$groupid} = 1;
}
# Second, find out what groups this person is a member of and see if they made changes.
SendSQL("SELECT groups.group_id, groups.isactive FROM groups, user_group_map WHERE " .
"groups.group_id = user_group_map.group_id AND user_group_map.user_id = $whoid");
while (my ($groupid, $isactive) = FetchSQLData()) {
# Box not checked so remove from group
if (!$::FORM{"group-$groupid"}) {
$buggroups{$groupid} = 0;
# Box checked and is active so add to group
} elsif ($::FORM{"group-$groupid"} == 1 && $isactive) {
$buggroups{$groupid} = 1;
} # Else leave alone
}
# Update the bug_group table with new group values.
SendSQL("DELETE FROM bug_group_map WHERE bug_id = $id");
foreach my $group (keys %buggroups) {
next if !$buggroups{$group};
SendSQL("INSERT INTO bug_group_map VALUES ($id, $group)");
}
}
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
my @ARGLIST = ();
if ( $removedCcString ne "" ) {
@@ -1246,7 +1362,7 @@ if ($::COOKIE{"BUGLIST"} && $::FORM{'id'}) {
my $cur = lsearch(\@bugs, $::FORM{"id"});
if ($cur >= 0 && $cur < $#bugs) {
my $next_bug = $bugs[$cur + 1];
if (detaint_natural($next_bug) && CanSeeBug($next_bug)) {
if (detaint_natural($next_bug) && CanSeeBug($next_bug, $userid, $::usergroupset)) {
$::FORM{'id'} = $next_bug;
$vars->{'next_id'} = $next_bug;

View File

@@ -103,13 +103,23 @@ sub ProcessOneBug {
$defmailhead{$field} = $mailhead;
$fielddescription{$field} = $description;
}
SendSQL("SELECT " . join(',', @::log_columns) . ", lastdiffed, now() " .
"FROM bugs WHERE bug_id = $id");
if ($::driver eq 'mysql') {
SendSQL("SELECT " . join(',', @::log_columns) . ", lastdiffed, " .
"now() FROM bugs WHERE bug_id = $id");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT " . join(',', @::log_columns) . ", to_char(lastdiffed, 'YYYYMMDDHH24MISS'), " .
"to_char(now(), 'YYYYMMDDHH24MISS') FROM bugs WHERE bug_id = $id");
}
my @row = FetchSQLData();
foreach my $i (@::log_columns) {
$values{$i} = shift(@row);
}
my ($start, $end) = (@row);
if (!$start && $::driver eq 'Pg') {
$start = '00000000000000';
}
# $start and $end are considered safe because users can't touch them
trick_taint($start);
trick_taint($end);
@@ -132,19 +142,24 @@ sub ProcessOneBug {
}
my @diffs;
my $datepart = "";
if ($::driver eq 'mysql') {
$datepart = " AND bug_when > '$start' AND bug_when <= '$end' ";
} elsif ($::driver eq 'Pg') {
$datepart = " AND to_char(bug_when, 'YYYYMMDDHH24MISS') > '$start' " .
" AND to_char(bug_when, 'YYYYMMDDHH24MISS') <= '$end' ";
}
SendSQL("SELECT profiles.login_name, fielddefs.description, " .
" bug_when, removed, added, attach_id " .
" bug_when, removed, added, attach_id " .
"FROM bugs_activity, fielddefs, profiles " .
"WHERE bug_id = $id " .
" AND fielddefs.fieldid = bugs_activity.fieldid " .
" AND profiles.userid = who " .
" AND bug_when > '$start' " .
" AND bug_when <= '$end' " .
"ORDER BY bug_when"
);
" AND profiles.userid = who " .
$datepart .
"ORDER BY bug_when");
while (MoreSQLData()) {
my @row = FetchSQLData();
push(@diffs, \@row);
@@ -166,10 +181,9 @@ sub ProcessOneBug {
$difftext = trim($difftext);
my $deptext = "";
my $resid =
my $resid =
SendSQL("SELECT bugs_activity.bug_id, bugs.short_desc, fielddefs.name, " .
" removed, added " .
@@ -180,10 +194,9 @@ sub ProcessOneBug {
" AND fielddefs.fieldid = bugs_activity.fieldid" .
" AND (fielddefs.name = 'bug_status' " .
" OR fielddefs.name = 'resolution') " .
" AND bug_when > '$start' " .
" AND bug_when <= '$end' " .
$datepart .
"ORDER BY bug_when, bug_id");
my $thisdiff = "";
my $lastbug = "";
my $interestingchange = 0;
@@ -279,7 +292,7 @@ sub ProcessOneBug {
@excludedAddresses = filterExcludeList(\@excludedAddresses,
\@allEmail);
# print LOG "excluded: " . join(',',@excludedAddresses) . "\n\n";
#print LOG "excluded: " . join(',',@excludedAddresses) . "\n\n";
foreach my $person ( @allEmail ) {
my @reasons;
@@ -309,9 +322,15 @@ sub ProcessOneBug {
}
}
SendSQL("UPDATE bugs SET lastdiffed = '$end', delta_ts = delta_ts " .
"WHERE bug_id = $id");
if ($::driver eq 'mysql') {
SendSQL("UPDATE bugs SET lastdiffed = '$end', delta_ts = now() " .
"WHERE bug_id = $id");
} else {
# SendSQL("UPDATE bugs SET lastdiffed = TO_DATE('$end', 'YYYYMMDDHH24MISS'), delta_ts = now() " .
# "WHERE bug_id = $id");
SendSQL("UPDATE bugs SET lastdiffed = now(), delta_ts = now() " .
"WHERE bug_id = $id");
}
# Filter the exclude list for dupes one last time
@excludedAddresses = filterExcludeList(\@excludedAddresses,
@@ -654,12 +673,16 @@ sub NewProcessOnePerson ($$$$$$$$$$$$) {
if ($nomail{$person}) {
return;
}
my $userid = DBname_to_id($person);
SendSQL("SELECT userid, groupset " .
"FROM profiles WHERE login_name = " . SqlQuote($person));
my ($userid, $groupset) = (FetchSQLData());
$seen{$person} = 1;
detaint_natural($userid);
detaint_natural($groupset);
# if this person doesn't have permission to see info on this bug,
# return.
@@ -669,7 +692,7 @@ sub NewProcessOnePerson ($$$$$$$$$$$$) {
# see the action of restricting the bug itself; the bug will just
# quietly disappear from their radar.
#
return unless CanSeeBug($id, $userid);
return unless CanSeeBug($id, $userid, $groupset);
# We shouldn't send changedmail if this is a dependency mail, and any of
# the depending bugs is not visible to the user.
@@ -677,7 +700,7 @@ sub NewProcessOnePerson ($$$$$$$$$$$$) {
my $save_id = $dep_id;
detaint_natural($dep_id) || warn("Unexpected Error: \@depbugs contains a non-numeric value: '$save_id'")
&& return;
return unless CanSeeBug($dep_id, $userid);
return unless CanSeeBug($dep_id, $userid, $groupset);
}
my %mailhead = %defmailhead;
@@ -767,11 +790,11 @@ sub NewProcessOnePerson ($$$$$$$$$$$$) {
}
if ($enableSendMail == 1) {
open(SENDMAIL, "|/usr/lib/sendmail $sendmailparam -t -i") ||
die "Can't open sendmail";
open(SENDMAIL, "|/usr/lib/sendmail $sendmailparam -t -i") ||
die "Can't open sendmail";
print SENDMAIL trim($msg) . "\n";
close SENDMAIL;
print SENDMAIL trim($msg) . "\n";
close SENDMAIL;
}
push(@sentlist, $person);
return 1;
@@ -833,7 +856,16 @@ if ($#ARGV == 1) {
if ($ARGV[0] eq "rescanall") {
print "Collecting bug ids...\n";
SendSQL("select bug_id, lastdiffed, delta_ts from bugs where lastdiffed < delta_ts AND delta_ts < date_sub(now(), INTERVAL 30 minute) order by bug_id");
if ($::driver eq 'mysql') {
SendSQL("select bug_id, lastdiffed, delta_ts from bugs " .
"where lastdiffed < delta_ts AND delta_ts < date_sub(now(), INTERVAL 30 minute) " .
"order by bug_id");
} else {
SendSQL("select bug_id, TO_CHAR(lastdiffed, 'YYYY-MM-DD HH24:MI:SS'), " .
"TO_CHAR(delta_ts, 'YYYY-MM-DD HH24:MI:SS') from bugs " .
"where lastdiffed < delta_ts AND now() - delta_ts <= '30 minutes' " .
"order by bug_id");
}
my @list;
while (my @row = FetchSQLData()) {
my $time = $row[2];

View File

@@ -50,19 +50,18 @@ use vars qw(
$vars
);
my $userid = 0;
if (defined $::FORM{"GoAheadAndLogIn"}) {
# We got here from a login page, probably from relogin.cgi. We better
# make sure the password is legit.
$userid = confirm_login();
confirm_login();
} else {
$userid = quietly_check_login();
quietly_check_login();
}
# Backwards compatibility hack -- if there are any of the old QUERY_*
# cookies around, and we are logged in, then move them into the database
# and nuke the cookie. This is required for Bugzilla 2.8 and earlier.
if ($userid) {
if ($::userid) {
my @oldquerycookies;
foreach my $i (keys %::COOKIE) {
if ($i =~ /^QUERY_(.*)$/) {
@@ -79,12 +78,12 @@ if ($userid) {
if ($value) {
my $qname = SqlQuote($name);
SendSQL("SELECT query FROM namedqueries " .
"WHERE userid = $userid AND name = $qname");
"WHERE userid = $::userid AND name = $qname");
my $query = FetchOneColumn();
if (!$query) {
SendSQL("REPLACE INTO namedqueries " .
"(userid, name, query) VALUES " .
"($userid, $qname, " . SqlQuote($value) . ")");
"($::userid, $qname, " . SqlQuote($value) . ")");
}
}
print "Set-Cookie: $cookiename= ; path=" . Param("cookiepath") .
@@ -94,17 +93,17 @@ if ($userid) {
}
if ($::FORM{'nukedefaultquery'}) {
if ($userid) {
if ($::userid) {
SendSQL("DELETE FROM namedqueries " .
"WHERE userid = $userid AND name = '$::defaultqueryname'");
"WHERE userid = $::userid AND name = '$::defaultqueryname'");
}
$::buffer = "";
}
my $userdefaultquery;
if ($userid) {
if ($::userid) {
SendSQL("SELECT query FROM namedqueries " .
"WHERE userid = $userid AND name = '$::defaultqueryname'");
"WHERE userid = $::userid AND name = '$::defaultqueryname'");
$userdefaultquery = FetchOneColumn();
}
@@ -188,7 +187,7 @@ if ($default{'chfieldto'}->[0] eq "") {
GetVersionTable();
# We don't want people to see products they don't
# if using usebuggroups, then we don't want people to see products they don't
# have access to. Remove them from the list.
my @products = ();
@@ -199,7 +198,7 @@ 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 (!CanSeeProduct($userid, $p));
next if (Param("usebuggroups") && GroupExists($p) && !UserInGroup($p));
# 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.
@@ -297,7 +296,7 @@ $vars->{'rep_platform'} = \@::legal_platform;
$vars->{'op_sys'} = \@::legal_opsys;
$vars->{'priority'} = \@::legal_priority;
$vars->{'bug_severity'} = \@::legal_severity;
$vars->{'userid'} = $userid;
$vars->{'userid'} = $::userid;
# Boolean charts
my @fields;
@@ -345,10 +344,10 @@ for (my $chart = 0; $::FORM{"field$chart-0-0"}; $chart++) {
$default{'charts'} = \@charts;
# Named queries
if ($userid) {
if ($::userid) {
my @namedqueries;
SendSQL("SELECT name FROM namedqueries " .
"WHERE userid = $userid AND name != '$::defaultqueryname' " .
"WHERE userid = $::userid AND name != '$::defaultqueryname' " .
"ORDER BY name");
while (MoreSQLData()) {
push(@namedqueries, FetchOneColumn());

View File

@@ -577,7 +577,7 @@ my $max_table_size = 50;
SendSQL("SELECT keyworddefs.name, keyworddefs.description,
COUNT(keywords.bug_id), keywords.bug_id
FROM keyworddefs LEFT JOIN keywords ON keyworddefs.id=keywords.keywordid
GROUP BY keyworddefs.id
GROUP BY keywords.bug_id, keyworddefs.name, keyworddefs.description
ORDER BY keyworddefs.name");
while (MoreSQLData()) {
@@ -609,9 +609,9 @@ while (MoreSQLData()) {
print "</table><p>\n";
my $userid = quietly_check_login();
quietly_check_login();
if (UserInGroup($userid, "editkeywords")) {
if (UserInGroup("editkeywords")) {
print qq{<p><a href="editkeywords.cgi">Edit keywords</a>\n};
}
@@ -670,7 +670,7 @@ SendSQL("SELECT product,description FROM products ORDER BY product");
while (MoreSQLData()) {
my ($product, $productdesc) = FetchSQLData();
next if (Param("usebuggroups") && GroupExists($product) && !UserInGroup($userid, $product));
next if (Param("usebuggroups") && GroupExists($product) && !UserInGroup($product));
push (@products, $product);
$line_count++;
@@ -692,7 +692,7 @@ print qq{
</table></td></tr></table> };
if (UserInGroup($userid, "editcomponents")) {
if (UserInGroup("editcomponents")) {
print qq{<p><a href="editproducts.cgi">Edit products</a><p>};
}
@@ -751,7 +751,7 @@ foreach $product (@products)
}
print qq{</table>};
if (UserInGroup($userid, "editcomponents")) {
if (UserInGroup("editcomponents")) {
print qq{<p><a href="editcomponents.cgi">Edit components</a><p>};
}
@@ -817,7 +817,7 @@ Containing at least <INPUT NAME=votes SIZE=3 VALUE=""> votes
<td>
Where the field(s)
<SELECT NAME="chfield" MULTIPLE SIZE=4>
<OPTION VALUE="[Bug creation]">[Bug creation]<OPTION VALUE="assigned_to">assigned_to<OPTION VALUE="bug_file_loc">bug_file_loc<OPTION VALUE="bug_severity">bug_severity<OPTION VALUE="bug_status">bug_status<OPTION VALUE="component">component<OPTION VALUE="everconfirmed">everconfirmed<OPTION VALUE="keywords">keywords<OPTION VALUE="op_sys">op_sys<OPTION VALUE="priority">priority<OPTION VALUE="product">product<OPTION VALUE="qa_contact">qa_contact<OPTION VALUE="rep_platform">rep_platform<OPTION VALUE="reporter">reporter<OPTION VALUE="resolution">resolution<OPTION VALUE="short_desc">short_desc<OPTION VALUE="status_whiteboard">status_whiteboard<OPTION VALUE="target_milestone">target_milestone<OPTION VALUE="version">version<OPTION VALUE="votes">votes
<OPTION VALUE="[Bug creation]">[Bug creation]<OPTION VALUE="assigned_to">assigned_to<OPTION VALUE="bug_file_loc">bug_file_loc<OPTION VALUE="bug_severity">bug_severity<OPTION VALUE="bug_status">bug_status<OPTION VALUE="component">component<OPTION VALUE="everconfirmed">everconfirmed<OPTION VALUE="groupset">groupset<OPTION VALUE="keywords">keywords<OPTION VALUE="op_sys">op_sys<OPTION VALUE="priority">priority<OPTION VALUE="product">product<OPTION VALUE="qa_contact">qa_contact<OPTION VALUE="rep_platform">rep_platform<OPTION VALUE="reporter">reporter<OPTION VALUE="resolution">resolution<OPTION VALUE="short_desc">short_desc<OPTION VALUE="status_whiteboard">status_whiteboard<OPTION VALUE="target_milestone">target_milestone<OPTION VALUE="version">version<OPTION VALUE="votes">votes
</SELECT> changed to <INPUT NAME="chfieldvalue" SIZE="10">
</td>
</tr>
@@ -906,6 +906,7 @@ queries, but it's not the easiest thing to learn (or explain).
<table>
<tr><td>
<table><tr><td>&nbsp;</td><td><SELECT NAME="field0-0-0"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product
@@ -985,6 +986,7 @@ Field 3: What the search term is<br>
<table>
<tr><td>
<table><tr><td>&nbsp;</td><td><SELECT NAME="field0-0-0"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product
@@ -1032,6 +1034,7 @@ Field 3: What the search term is<br>
<OPTION VALUE="changedto">changed to
<OPTION VALUE="changedby">changed by
</SELECT><INPUT NAME="value0-0-0" VALUE=""></td></tr><tr><td><b>OR</b></td><td><SELECT NAME="field0-0-1"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product
@@ -1113,6 +1116,7 @@ the query will be anything that matches either of the terms.
<tr><td>
<table><tr><td>&nbsp;</td><td><SELECT NAME="field0-0-0"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product
@@ -1160,6 +1164,7 @@ the query will be anything that matches either of the terms.
<OPTION VALUE="changedto">changed to
<OPTION VALUE="changedby">changed by
</SELECT><INPUT NAME="value0-0-0" VALUE=""><INPUT TYPE="button" VALUE="Or" NAME="cmd-add0-0-1" ONCLICK="document.forms[0].action='query.cgi#chart' ; document.forms[0].method='POST' ; return 1;"></td></tr><tr><td>&nbsp;</td><td align="center" valign="middle"><b>AND</b></td></tr><tr><td>&nbsp;</td><td><SELECT NAME="field0-1-0"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product
@@ -1242,6 +1247,7 @@ can think of the lines of "Or" as having parenthesis around them.
<table>
<tr><td>
<table><tr><td>&nbsp;</td><td><SELECT NAME="field0-0-0"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product
@@ -1295,6 +1301,7 @@ can think of the lines of "Or" as having parenthesis around them.
<td colspan="2"><hr></td>
</tr><tr><td>&nbsp;</td><td>
<SELECT NAME="field1-0-0"><OPTION SELECTED VALUE="noop">---
<OPTION VALUE="groupset">groupset
<OPTION VALUE="bug_id">Bug #
<OPTION VALUE="short_desc">Summary
<OPTION VALUE="product">Product

View File

@@ -42,7 +42,7 @@ if ($action eq "show") {
my @quips;
push (@quips, $_) while (<COMMENTS>);
close COMMENTS;
$vars->{'quips'} = \@quips;
$vars->{'show_quips'} = 1;
}

View File

@@ -30,6 +30,7 @@ use vars qw($template $vars);
use lib qw(.);
require "CGI.pl";
require "globals.pl";
# We don't want to remove a random logincookie from the db, so
# call quietly_check_login. If we're logged in after this, then
@@ -66,8 +67,8 @@ delete $::COOKIE{"Bugzilla_login"};
$vars->{'link'} = "Log in again here";
$vars->{'user'} = {};
print "Content-Type: text/html\n\n";
$template->process("global/message.html.tmpl", $vars)
print "Content-Type: text/html\n\n";
$template->process("global/message.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
exit;

View File

@@ -25,6 +25,9 @@
# Added -All- report, change "nobanner" to "banner" (it is strange to have a
# list with 2 positive and 1 negative choice), default links on, add show
# sql comment.
# 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
@@ -70,19 +73,24 @@ my %reports =
# If we're using bug groups for products, we should apply those restrictions
# to viewing reports, as well. Time to check the login in that case.
ConnectToDatabase(1);
my $userid = quietly_check_login();
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;
push(@myproducts, "-All-");
foreach my $this_product (@legal_product) {
if (!CanSeeProduct($userid, $this_product)) {
next;
} else {
push(@myproducts, $this_product)
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 );
}
if (! defined $FORM{'product'}) {
@@ -100,6 +108,14 @@ if (! defined $FORM{'product'}) {
grep($_ eq $FORM{'product'}, @myproducts)
|| DisplayError("You entered an invalid product name.") && exit;
# If usebuggroups is on, 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'})
&& DisplayError("You do not have the permissions necessary to view reports for this product.")
&& exit;
# For security and correctness, validate the value of the "output" form variable.
# Valid values are the keys from the %reports hash defined above which appear in
# the "output" drop-down menu on the report generation form.
@@ -155,7 +171,6 @@ sub choose_product {
print <<FIN;
<center>
<h1>Welcome to the Bugzilla Query Kitchen</h1>
</center>
<form method=get action=reports.cgi>
<table border=1 cellpadding=5>
<tr>
@@ -258,29 +273,39 @@ FIN
# Build up $query string
my $query;
$query = <<FIN;
select
if ($::driver eq 'mysql') {
$query = <<FIN;
SELECT
bugs.bug_id,
bugs.bug_status,
assign.login_name,
unix_timestamp(date_format(bugs.creation_ts, '%Y-%m-%d %h:%m:%s'))
from bugs,
profiles assign
where bugs.assigned_to = assign.userid
FROM
bugs,
profiles assign
WHERE
bugs.assigned_to = assign.userid
FIN
} elsif ($::driver eq 'Pg') {
$query = <<FIN;
SELECT
bugs.bug_id,
bugs.bug_status,
assign.login_name,
bugs.creation_ts
FROM
bugs,
profiles assign
WHERE
bugs.assigned_to = assign.userid
FIN
if ($FORM{'product'} ne "-All-" ) {
$query .= "and bugs.product=".SqlQuote($FORM{'product'});
} else {
my @sqlproducts = ();
foreach my $p (@myproducts) {
push (@sqlproducts, "product = " . SqlQuote($p));
}
$query .= " and (" . join (" or ", @sqlproducts) . ")";
}
$query .= "AND bugs.bug_status IN ('NEW', 'ASSIGNED', 'REOPENED')";
if ($FORM{'product'} ne "-All-" ) {
$query .= " AND bugs.product = ".SqlQuote($FORM{'product'});
}
$query .= " AND bugs.bug_status IN ('NEW', 'ASSIGNED', 'REOPENED')";
# End build up $query string
print "<font color=purple><tt>$query</tt></font><p>\n"
@@ -685,15 +710,9 @@ sub most_doomed_for_milestone {
# Build up $query string
my $query;
$query = "select distinct assigned_to from bugs where target_milestone=\"$ms\"";
$query = "select distinct assigned_to from bugs where target_milestone = '$ms' ";
if ($FORM{'product'} ne "-All-" ) {
$query .= "and bugs.product=".SqlQuote($FORM{'product'});
} else {
my @sqlproducts = ();
foreach my $p (@myproducts) {
push (@sqlproducts, "product = " . SqlQuote($p));
}
$query .= " and (" . join (" or ", @sqlproducts) . ")";
$query .= " and bugs.product=".SqlQuote($FORM{'product'});
}
$query .= <<FIN;
and
@@ -717,15 +736,9 @@ FIN
my $person = "";
my $bugtotal = 0;
foreach $person (@people) {
my $query = "select count(bug_id) from bugs,profiles where target_milestone=\"$ms\" and userid=assigned_to and userid=\"$person\"";
my $query = "select count(bug_id) from bugs,profiles where target_milestone = '$ms' and userid=assigned_to and userid = '$person' ";
if( $FORM{'product'} ne "-All-" ) {
$query .= "and bugs.product=".SqlQuote($FORM{'product'});
} else {
my @sqlproducts = ();
foreach my $p (@myproducts) {
push (@sqlproducts, "product = " . SqlQuote($p));
}
$query .= " and (" . join (" or ", @sqlproducts) . ")";
$query .= "and bugs.product=".SqlQuote($FORM{'product'});
}
$query .= <<FIN;
and
@@ -822,12 +835,6 @@ sub most_recently_doomed {
my $query = "select distinct assigned_to from bugs where bugs.bug_status='NEW' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
if ($FORM{'product'} ne "-All-" ) {
$query .= "and bugs.product=".SqlQuote($FORM{'product'});
} else {
my @sqlproducts = ();
foreach my $p (@myproducts) {
push (@sqlproducts, "product = " . SqlQuote($p));
}
$query .= " and (" . join (" or ", @sqlproducts) . ")";
}
# End build up $query string

View File

@@ -23,6 +23,7 @@
use diagnostics;
use strict;
use POSIX;
use lib qw(.);
@@ -32,7 +33,7 @@ use vars %::FORM;
ConnectToDatabase();
my $userid = confirm_login();
confirm_login();
# Make sure the user is authorized to access sanitycheck.cgi. Access
# is restricted to logged-in users who have "editbugs" privileges,
@@ -41,7 +42,7 @@ my $userid = confirm_login();
# and restricting access to this installation's administrators (which
# prevents users with a legitimate interest in Bugzilla integrity
# from accessing the script).
UserInGroup($userid, "editbugs")
UserInGroup("editbugs")
|| DisplayError("You are not authorized to access this script,
which is reserved for users with the ability to edit bugs.")
&& exit;
@@ -49,7 +50,9 @@ UserInGroup($userid, "editbugs")
print "Content-type: text/html\n";
print "\n";
SendSQL("set SQL_BIG_TABLES=1");
if ($::driver eq 'mysql') {
SendSQL("set SQL_BIG_TABLES=1");
}
my $offervotecacherebuild = 0;
@@ -123,8 +126,10 @@ PutHeader("Bugzilla Sanity Check");
if (exists $::FORM{'rebuildvotecache'}) {
Status("OK, now rebuilding vote cache.");
SendSQL("lock tables bugs write, votes read");
SendSQL("update bugs set votes = 0, delta_ts=delta_ts");
if ($::driver eq 'mysql') {
SendSQL("lock tables bugs write, votes read");
}
SendSQL("update bugs set votes = 0, delta_ts=now()");
SendSQL("select bug_id, sum(count) from votes group by bug_id");
my %votes;
while (@row = FetchSQLData()) {
@@ -132,9 +137,11 @@ if (exists $::FORM{'rebuildvotecache'}) {
$votes{$id} = $v;
}
foreach my $id (keys %votes) {
SendSQL("update bugs set votes = $votes{$id}, delta_ts=delta_ts where bug_id = $id");
SendSQL("update bugs set votes = $votes{$id}, delta_ts=now() where bug_id = $id");
}
if ($::driver eq 'mysql') {
SendSQL("unlock tables");
}
SendSQL("unlock tables");
Status("Vote cache has been rebuilt.");
}
@@ -200,6 +207,24 @@ CrossCheck("profiles", "userid",
["components", "initialowner", "value"],
["components", "initialqacontact", "value", ["0"]]);
#Status("Checking passwords");
#SendSQL("SELECT COUNT(*) FROM profiles WHERE cryptpassword != ENCRYPT(password, left(cryptpassword, 2))");
#my $count = FetchOneColumn();
#if ($count) {
# Alert("$count entries have problems in their crypted password.");
# if ($::FORM{'rebuildpasswords'}) {
# Status("Rebuilding passwords");
# SendSQL("UPDATE profiles
# SET cryptpassword = ENCRYPT(password,
# left(cryptpassword, 2))
# WHERE cryptpassword != ENCRYPT(password,
# left(cryptpassword, 2))");
# Status("Passwords have been rebuilt.");
# } else {
# print qq{<a href="sanitycheck.cgi?rebuildpasswords=1">Click here to rebuild the crypted passwords</a><p>\n};
# }
#}
CrossCheck("products", "product",
["bugs", "product", "bug_id"],
["components", "program", "value"],
@@ -209,20 +234,37 @@ CrossCheck("products", "product",
Status("Checking groups");
my %legal_groups = ();
SendSQL("select group_id from groups order by group_id");
while ( my @row = FetchSQLData() ) {
$legal_groups{$row[0]} = 1;
if ($::driver eq 'mysql') {
SendSQL("select bit from groups");
} elsif ($::driver eq 'Pg') {
SendSQL("select group_bit from groups");
}
SendSQL("select distinct group_id from user_group_map order by group_id");
while ( my @row = FetchSQLData() ) {
Alert("Illegal group_id number found in user_group_map table: $row[0]") if !$legal_groups{$row[0]};
while (my $bit = FetchOneColumn()) {
if ( $bit != pow(2, int(log($bit) / log(2))) ) {
Alert("Illegal bit number found in group table: $bit");
}
}
SendSQL("select distinct group_id from bug_group_map order by group_id");
while ( my @row = FetchSQLData() ) {
Alert("Illegal group_id number found in bug_group_map table: $row[0]") if !$legal_groups{$row[0]};
}
if ($::driver eq 'mysql') {
SendSQL("select sum(bit) from groups where isbuggroup != 0");
} elsif ($::driver eq 'Pg') {
SendSQL("select sum(group_bit) from groups where isbuggroup != 0");
}
my $buggroupset = FetchOneColumn();
if (!defined $buggroupset || $buggroupset eq "") {
$buggroupset = 0;
}
if ($::driver eq 'mysql') {
SendSQL("select bug_id, groupset from bugs where groupset & $buggroupset != groupset");
} elsif ($::driver eq 'Pg') {
SendSQL("select bug_id, groupset from bugs where (groupset & int8($buggroupset)) != groupset");
}
while (@row = FetchSQLData()) {
Alert("Bad groupset $row[1] found in bug " . BugLink($row[0]));
}
Status("Checking version/products");
@@ -302,8 +344,7 @@ Status("Checking profile logins");
my $emailregexp = Param("emailregexp");
$emailregexp =~ s/'/\\'/g;
SendSQL("SELECT userid, login_name FROM profiles " .
"WHERE login_name NOT REGEXP '" . $emailregexp . "'");
"WHERE " . SqlRegEx("login_name", SqlQuote($emailregexp), "not"));
while (my ($id,$email) = (FetchSQLData())) {
Alert "Bad profile email address, id=$id, &lt;$email&gt;."
@@ -386,7 +427,9 @@ Status("Checking cached keywords");
my %realk;
if (exists $::FORM{'rebuildkeywordcache'}) {
SendSQL("LOCK TABLES bugs write, keywords read, keyworddefs read");
if ($::driver eq 'mysql') {
SendSQL("LOCK TABLES bugs write, keywords read, keyworddefs read");
}
}
SendSQL("SELECT keywords.bug_id, keyworddefs.name " .
@@ -435,10 +478,13 @@ if (@badbugs) {
if (exists($realk{$b})) {
$k = $realk{$b};
}
SendSQL("UPDATE bugs SET delta_ts = delta_ts, keywords = " .
SendSQL("UPDATE bugs SET delta_ts = now(), keywords = " .
SqlQuote($k) .
" WHERE bug_id = $b");
}
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
Status("Keyword cache fixed.");
} else {
print qq{<a href="sanitycheck.cgi?rebuildkeywordcache=1">Click here to rebuild the keyword cache</a><p>\n};
@@ -446,7 +492,9 @@ if (@badbugs) {
}
if (exists $::FORM{'rebuildkeywordcache'}) {
SendSQL("UNLOCK TABLES");
if ($::driver eq 'mysql') {
SendSQL("UNLOCK TABLES");
}
}
###########################################################################
@@ -597,6 +645,64 @@ if (@badbugs > 0) {
join (", ", @badbugs));
}
############################################################################
# Check for missing values in enum tables that are present in bugs table
############################################################################
foreach my $enum ( "bug_status", "resolution", "bug_severity", "op_sys", "priority", "rep_platform" ) {
my %bug_values;
my %table_values;
Status("Checking for orphan $enum entries");
SendSQL("select distinct $enum from bugs");
while ( my @row = FetchSQLData() ) {
$bug_values{$row[0]} = 1;
}
SendSQL("select value from $enum");
while ( my @row = FetchSQLData() ) {
$table_values{$row[0]} = 1;
}
foreach my $value ( keys %bug_values ) {
if ( !$table_values{$value} ) {
SendSQL("select count(bug_id) from bugs where $enum = " . SqlQuote($value));
my $count = FetchOneColumn();
Alert("There were $count bugs with a $enum value of $value which is not in the $enum enum table.");
}
}
}
###########################################################################
# Unsent mail
###########################################################################
Status("Checking for unsent mail");
@badbugs = ();
if ($::driver eq 'mysql') {
SendSQL("SELECT bug_id " .
"FROM bugs WHERE lastdiffed < delta_ts AND ".
"delta_ts < date_sub(now(), INTERVAL 30 minute) ".
"ORDER BY bug_id");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT bug_id " .
"FROM bugs WHERE lastdiffed < delta_ts AND ".
"now() - INTERVAL '30 minutes' > delta_ts ".
"ORDER BY bug_id");
}
while (@row = FetchSQLData()) {
my ($id) = (@row);
push(@badbugs, $id);
}
if (@badbugs > 0) {
Alert("Bugs that have changes but no mail sent for at least half an hour: " .
join (", ", @badbugs));
print("Run <code>processmail rescanall</code> to fix this<p>\n");
}
###########################################################################
# Unsent mail
###########################################################################

View File

@@ -36,12 +36,15 @@ ConnectToDatabase();
# Begin Data/Security Validation
###############################################################################
# Check whether or not the user is currently logged in.
my $userid = quietly_check_login();
# Check whether or not the user is currently logged in. This function
# sets the value of $::usergroupset, the binary number that records
# the set of groups to which the user belongs and which we can use
# to determine whether or not the user is authorized to access this bug.
quietly_check_login();
# Make sure the bug ID is a positive integer representing an existing
# bug that the user is authorized to access.
ValidateBugID($::FORM{'id'}, $userid);
ValidateBugID($::FORM{'id'});
###############################################################################
# End Data/Security Validation

View File

@@ -30,11 +30,10 @@ require "bug_form.pl";
ConnectToDatabase();
my $userid = 0;
if ($::FORM{'GoAheadAndLogIn'}) {
$userid = confirm_login();
confirm_login();
} else {
$userid = quietly_check_login();
quietly_check_login();
}
######################################################################
@@ -44,7 +43,7 @@ if ($::FORM{'GoAheadAndLogIn'}) {
# Make sure the bug ID is a positive integer representing an existing
# bug that the user is authorized to access.
if (defined ($::FORM{'id'})) {
ValidateBugID($::FORM{'id'}, $userid);
ValidateBugID($::FORM{'id'});
}
######################################################################

View File

@@ -23,6 +23,7 @@
use diagnostics;
use strict;
use MIME::Base64;
use lib qw(.);
@@ -34,4 +35,3 @@ my $id = $::FORM{'attach_id'} || "";
print "Status: 301 Permanent Redirect\n";
print "Location: attachment.cgi?id=$id&action=view\n\n";
exit;

View File

@@ -30,7 +30,7 @@ require "CGI.pl";
ConnectToDatabase();
my $userid = quietly_check_login();
quietly_check_login();
use vars qw($template $vars $userid $usergroupset);
@@ -99,7 +99,7 @@ if ($::FORM{'doall'}) {
} else {
foreach my $i (split('[\s,]+', $::FORM{'id'})) {
$i = trim($i);
ValidateBugID($i, $userid);
ValidateBugID($i);
$baselist{$i} = 1;
}
@@ -127,21 +127,22 @@ if ($::FORM{'doall'}) {
}
}
# Determine which bugs we can see or not
my @canseebugs = keys %seen;
my $canseeref = CanSeeBug(\@canseebugs, $userid);
my $canseeref = CanSeeBug(\@canseebugs, $::userid, $::usergroupset);
foreach my $k (keys(%seen)) {
# Skip this bug if we cannot see it
next if !$canseeref->{$k};
my $summary = "";
my $stat;
if ($::FORM{'showsummary'}) {
SendSQL("SELECT bug_status, short_desc FROM bugs WHERE bugs.bug_id = $k");
($stat, $summary) = FetchSQLData();
$stat = "NEW" if !defined $stat;
$summary = "" if !defined $summary;
if (!$canseeref->{$k}) {
SendSQL("SELECT bug_status FROM bugs WHERE bug_id = $k");
$stat = FetchOneColumn();
} else {
SendSQL("SELECT bug_status, short_desc FROM bugs WHERE bugs.bug_id = $k");
($stat, $summary) = FetchSQLData();
$stat = "NEW" if !defined $stat;
$summary = "" if !defined $summary;
}
} else {
SendSQL("SELECT bug_status FROM bugs WHERE bug_id = $k");
$stat = FetchOneColumn();

View File

@@ -36,7 +36,7 @@ use vars %::FORM;
ConnectToDatabase();
my $userid = quietly_check_login();
quietly_check_login();
# More warning suppression silliness.
$::userid = $::userid;
@@ -48,7 +48,7 @@ $::usergroupset = $::usergroupset;
# Make sure the bug ID is a positive integer representing an existing
# bug that the user is authorized to access.
ValidateBugID($::FORM{'id'}, $userid);
ValidateBugID($::FORM{'id'});
my $id = $::FORM{'id'};
my $hide_resolved = $::FORM{'hide_resolved'} ? 1 : 0;
@@ -144,9 +144,9 @@ sub GetBug {
# Retrieves the necessary information about a bug, stores it in the bug cache,
# and returns it to the calling code.
my ($id) = @_;
return {} if !CanSeeBug($id, $userid);
return {} if !CanSeeBug($id, $::userid, $::usergroupset);
SendSQL("SELECT 1,
bug_status,
short_desc,
@@ -155,7 +155,7 @@ sub GetBug {
assignee.login_name
FROM bugs, profiles AS assignee
WHERE bugs.bug_id = $id
AND bugs.assigned_to = assignee.userid");
AND bugs.assigned_to = assignee.userid");
my $bug = {};

View File

@@ -34,7 +34,7 @@ ConnectToDatabase();
GetVersionTable();
# Check to see if the user has logged in yet.
my $userid = quietly_check_login();
quietly_check_login();
###############################################################################
# Main Body Execution
@@ -42,11 +42,13 @@ my $userid = quietly_check_login();
$vars->{'username'} = $::COOKIE{'Bugzilla_login'} || '';
$vars->{'anyvotesallowed'} = $::anyvotesallowed;
$vars->{'userid'} = $userid;
if ($userid) {
SendSQL("SELECT mybugslink FROM profiles WHERE user_id = $userid");
my $mybugslink = FetchOneColumn();
if (defined $::COOKIE{'Bugzilla_login'}) {
SendSQL("SELECT mybugslink, userid, blessgroupset FROM profiles " .
"WHERE login_name = " . SqlQuote($::COOKIE{'Bugzilla_login'}));
my ($mybugslink, $userid, $blessgroupset) = (FetchSQLData());
$vars->{'userid'} = $userid;
$vars->{'blessgroupset'} = $blessgroupset;
if ($mybugslink) {
my $mybugstemplate = Param("mybugstemplate");
my %substs = ( 'userid' => url_quote($::COOKIE{'Bugzilla_login'}) );
@@ -57,8 +59,6 @@ if ($userid) {
my ($name) = FetchSQLData();
push(@{$vars->{'namedqueries'}}, $name);
}
SendSQL("SELECT COUNT(*) FROM user_group_map WHERE user_id = $userid AND canbless >= 1");
$vars->{'canbless'} = FetchOneColumn();
}
# This sidebar is currently for use with Mozilla based web browsers.

View File

@@ -1,339 +0,0 @@
[%# 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): Myk Melez <myk@mozilla.org>
#%]
<script type="text/javascript" language="JavaScript">
var numelements = document.forms.changeform.elements.length;
function SetCheckboxes(value) {
var item;
for (var i=0 ; i<numelements ; i++) {
item = document.forms.changeform.elements[i];
item.checked = value;
}
}
document.write(' <input type="button" value="Uncheck All" onclick="SetCheckboxes(false);">');
document.write(' <input type="button" value="Check All" onclick="SetCheckboxes(true);">');
</script>
<hr />
<p><font size="-1">
To change multiple bugs:
<ol>
<li>Check the bugs you want to change above.</li>
<li>Make your changes in the form fields below. If the change
you are making requires an explanation, include it in
the comments box.</li>
<li>Click the <em>Commit</em> button.</li>
</ol>
</font></p>
<table id="form">
<tr>
<th><label for="product">Product:</label></th>
<td>
[% PROCESS selectmenu menuname = "product"
menuitems = products %]
</td>
<th><label for="version">Version:</label></th>
<td>
[% PROCESS selectmenu menuname = "version"
menuitems = versions %]
</td>
</tr>
<tr>
<th>
<label for="rep_platform">
<a href="bug_status.html#rep_platform">Platform:</a>
</label>
</th>
<td>
[% PROCESS selectmenu menuname = "rep_platform"
menuitems = platforms %]
</td>
<th>
<label for="priority">
<a href="bug_status.html#priority">Priority:</a>
</label>
</th>
<td>
[% PROCESS selectmenu menuname = "priority"
menuitems = priorities %]
</td>
</tr>
<tr>
<th><label for="component">Component:</label></th>
<td>
[% PROCESS selectmenu menuname = "component"
menuitems = components %]
</td>
<th>
<label for="severity">
<a href="bug_status.html#severity">Severity:</a>
</label>
</th>
<td>
[% PROCESS selectmenu menuname = "severity"
menuitems = severities %]
</td>
</tr>
<tr>
<th><label for="target_milestone">Target Milestone:</label></th>
<td colspan="3">
[% PROCESS selectmenu menuname = "target_milestone"
menuitems = targetmilestones %]
</td>
</tr>
[% IF Param("useqacontact") %]
<tr>
<th><label for="qa_contact">QA Contact:</label></th>
<td colspan="3">
<input id="qa_contact"
name="qa_contact"
value="[% dontchange FILTER html %]"
size="32">
</td>
</tr>
[% END %]
<tr>
<th><label for="masscc">CC List:</label></th>
<td colspan="3">
<input id="masscc" name="masscc" size="32">
<select name="ccaction">
<option value="add">Add these to the CC List</option>
<option value="remove">Remove these from the CC List</option>
</select>
</td>
</tr>
[% IF use_keywords %]
<tr>
<th>
<label for="keywords">
<a href="describekeywords.cgi">Keywords:</a>
</label>
</th>
<td colspan="3">
<input id="keywords" name="keywords" size="32">
<select name="keywordaction">
<option value="add">Add these keywords</option>
<option value="delete">Delete these keywords</option>
<option value="makeexact">Make the keywords be exactly this list</option>
</select>
</td>
</tr>
[% END %]
<tr>
<th>Depends on:</th>
<td colspan="3">
<input id="dependson" name="dependson" size="32">
<select name="dependsonaction">
<option value="add">Add these dependencies</option>
<option value="delete">Remove these dependencies</option>
<option value="makeexact">Make the dependencies be exactly this list</option>
</select>
</td>
</tr>
<tr>
<th>Blocks:</th>
<td colspan="3">
<input id="blocked" name="blocked" size="32">
<select name="blockedaction">
<option value="add">Add these dependencies</option>
<option value="delete">Remove these dependencies</option>
<option value="makeexact">Make the dependencies be exactly this list</option>
</select>
</td>
</tr>
</table>
<input type="hidden" name="multiupdate" value="Y">
<label for="comment"><b>Additional Comments:</b></label><br />
<textarea id="comment" name="comment" rows="5" cols="80" wrap="hard"></textarea><br />
[% IF groups.size > 0 %]
<b>Groupset:</b><br />
<table border="1">
<tr>
<th>Don't<br />change<br />this group<br />restriction</td>
<th>Remove<br />bugs<br />from this<br />group</td>
<th>Add<br />bugs<br />to this<br />group</td>
<th>Group Name:</td>
</tr>
[% FOREACH group = groups %]
<tr>
<td align="center">
<input type="radio" name="group-[% group.bit %]" value="-1" checked>
</td>
<td align="center">
<input type="radio" name="group-[% group.bit %]" value="0">
</td>
[% IF group.isactive %]
<td align="center">
<input type="radio" name="group-[% group.bit %]" value="1">
</td>
[% ELSE %]
<td>&nbsp;</td>
[% foundinactive = 1 %]
[% END %]
<td>
[% IF group.isactive %]
[% group.description %]
[% ELSE %]
[% group.description FILTER strike %]
[% END %]
</td>
</tr>
[% END %]
</table>
[% IF foundinactive %]
<font size="-1">(Note: Bugs may not be added to <strike>inactive
groups</strike>, only removed.)</font><br />
[% END %]
[% END %]
[% knum = 0 %]
<input id="knob-none" type="radio" name="knob" value="none" CHECKED>
<label for="knob-none">Do nothing else</label><br />
[% IF bugstatuses.size == 1 && bugstatuses.0 == unconfirmedstate %]
[% knum = knum + 1 %]
<input id="knob-confirm" type="radio" name="knob" value="confirm>
<label for="knob-confirm">
Confirm bugs (change status to <b>NEW</b>)
</label><br />
[% END %]
[% knum = knum + 1 %]
<input id="knob-accept" type="radio" name="knob" value="accept">
<label for="knob-accept">
Accept bugs (change status to <b>ASSIGNED</b>)
</label><br />
[%# If all the bugs being changed are open, allow the user to close them. %]
[% IF !bugstatuses.containsany(closedstates) %]
[% knum = knum + 1 %]
<input id="knob-clearresolution" type="radio" name="knob" value="clearresolution">
<label for="knob-clearresolution">Clear the resolution</label><br />
[% knum = knum + 1 %]
<input id="knob-resolve" type="radio" name="knob" value="resolve">
<label for="knob-resolve">
Resolve bugs, changing <A HREF="bug_status.html">resolution</A> to
</label>
<select name="resolution" onchange="document.forms.changeform.knob[[% knum %]].checked=true">
[% FOREACH resolution = resolutions %]
[% NEXT IF !resolution %]
<option value="[% resolution %]" [% selected IF resolution == "FIXED" %]>
[% resolution %]
</option>
[% END %]
</select><br />
[% END %]
[%# If all the bugs are closed, allow the user to reopen them. %]
[% IF !bugstatuses.containsany(openstates) %]
[% knum = knum + 1 %]
<input id="knob-reopen" type="radio" name="knob" value="reopen">
<label for="knob-reopen">Reopen bugs</label><br />
[% END %]
[% IF bugstatuses.size == 1 %]
[% IF bugstatuses.contains('RESOLVED') %]
[% knum = knum + 1 %]
<input id="knob-verify" type="radio" name="knob" value="verify">
<label for="knob-verify">Mark bugs as <b>VERIFIED</b></label><br />
[% ELSIF bugstatuses.contains('VERIFIED') %]
[% knum = knum + 1 %]
<input id="knob-close" type="radio" name="knob" value="close">
<label for="knob-close">Mark bugs as <b>CLOSED</b></label><br />
[% END %]
[% END %]
[% knum = knum + 1 %]
<input id="knob-reassign" type="radio" name="knob" value="reassign">
<label for="knob-reassign"><a href="bug_status.html#assigned_to">
Reassign</A> bugs to
</label>
<input name="assigned_to"
value="[% user %]"
onchange="document.forms.changeform.knob[[% knum %]].checked = true;"
size="32"><br />
[% knum = knum + 1 %]
<input id="knob-reassignbycomponent"
type="radio"
name="knob"
value="reassignbycomponent">
<label for="knob-reassignbycomponent">
Reassign bugs to owner of selected component
</label><br />
<input type="submit" value="Commit">
[% IF ismover %]
<input type="submit" name="action" value="[% Param('move-button-text') %]">
[% END %]
[%############################################################################%]
[%# Select Menu Block #%]
[%############################################################################%]
[% BLOCK selectmenu %]
<select id="[% menuname %]" name="[% menuname %]">
<option value="[% dontchange FILTER html %]" selected>
[% dontchange FILTER html %]
</option>
[% FOREACH menuitem = menuitems %]
<option value="[% menuitem FILTER html %]">[% menuitem FILTER html %]</option>
[% END %]
</select>
[% END %]

View File

@@ -1,256 +0,0 @@
[%# 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): Gervase Markham <gerv@gerv.net>
# Ville Skyttä <ville.skytta@iki.fi>
#%]
[% INCLUDE global/header
title = "Enter Bug"
h2 = "This page lets you enter a new bug into Bugzilla."
%]
<form method="post" action="post_bug.cgi">
<input type="hidden" name="product" value="[% product FILTER html %]" />
<table cellspacing="2" cellpadding="0" border="0">
<tr>
<td><br /></td>
</tr>
<tr>
<td></td>
<td colspan="3">
Before filling this in, please read the
<a href="bugwritinghelp.html">bug-writing guidelines</a>.
</td>
</tr>
<tr>
<td><br /></td>
</tr>
<tr>
<td align="right" valign="top"><strong>Reporter:</strong></td>
<td valign="top">[% reporter FILTER html %]</td>
<td align="right" valign="top"><strong>Product:</strong></td>
<td valign="top">[% product FILTER html %]</td>
</tr>
[%# We can't use the select block in these two cases for various reasons. %]
<tr>
<td align="right" valign="top">
<strong>Version:</strong>
</td>
<td>
<select name="version" size="5">
[%- FOREACH v = version %]
<option value="[% v FILTER html %]"
[% " selected=\"selected\"" IF v == default.version %]>[% v FILTER html -%]
</option>
[%- END %]
</select>
</td>
<td align="right" valign="top">
<strong>
<a href="describecomponents.cgi?product=[% product FILTER uri %]">
Component:</a>
</strong>
</td>
<td>
<select name="component" size="5">
[%- FOREACH c = component_ %]
<option value="[% c FILTER html %]"
[% " selected=\"selected\"" IF c == default.component_ %]>[% c FILTER html -%]
</option>
[%- END %]
</select>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"></td>
</tr>
<tr>
[% sel = { description => 'Platform', name => 'rep_platform' } %]
[% INCLUDE select %]
[% sel = { description => 'OS', name => 'op_sys' } %]
[% INCLUDE select %]
</tr>
<tr>
[% IF Param('letsubmitterchoosepriority') %]
[% sel = { description => 'Priority', name => 'priority' } %]
[% INCLUDE select %]
[% ELSE %]
<input type="hidden" name="priority" value="[% default.priority %]" />
[% END %]
[% sel = { description => 'Severity', name => 'bug_severity' } %]
[% INCLUDE select %]
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"></td>
</tr>
[% IF bug_status.size > 1 %]
<tr>
[% sel = { description => 'Initial State', name => 'bug_status' } %]
[% INCLUDE select %]
<td colspan="2"></td>
</tr>
[% ELSE %]
<input type="hidden" name="bug_status" value="[% default.bug_status %]" />
[% END %]
<tr>
<td align="right">
<strong>
<a href="bug_status.html#assigned_to">Assigned To:</a>
</strong>
</td>
<td colspan="3">
<input name="assigned_to" size="32"
value="[% assigned_to FILTER html %]" />
(Leave blank to assign to default component owner)
</td>
</tr>
<tr>
<td align="right"><strong>Cc:</strong></td>
<td colspan="3">
<input name="cc" size="45" value="[% cc FILTER html %]" />
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"></td>
</tr>
<tr>
<td align="right"<strong>URL:</strong></td>
<td colspan="3">
<input name="bug_file_loc" size="60"
value="[% bug_file_loc FILTER html %]" />
</td>
</tr>
<tr>
<td align="right"><strong>Summary:</strong></td>
<td colspan="3">
<input name="short_desc" size="60" value="[% short_desc FILTER html %]" />
</td>
</tr>
<tr><td align="right" valign="top"><strong>Description:</strong></td>
<td colspan="3">
<textarea wrap="hard" name="comment" rows="10" cols="80">
[% comment FILTER html %]</textarea>
<br />
</td>
</tr>
<tr>
<td></td>
<td colspan="3">
[% IF group.size %]
<br />
<strong>
Only users in the selected groups can view this bug:
</strong>
<br />
<font size="-1">
(Leave all boxes unchecked to make this a public bug.)
</font>
<br />
<br />
<!-- Checkboxes -->
[% FOREACH g = group %]
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="checkbox" name="group-[% g.bit %]" value="1"
[% " checked=\"checked\"" IF g.checked %] />[% g.description %]<br />
[% END %]
<br />
[% END %]
</td>
</tr>
<tr>
<td></td>
<td colspan="3">
<input type="submit" value=" Commit "
onclick="if (this.form.short_desc.value == '')
{ alert('Please enter a summary sentence for this bug.');
return false; } return true;">
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="submit" name="maketemplate"
value="Remember values as bookmarkable template" />
</td>
</tr>
[% IF Param('usebrowserinfo') %]
<tr>
<td></td>
<td colspan="3">
<br />
We've made a guess at your operating system and platform.
Please check them and, if we got it wrong, email
[% Param('maintainer') %].
</td>
</tr>
[% END %]
</table>
<input type="hidden" name="form_name" value="enter_bug" />
</form>
[% INCLUDE global/footer %]
[%############################################################################%]
[%# Block for SELECT fields #%]
[%############################################################################%]
[% BLOCK select %]
[% IF sel.description %]
<td align="right">
<strong>
<a href="bug_status.html#[% sel.name %]">[% sel.description %]:</a>
</strong>
</td>
[% END %]
<td>
<select name="[% sel.name %]">
[%- FOREACH x = ${sel.name} %]
<option value="[% x FILTER html %]"
[% " selected=\"selected\"" IF x == default.${sel.name} %]>[% x FILTER html -%]
</option>
[%- END %]
</select>
</td>
[% END %]

View File

@@ -1,21 +0,0 @@
[%# 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): Gervase Markham <gerv@gerv.net>
#%]
[% form.comment %]

View File

@@ -212,12 +212,12 @@ sub changePassword {
# Update the user's password in the profiles table and delete the token
# from the tokens table.
SendSQL("LOCK TABLES profiles WRITE , tokens WRITE");
SendSQL("LOCK TABLE profiles WRITE , tokens WRITE") if $::driver eq 'mysql';
SendSQL("UPDATE profiles
SET cryptpassword = $quotedpassword
WHERE userid = $userid");
SendSQL("DELETE FROM tokens WHERE token = $::quotedtoken");
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
InvalidateLogins($userid);
@@ -264,14 +264,14 @@ sub changeEmail {
# Update the user's login name in the profiles table and delete the token
# from the tokens table.
SendSQL("LOCK TABLES profiles WRITE , tokens WRITE");
SendSQL("LOCK TABLES profiles WRITE , tokens WRITE") if $::driver eq 'mysql';
SendSQL("UPDATE profiles
SET login_name = $quotednewemail
WHERE userid = $userid");
SendSQL("DELETE FROM tokens WHERE token = $::quotedtoken");
SendSQL("DELETE FROM tokens WHERE userid = $userid
AND tokentype = 'emailnew'");
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
# Return HTTP response headers.
print "Content-Type: text/html\n\n";
@@ -303,11 +303,11 @@ sub cancelChangeEmail {
if($actualemail ne $old_email) {
my $quotedoldemail = SqlQuote($old_email);
SendSQL("LOCK TABLES profiles WRITE");
SendSQL("LOCK TABLES profiles WRITE") if $::driver eq 'mysql';
SendSQL("UPDATE profiles
SET login_name = $quotedoldemail
WHERE userid = $userid");
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
$vars->{'message'} .=
" Your old account settings have been reinstated.";
}
@@ -318,11 +318,11 @@ sub cancelChangeEmail {
}
Token::Cancel($::token, $vars->{'message'});
SendSQL("LOCK TABLES tokens WRITE");
SendSQL("LOCK TABLES tokens WRITE") if $::driver eq 'mysql';
SendSQL("DELETE FROM tokens
WHERE userid = $userid
AND tokentype = 'emailold' OR tokentype = 'emailnew'");
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
# Return HTTP response headers.
print "Content-Type: text/html\n\n";

View File

@@ -34,6 +34,7 @@ use RelationSet;
sub sillyness {
my $zz;
$zz = $::defaultqueryname;
$zz = $::usergroupset;
}
# Use global template variables.
@@ -66,11 +67,19 @@ sub DoAccount {
$vars->{'realname'} = FetchSQLData();
if(Param('allowemailchange')) {
SendSQL("SELECT tokentype, issuedate + INTERVAL 3 DAY, eventdata
if ($::driver eq 'mysql') {
SendSQL("SELECT tokentype, issuedate + INTERVAL 3 DAY, eventdata
FROM tokens
WHERE userid = $userid
AND tokentype LIKE 'email%'
ORDER BY tokentype ASC LIMIT 1");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT tokentype, issuedate + INTERVAL '3 days', eventdata
FROM tokens
WHERE userid = $userid
AND tokentype LIKE 'email%'
ORDER BY tokentype ASC LIMIT 1");
}
if(MoreSQLData()) {
my ($tokentype, $change_date, $eventdata) = &::FetchSQLData();
$vars->{'login_change_date'} = $change_date;
@@ -273,7 +282,7 @@ sub SaveEmail {
($CCDELTAS[0] eq "") || SendSQL($CCDELTAS[0]);
($CCDELTAS[1] eq "") || SendSQL($CCDELTAS[1]);
SendSQL("UNLOCK TABLES");
SendSQL("UNLOCK TABLES") if $::driver eq 'mysql';
}
}
@@ -329,22 +338,32 @@ sub SaveFooter {
sub DoPermissions {
my (@has_bits, @set_bits);
SendSQL("SELECT description FROM groups, user_group_map " .
"WHERE groups.group_id = user_group_map.group_id " .
"AND user_group_map.user_id = $userid " .
"ORDER BY description");
if ($::driver eq 'mysql') {
SendSQL("SELECT description FROM groups " .
"WHERE (bit & $::usergroupset) != 0 " .
"ORDER BY bit");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT description FROM groups " .
"WHERE (group_bit & int8($::usergroupset)) != 0 " .
"ORDER BY group_bit");
}
while (MoreSQLData()) {
push(@has_bits, FetchSQLData());
}
SendSQL("SELECT COUNT(*) FROM user_group_map WHERE user_id = $userid AND canbless >= 1");
my $blessgroups = FetchOneColumn();
if ($blessgroups) {
SendSQL("SELECT description FROM groups, user_group_map " .
"WHERE groups.group_id = user_group_map.group_id " .
"AND user_group_map.user_id = $userid " .
"AND canbless = 1 " .
"ORDER BY description");
SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $userid");
my $blessgroupset = FetchOneColumn();
if ($blessgroupset) {
if ($::driver eq 'mysql') {
SendSQL("SELECT description FROM groups " .
"WHERE (bit & $blessgroupset) != 0 " .
"ORDER BY bit");
} elsif ($::driver eq 'Pg') {
SendSQL("SELECT description FROM groups " .
"WHERE (group_bit & int8($blessgroupset)) != 0 " .
"ORDER BY group_bit");
}
while (MoreSQLData()) {
push(@set_bits, FetchSQLData());
}
@@ -359,7 +378,7 @@ sub DoPermissions {
###############################################################################
# Live code (not subroutine definitions) starts here
###############################################################################
$userid = confirm_login();
confirm_login();
GetVersionTable();
@@ -397,4 +416,3 @@ SWITCH: for ($current_tab_name) {
print "Content-type: text/html\n\n";
$template->process("account/prefs/prefs.html.tmpl", $vars)
|| ThrowTemplateError($template->error());

View File

@@ -47,14 +47,13 @@ ConnectToDatabase();
my $action = $::FORM{'action'} ||
($::FORM{'bug_id'} ? "show_bug" : "show_user");
my $userid = 0;
if ($action eq "show_bug" ||
($action eq "show_user" && defined($::FORM{'user'})))
{
$userid = quietly_check_login();
quietly_check_login();
}
else {
$userid = confirm_login();
confirm_login();
}
################################################################################
@@ -64,7 +63,7 @@ else {
# Make sure the bug ID is a positive integer representing an existing
# bug that the user is authorized to access.
if (defined $::FORM{'bug_id'}) {
ValidateBugID($::FORM{'bug_id'}, $userid);
ValidateBugID($::FORM{'bug_id'});
}
################################################################################
@@ -214,7 +213,7 @@ sub show_user {
SendSQL("DELETE FROM votes WHERE count <= 0");
SendSQL("UNLOCK TABLES");
$vars->{'user'} = { canedit => $canedit, name => $name, id => $who };
$vars->{'voting_user'} = { "login" => $name };
$vars->{'products'} = \@products;
print "Content-type: text/html\n\n";
@@ -254,7 +253,7 @@ sub record_votes {
# a non-negative integer (a series of digits not preceded by a
# minus sign).
foreach my $id (@buglist) {
ValidateBugID($id, $userid);
ValidateBugID($id);
detaint_natural($::FORM{$id})
|| DisplayError("Only use non-negative numbers for your bug votes.")
&& exit;

View File

@@ -33,9 +33,15 @@ require "globals.pl";
ConnectToDatabase();
SendSQL("select bug_id,login_name from bugs,profiles where " .
"bug_status = 'NEW' and to_days(now()) - to_days(delta_ts) > " .
Param('whinedays') . " and userid=assigned_to order by bug_id");
if ($::driver eq 'mysql') {
SendSQL("select bug_id,login_name from bugs,profiles where " .
"bug_status = 'NEW' and to_days(now()) - to_days(delta_ts) > " .
Param('whinedays') . " and userid=assigned_to order by bug_id");
} elsif ($::driver eq 'Pg') {
SendSQL("select bug_id,login_name from bugs,profiles where " .
"bug_status = 'NEW' and to_days(now()) - to_days(delta_ts) > '" .
Param('whinedays') . " days' and userid=assigned_to order by bug_id");
}
my %bugs;
my @row;

View File

@@ -39,7 +39,7 @@ if (!defined $::FORM{'id'} || !$::FORM{'id'}) {
exit;
}
my $userid = quietly_check_login();
quietly_check_login();
my $exporter = $::COOKIE{"Bugzilla_login"} || undef;
@@ -49,7 +49,7 @@ print "Content-type: text/xml\n\n";
print Bug::XML_Header(Param("urlbase"), $::param{'version'},
Param("maintainer"), $exporter);
foreach my $id (@ids) {
my $bug = new Bug(trim($id), $userid);
my $bug = new Bug(trim($id), $::userid);
print $bug->emitXML;
}