Compare commits
38 Commits
Bugzilla_G
...
Bugzilla_P
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c591d53e2 | ||
|
|
c1aa983fd5 | ||
|
|
3551227412 | ||
|
|
d0cc91f285 | ||
|
|
65ff7d56b3 | ||
|
|
800eccde9a | ||
|
|
5360e5b008 | ||
|
|
da759055dd | ||
|
|
1f960bb1bd | ||
|
|
e0f4b89db1 | ||
|
|
025b6e8e46 | ||
|
|
704f46aa53 | ||
|
|
f26338df7e | ||
|
|
58548c3f0d | ||
|
|
9a6b4393ad | ||
|
|
4316819604 | ||
|
|
9d93dfabb8 | ||
|
|
d2ddb07675 | ||
|
|
66d426dc97 | ||
|
|
b7e91cb3b6 | ||
|
|
5ac0899827 | ||
|
|
4f49e57a3b | ||
|
|
38c27be28f | ||
|
|
d60d3d6121 | ||
|
|
db0b87fb6c | ||
|
|
6e2791a4b7 | ||
|
|
14542c62c7 | ||
|
|
38ebcba576 | ||
|
|
a5502157a9 | ||
|
|
ba69b37618 | ||
|
|
22b863a5e9 | ||
|
|
3e54979994 | ||
|
|
d73ca44c76 | ||
|
|
a4fc52b12e | ||
|
|
353baca797 | ||
|
|
4618ab6c36 | ||
|
|
faaed9c15f | ||
|
|
675f64d0ae |
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
######################################################################
|
||||
|
||||
@@ -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)
|
||||
|
||||
306
mozilla/webtools/bugzilla/docs/html/cust-templates.html
Normal file
306
mozilla/webtools/bugzilla/docs/html/cust-templates.html
Normal 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
|
||||
> 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
|
||||
> 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
|
||||
> 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
|
||||
> 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
|
||||
> 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
|
||||
> 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
|
||||
> There are a few templates you may be particularly interested in
|
||||
customising for your installation.
|
||||
</P
|
||||
><P
|
||||
> <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
|
||||
> 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"
|
||||
>&format=simple</TT
|
||||
> to a buglist.cgi
|
||||
URL on your Bugzilla installation.) This
|
||||
mechanism, called template 'formats', is extensible.
|
||||
</P
|
||||
><P
|
||||
> 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
|
||||
> 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
|
||||
> Write your template in whatever markup or text style is appropriate.
|
||||
</P
|
||||
><P
|
||||
> 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
|
||||
> Save the template as <TT
|
||||
CLASS="filename"
|
||||
><stubname>-<formatname>.<contenttypetag>.tmpl</TT
|
||||
>.
|
||||
Try out the template by calling the CGI as
|
||||
<TT
|
||||
CLASS="filename"
|
||||
><cginame>.cgi?format=<formatname></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
|
||||
>
|
||||
390
mozilla/webtools/bugzilla/docs/html/extraconfig.html
Normal file
390
mozilla/webtools/bugzilla/docs/html/extraconfig.html
Normal 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
|
||||
> <TT
|
||||
CLASS="computeroutput"
|
||||
> <TT
|
||||
CLASS="prompt"
|
||||
>bash#</TT
|
||||
>
|
||||
|
||||
<B
|
||||
CLASS="command"
|
||||
>crontab -e</B
|
||||
>
|
||||
</TT
|
||||
>
|
||||
</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
> <TT
|
||||
CLASS="computeroutput"
|
||||
>5 0 * * * cd <your-bugzilla-directory> ;
|
||||
./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
|
||||
> 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
|
||||
> <TT
|
||||
CLASS="computeroutput"
|
||||
> <B
|
||||
CLASS="command"
|
||||
>cd <your-bugzilla-directory> ;
|
||||
./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
|
||||
> <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
|
||||
> 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
|
||||
> 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
|
||||
>
|
||||
431
mozilla/webtools/bugzilla/docs/html/groups.html
Normal file
431
mozilla/webtools/bugzilla/docs/html/groups.html
Normal 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 Groups example ----------------------- For<br>
|
||||
this example, let us suppose we have four groups, call them Group1,<br>
|
||||
Group2, Group3, and Group4. We have 5 users, User1, User2, User3,<br>
|
||||
User4, User5. We have 8 bugs, Bug1, ..., Bug8. Group membership is<br>
|
||||
defined by this chart: (X denotes that user is in that group.) (I<br>
|
||||
apologize for the nasty formatting of this table. Try viewing it in a<br>
|
||||
text-based browser or something for now. -MPB) G G G G r r r r o o o<br>
|
||||
o u u u u p p p p 1 2 3 4 +-+-+-+-+ User1|X| | | | +-+-+-+-+ User2|<br>
|
||||
|X| | | +-+-+-+-+ User3|X| |X| | +-+-+-+-+ User4|X|X|X| | +-+-+-+-+<br>
|
||||
User5| | | | | +-+-+-+-+ Bug restrictions are defined by this chart:<br>
|
||||
(X denotes that bug is restricted to that group.) G G G G r r r r o o<br>
|
||||
o o u u u u p p p p 1 2 3 4 +-+-+-+-+ Bug1| | | | | +-+-+-+-+ Bug2|<br>
|
||||
|X| | | +-+-+-+-+ Bug3| | |X| | +-+-+-+-+ Bug4| | | |X| +-+-+-+-+<br>
|
||||
Bug5|X|X| | | +-+-+-+-+ Bug6|X| |X| | +-+-+-+-+ Bug7|X|X|X| |<br>
|
||||
+-+-+-+-+ Bug8|X|X|X|X| +-+-+-+-+ Who can see each bug? Bug1 has no<br>
|
||||
group restrictions. Therefore, Bug1 can be seen by any user, whatever<br>
|
||||
their group membership. This is going to be the only bug that User5<br>
|
||||
can see, because User5 isn't in any groups. Bug2 can be seen by<br>
|
||||
anyone in Group2, that is User2 and User4. Bug3 can be seen by anyone<br>
|
||||
in Group3, that is User3 and User4. Bug4 can be seen by anyone in<br>
|
||||
Group4. Nobody is in Group4, so none of these users can see Bug4.<br>
|
||||
Bug5 can be seen by anyone who is in _both_ Group1 and Group2. This<br>
|
||||
is only User4. User1 cannot see it because he is not in Group2, and<br>
|
||||
User2 cannot see it because she is not in Group1. Bug6 can be seen by<br>
|
||||
anyone who is in both Group1 and Group3. This would include User3 and<br>
|
||||
User4. Similar to Bug5, User1 cannot see Bug6 because he is not in<br>
|
||||
Group3. Bug7 can be seen by anyone who is in Group1, Group2, and<br>
|
||||
Group3. This is only User4. All of the others are missing at least<br>
|
||||
one of those group privileges, and thus cannot see the bug. Bug8 can<br>
|
||||
be seen by anyone who is in Group1, Group2, Group3, and Group4. There<br>
|
||||
is nobody in all four of these groups, so nobody can see Bug8. It<br>
|
||||
doesn't matter that User4 is in Group1, Group2, and Group3, since he<br>
|
||||
isn't in 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
|
||||
>
|
||||
296
mozilla/webtools/bugzilla/docs/html/hintsandtips.html
Normal file
296
mozilla/webtools/bugzilla/docs/html/hintsandtips.html
Normal 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
|
||||
> 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
|
||||
> 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
|
||||
> 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
|
||||
>
|
||||
153
mozilla/webtools/bugzilla/docs/html/introduction.html
Normal file
153
mozilla/webtools/bugzilla/docs/html/introduction.html
Normal 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"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>What is Bugzilla?</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
282
mozilla/webtools/bugzilla/docs/html/troubleshooting.html
Normal file
282
mozilla/webtools/bugzilla/docs/html/troubleshooting.html
Normal 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
|
||||
> 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
|
||||
> To fix this, go to <path-to-perl>/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->{'NUM_OF_FIELDS'}) {
|
||||
$numFields = $attribs->{'NUM_OF_FIELDS'};
|
||||
} elsif ($attribs->{'NAME'}) {
|
||||
$numFields = @{$attribs->{NAME}};
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> by
|
||||
</P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> my $numFields;
|
||||
if ($attribs->{'NUM_OF_FIELDS'}) {
|
||||
$numFields = $attribs->{'NUM_OF_FIELDS'};
|
||||
} elsif ($attribs->{'NAMES'}) {
|
||||
$numFields = @{$attribs->{NAMES}};
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> (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
|
||||
> 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
|
||||
>
|
||||
241
mozilla/webtools/bugzilla/docs/html/userpreferences.html
Normal file
241
mozilla/webtools/bugzilla/docs/html/userpreferences.html
Normal 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
|
||||
>
|
||||
235
mozilla/webtools/bugzilla/docs/html/voting.html
Normal file
235
mozilla/webtools/bugzilla/docs/html/voting.html
Normal 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
|
||||
>
|
||||
22
mozilla/webtools/bugzilla/docs/sgml/gd-makefile.patch
Normal file
22
mozilla/webtools/bugzilla/docs/sgml/gd-makefile.patch
Normal 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') {
|
||||
146
mozilla/webtools/bugzilla/docs/sgml/introduction.sgml
Normal file
146
mozilla/webtools/bugzilla/docs/sgml/introduction.sgml
Normal 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:
|
||||
-->
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 .
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.";
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
2783
mozilla/webtools/bugzilla/pgsetup.pl
Executable file
2783
mozilla/webtools/bugzilla/pgsetup.pl
Executable file
File diff suppressed because it is too large
Load Diff
@@ -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'});
|
||||
|
||||
|
||||
@@ -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'} = \"eUrls;
|
||||
|
||||
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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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> </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> </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> </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> </td><td align="center" valign="middle"><b>AND</b></td></tr><tr><td> </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> </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> </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
|
||||
|
||||
@@ -42,7 +42,7 @@ if ($action eq "show") {
|
||||
my @quips;
|
||||
push (@quips, $_) while (<COMMENTS>);
|
||||
close COMMENTS;
|
||||
|
||||
|
||||
$vars->{'quips'} = \@quips;
|
||||
$vars->{'show_quips'} = 1;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, <$email>."
|
||||
@@ -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
|
||||
###########################################################################
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'});
|
||||
}
|
||||
|
||||
######################################################################
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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> </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 %]
|
||||
@@ -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> </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> </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> </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 %]
|
||||
|
||||
<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;">
|
||||
|
||||
<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 %]
|
||||
@@ -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 %]
|
||||
@@ -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";
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user