Compare commits
3 Commits
kiko_UI_20
...
Bugzilla_P
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b52a1dc48a | ||
|
|
48b5f960fd | ||
|
|
73bf11394f |
|
Before Width: | Height: | Size: 82 B After Width: | Height: | Size: 82 B |
@@ -24,41 +24,18 @@
|
||||
# Module Initialization
|
||||
############################################################################
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Attachment;
|
||||
package Attachment;
|
||||
|
||||
# This module requires that its caller have said "require CGI.pl" to import
|
||||
# relevant functions from that script and its companion globals.pl.
|
||||
|
||||
# Use the Flag module to handle flags.
|
||||
use Bugzilla::Flag;
|
||||
|
||||
############################################################################
|
||||
# Functions
|
||||
############################################################################
|
||||
|
||||
sub new {
|
||||
# Returns a hash of information about the attachment with the given ID.
|
||||
|
||||
my ($invocant, $id) = @_;
|
||||
return undef if !$id;
|
||||
my $self = { 'id' => $id };
|
||||
my $class = ref($invocant) || $invocant;
|
||||
bless($self, $class);
|
||||
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("SELECT 1, description, bug_id, isprivate FROM attachments " .
|
||||
"WHERE attach_id = $id");
|
||||
($self->{'exists'},
|
||||
$self->{'summary'},
|
||||
$self->{'bug_id'},
|
||||
$self->{'isprivate'}) = &::FetchSQLData();
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub query
|
||||
{
|
||||
# Retrieves and returns an array of attachment records for a given bug.
|
||||
@@ -66,19 +43,13 @@ sub query
|
||||
# "attachments" variable.
|
||||
my ($bugid) = @_;
|
||||
|
||||
my $in_editbugs = &::UserInGroup("editbugs");
|
||||
&::SendSQL("SELECT product_id
|
||||
FROM bugs
|
||||
WHERE bug_id = $bugid");
|
||||
my $productid = &::FetchOneColumn();
|
||||
my $caneditproduct = &::CanEditProductId($productid);
|
||||
my $in_editbugs = &::UserInGroup($::userid, "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.
|
||||
&::SendSQL("
|
||||
SELECT attach_id, DATE_FORMAT(creation_ts, '%Y.%m.%d %H:%i'),
|
||||
mimetype, description, ispatch, isobsolete, isprivate,
|
||||
submitter_id, LENGTH(thedata)
|
||||
SELECT attach_id, creation_ts, mimetype, description, ispatch,
|
||||
isobsolete, submitter_id
|
||||
FROM attachments WHERE bug_id = $bugid ORDER BY attach_id
|
||||
");
|
||||
my @attachments = ();
|
||||
@@ -86,19 +57,37 @@ sub query
|
||||
my %a;
|
||||
my $submitter_id;
|
||||
($a{'attachid'}, $a{'date'}, $a{'contenttype'}, $a{'description'},
|
||||
$a{'ispatch'}, $a{'isobsolete'}, $a{'isprivate'}, $submitter_id,
|
||||
$a{'datasize'}) = &::FetchSQLData();
|
||||
$a{'ispatch'}, $a{'isobsolete'}, $submitter_id) = &::FetchSQLData();
|
||||
|
||||
# Format the attachment's creation/modification date into a standard
|
||||
# format (YYYY-MM-DD HH:MM)
|
||||
if ($a{'date'} =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
|
||||
$a{'date'} = "$1-$2-$3 $4:$5";
|
||||
}
|
||||
|
||||
# Retrieve a list of status flags that have been set on the attachment.
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("
|
||||
SELECT name
|
||||
FROM attachstatuses, attachstatusdefs
|
||||
WHERE attach_id = $a{'attachid'}
|
||||
AND attachstatuses.statusid = attachstatusdefs.id
|
||||
ORDER BY sortkey
|
||||
");
|
||||
my @statuses = ();
|
||||
while (&::MoreSQLData()) {
|
||||
my ($status) = &::FetchSQLData();
|
||||
push @statuses , $status;
|
||||
}
|
||||
$a{'statuses'} = \@statuses;
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
# Retrieve a list of flags for this attachment.
|
||||
$a{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a{'attachid'},
|
||||
'is_active' => 1 });
|
||||
|
||||
# We will display the edit link if the user can edit the attachment;
|
||||
# ie the are the submitter, or they have canedit.
|
||||
# Also show the link if the user is not logged in - in that cae,
|
||||
# They'll be prompted later
|
||||
$a{'canedit'} = ($::userid == 0 || (($submitter_id == $::userid ||
|
||||
$in_editbugs) && $caneditproduct));
|
||||
$a{'canedit'} = ($::userid == 0 || $submitter_id == $::userid ||
|
||||
$in_editbugs);
|
||||
push @attachments, \%a;
|
||||
}
|
||||
|
||||
525
mozilla/webtools/bugzilla/Bug.pm
Executable file
525
mozilla/webtools/bugzilla/Bug.pm
Executable file
@@ -0,0 +1,525 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dawn Endico <endico@mozilla.org>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
# Chris Yeh <cyeh@bluemartini.com>
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
use DBI;
|
||||
use RelationSet;
|
||||
use vars qw($unconfirmedstate $legal_keywords);
|
||||
require "globals.pl";
|
||||
require "CGI.pl";
|
||||
package Bug;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
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) ){
|
||||
$ok_field{$key}++;
|
||||
}
|
||||
|
||||
# create a new empty bug
|
||||
#
|
||||
sub new {
|
||||
my $type = shift();
|
||||
my %bug;
|
||||
|
||||
# create a ref to an empty hash and bless it
|
||||
#
|
||||
my $self = {%bug};
|
||||
bless $self, $type;
|
||||
|
||||
# construct from a hash containing a bug's info
|
||||
#
|
||||
if ($#_ == 1) {
|
||||
$self->initBug(@_);
|
||||
} else {
|
||||
confess("invalid number of arguments \($#_\)($_)");
|
||||
}
|
||||
|
||||
# bless as a Bug
|
||||
#
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
# dump info about bug into hash unless user doesn't have permission
|
||||
# user_id 0 is used when person is not logged in.
|
||||
#
|
||||
sub initBug {
|
||||
my $self = shift();
|
||||
my ($bug_id, $user_id) = (@_);
|
||||
|
||||
my $old_bug_id = $bug_id;
|
||||
if ((! defined $bug_id) || (!$bug_id) || (!&::detaint_natural($bug_id))) {
|
||||
# no bug number given
|
||||
$self->{'bug_id'} = $old_bug_id;
|
||||
$self->{'error'} = "InvalidBugId";
|
||||
return $self;
|
||||
}
|
||||
|
||||
# default userid 0, or get DBID if you used an email address
|
||||
unless (defined $user_id) {
|
||||
$user_id = 0;
|
||||
}
|
||||
else {
|
||||
if ($user_id =~ /^\@/) {
|
||||
$user_id = &::DBname_to_id($user_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&::ConnectToDatabase();
|
||||
&::GetVersionTable();
|
||||
|
||||
# this verification should already have been done by caller
|
||||
# my $loginok = quietly_check_login();
|
||||
|
||||
|
||||
$self->{'whoid'} = $user_id;
|
||||
|
||||
# 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;
|
||||
return $self;
|
||||
}
|
||||
|
||||
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)
|
||||
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,
|
||||
delta_ts, sum(votes.count)
|
||||
from bugs left join votes using(bug_id)
|
||||
where bugs.bug_id = $bug_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, 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++;
|
||||
}
|
||||
|
||||
$self->{'assigned_to'} = &::DBID_to_name($self->{'assigned_to'});
|
||||
$self->{'reporter'} = &::DBID_to_name($self->{'reporter'});
|
||||
|
||||
my $ccSet = new RelationSet;
|
||||
$ccSet->mergeFromDB("select who from cc where bug_id=$bug_id");
|
||||
my @cc = $ccSet->toArrayOfStrings();
|
||||
if (@cc) {
|
||||
$self->{'cc'} = \@cc;
|
||||
}
|
||||
|
||||
if (&::Param("useqacontact") && (defined $self->{'qa_contact'}) ) {
|
||||
my $name = $self->{'qa_contact'} > 0 ? &::DBID_to_name($self->{'qa_contact'}) :"";
|
||||
if ($name) {
|
||||
$self->{'qa_contact'} = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (@::legal_keywords) {
|
||||
&::SendSQL("SELECT keyworddefs.name
|
||||
FROM keyworddefs, keywords
|
||||
WHERE keywords.bug_id = $bug_id
|
||||
AND keyworddefs.id = keywords.keywordid
|
||||
ORDER BY keyworddefs.name");
|
||||
my @list;
|
||||
while (&::MoreSQLData()) {
|
||||
push(@list, &::FetchOneColumn());
|
||||
}
|
||||
if (@list) {
|
||||
$self->{'keywords'} = join(', ', @list);
|
||||
}
|
||||
}
|
||||
|
||||
&::SendSQL("select attach_id, creation_ts, description
|
||||
from attachments
|
||||
where bug_id = $bug_id");
|
||||
my @attachments;
|
||||
while (&::MoreSQLData()) {
|
||||
my ($attachid, $date, $desc) = (&::FetchSQLData());
|
||||
if ($date =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
|
||||
$date = "$3/$4/$2 $5:$6";
|
||||
my %attach;
|
||||
$attach{'attachid'} = $attachid;
|
||||
$attach{'date'} = $date;
|
||||
$attach{'desc'} = $desc;
|
||||
push @attachments, \%attach;
|
||||
}
|
||||
}
|
||||
if (@attachments) {
|
||||
$self->{'attachments'} = \@attachments;
|
||||
}
|
||||
|
||||
&::SendSQL("select bug_id, who, bug_when, thetext
|
||||
from longdescs
|
||||
where bug_id = $bug_id");
|
||||
my @longdescs;
|
||||
while (&::MoreSQLData()) {
|
||||
my ($bug_id, $who, $bug_when, $thetext) = (&::FetchSQLData());
|
||||
my %longdesc;
|
||||
$longdesc{'who'} = $who;
|
||||
$longdesc{'bug_when'} = $bug_when;
|
||||
$longdesc{'thetext'} = $thetext;
|
||||
push @longdescs, \%longdesc;
|
||||
}
|
||||
if (@longdescs) {
|
||||
$self->{'longdescs'} = \@longdescs;
|
||||
}
|
||||
|
||||
if (&::Param("usedependencies")) {
|
||||
my @depends = EmitDependList("blocked", "dependson", $bug_id);
|
||||
if ( @depends ) {
|
||||
$self->{'dependson'} = \@depends;
|
||||
}
|
||||
my @blocks = EmitDependList("dependson", "blocked", $bug_id);
|
||||
if ( @blocks ) {
|
||||
$self->{'blocks'} = \@blocks;
|
||||
}
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
# given a bug hash, emit xml for it. with file header provided by caller
|
||||
#
|
||||
sub emitXML {
|
||||
( $#_ == 0 ) || confess("invalid number of arguments");
|
||||
my $self = shift();
|
||||
my $xml;
|
||||
|
||||
|
||||
if (exists $self->{'error'}) {
|
||||
$xml .= "<bug error=\"$self->{'error'}\">\n";
|
||||
$xml .= " <bug_id>$self->{'bug_id'}</bug_id>\n";
|
||||
$xml .= "</bug>\n";
|
||||
return $xml;
|
||||
}
|
||||
|
||||
$xml .= "<bug>\n";
|
||||
|
||||
foreach my $field ("bug_id", "bug_status", "product",
|
||||
"priority", "version", "rep_platform", "assigned_to", "delta_ts",
|
||||
"component", "reporter", "target_milestone", "bug_severity",
|
||||
"creation_ts", "qa_contact", "op_sys", "resolution", "bug_file_loc",
|
||||
"short_desc", "keywords", "status_whiteboard") {
|
||||
if ($self->{$field}) {
|
||||
$xml .= " <$field>" . QuoteXMLChars($self->{$field}) . "</$field>\n";
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $field ("dependson", "blocks", "cc") {
|
||||
if (defined $self->{$field}) {
|
||||
for (my $i=0 ; $i < @{$self->{$field}} ; $i++) {
|
||||
$xml .= " <$field>" . $self->{$field}[$i] . "</$field>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $self->{'longdescs'}) {
|
||||
for (my $i=0 ; $i < @{$self->{'longdescs'}} ; $i++) {
|
||||
$xml .= " <long_desc>\n";
|
||||
$xml .= " <who>" . &::DBID_to_name($self->{'longdescs'}[$i]->{'who'})
|
||||
. "</who>\n";
|
||||
$xml .= " <bug_when>" . $self->{'longdescs'}[$i]->{'bug_when'}
|
||||
. "</bug_when>\n";
|
||||
$xml .= " <thetext>" . QuoteXMLChars($self->{'longdescs'}[$i]->{'thetext'})
|
||||
. "</thetext>\n";
|
||||
$xml .= " </long_desc>\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $self->{'attachments'}) {
|
||||
for (my $i=0 ; $i < @{$self->{'attachments'}} ; $i++) {
|
||||
$xml .= " <attachment>\n";
|
||||
$xml .= " <attachid>" . $self->{'attachments'}[$i]->{'attachid'}
|
||||
. "</attachid>\n";
|
||||
$xml .= " <date>" . $self->{'attachments'}[$i]->{'date'} . "</date>\n";
|
||||
$xml .= " <desc>" . QuoteXMLChars($self->{'attachments'}[$i]->{'desc'}) . "</desc>\n";
|
||||
# $xml .= " <type>" . $self->{'attachments'}[$i]->{'type'} . "</type>\n";
|
||||
# $xml .= " <data>" . $self->{'attachments'}[$i]->{'data'} . "</data>\n";
|
||||
$xml .= " </attachment>\n";
|
||||
}
|
||||
}
|
||||
|
||||
$xml .= "</bug>\n";
|
||||
return $xml;
|
||||
}
|
||||
|
||||
sub EmitDependList {
|
||||
my ($myfield, $targetfield, $bug_id) = (@_);
|
||||
my @list;
|
||||
&::SendSQL("select dependencies.$targetfield, bugs.bug_status
|
||||
from dependencies, bugs
|
||||
where dependencies.$myfield = $bug_id
|
||||
and bugs.bug_id = dependencies.$targetfield
|
||||
order by dependencies.$targetfield");
|
||||
while (&::MoreSQLData()) {
|
||||
my ($i, $stat) = (&::FetchSQLData());
|
||||
push @list, $i;
|
||||
}
|
||||
return @list;
|
||||
}
|
||||
|
||||
sub QuoteXMLChars {
|
||||
$_[0] =~ s/&/&/g;
|
||||
$_[0] =~ s/</</g;
|
||||
$_[0] =~ s/>/>/g;
|
||||
$_[0] =~ s/'/'/g;
|
||||
$_[0] =~ s/"/"/g;
|
||||
# $_[0] =~ s/([\x80-\xFF])/&XmlUtf8Encode(ord($1))/ge;
|
||||
return($_[0]);
|
||||
}
|
||||
|
||||
sub XML_Header {
|
||||
my ($urlbase, $version, $maintainer, $exporter) = (@_);
|
||||
|
||||
my $xml;
|
||||
$xml = "<?xml version=\"1.0\" standalone=\"yes\"?>\n";
|
||||
$xml .= "<!DOCTYPE bugzilla SYSTEM \"$urlbase";
|
||||
if (! ($urlbase =~ /.+\/$/)) {
|
||||
$xml .= "/";
|
||||
}
|
||||
$xml .= "bugzilla.dtd\">\n";
|
||||
$xml .= "<bugzilla";
|
||||
if (defined $exporter) {
|
||||
$xml .= " exporter=\"$exporter\"";
|
||||
}
|
||||
$xml .= " version=\"$version\"";
|
||||
$xml .= " urlbase=\"$urlbase\"";
|
||||
$xml .= " maintainer=\"$maintainer\">\n";
|
||||
return ($xml);
|
||||
}
|
||||
|
||||
|
||||
sub XML_Footer {
|
||||
return ("</bugzilla>\n");
|
||||
}
|
||||
|
||||
sub UserInGroup {
|
||||
my $self = shift();
|
||||
my ($groupname) = (@_);
|
||||
return &::UserInGroup($self->{'whoid'}, $groupname);
|
||||
}
|
||||
|
||||
sub CanChangeField {
|
||||
my $self = shift();
|
||||
my ($f, $oldvalue, $newvalue) = (@_);
|
||||
my $UserInEditGroup = -1;
|
||||
my $UserInCanConfirmGroup = -1;
|
||||
my $ownerid;
|
||||
my $reporterid;
|
||||
my $qacontactid;
|
||||
|
||||
if ($f eq "assigned_to" || $f eq "reporter" || $f eq "qa_contact") {
|
||||
if ($oldvalue =~ /^\d+$/) {
|
||||
if ($oldvalue == 0) {
|
||||
$oldvalue = "";
|
||||
} else {
|
||||
$oldvalue = &::DBID_to_name($oldvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($oldvalue eq $newvalue) {
|
||||
return 1;
|
||||
}
|
||||
if (&::trim($oldvalue) eq &::trim($newvalue)) {
|
||||
return 1;
|
||||
}
|
||||
if ($f =~ /^longdesc/) {
|
||||
return 1;
|
||||
}
|
||||
if ($UserInEditGroup < 0) {
|
||||
$UserInEditGroup = UserInGroup($self, "editbugs");
|
||||
}
|
||||
if ($UserInEditGroup) {
|
||||
return 1;
|
||||
}
|
||||
&::SendSQL("SELECT reporter, assigned_to, qa_contact FROM bugs " .
|
||||
"WHERE bug_id = $self->{'bug_id'}");
|
||||
($reporterid, $ownerid, $qacontactid) = (&::FetchSQLData());
|
||||
|
||||
# Let reporter change bug status, even if they can't edit bugs.
|
||||
# If reporter can't re-open their bug they will just file a duplicate.
|
||||
# While we're at it, let them close their own bugs as well.
|
||||
if ( ($f eq "bug_status") && ($self->{'whoid'} eq $reporterid) ) {
|
||||
return 1;
|
||||
}
|
||||
if ($f eq "bug_status" && $newvalue ne $::unconfirmedstate &&
|
||||
&::IsOpenedState($newvalue)) {
|
||||
|
||||
# Hmm. They are trying to set this bug to some opened state
|
||||
# that isn't the UNCONFIRMED state. Are they in the right
|
||||
# group? Or, has it ever been confirmed? If not, then this
|
||||
# isn't legal.
|
||||
|
||||
if ($UserInCanConfirmGroup < 0) {
|
||||
$UserInCanConfirmGroup = &::UserInGroup($self->{'whoid'},"canconfirm");
|
||||
}
|
||||
if ($UserInCanConfirmGroup) {
|
||||
return 1;
|
||||
}
|
||||
&::SendSQL("SELECT everconfirmed FROM bugs WHERE bug_id = $self->{'bug_id'}");
|
||||
my $everconfirmed = FetchOneColumn();
|
||||
if ($everconfirmed) {
|
||||
return 1;
|
||||
}
|
||||
} elsif ($reporterid eq $self->{'whoid'} || $ownerid eq $self->{'whoid'} ||
|
||||
$qacontactid eq $self->{'whoid'}) {
|
||||
return 1;
|
||||
}
|
||||
$self->{'error'} = "
|
||||
Only the owner or submitter of the bug, or a sufficiently
|
||||
empowered user, may make that change to the $f field."
|
||||
}
|
||||
|
||||
sub Collision {
|
||||
my $self = shift();
|
||||
my $write = "WRITE"; # Might want to make a param to control
|
||||
# whether we do LOW_PRIORITY ...
|
||||
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();
|
||||
if ($::driver eq 'mysql') {
|
||||
&::SendSQL("unlock tables");
|
||||
}
|
||||
if ($self->{'delta_ts'} ne $delta_ts) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub AppendComment {
|
||||
my $self = shift();
|
||||
my ($comment) = (@_);
|
||||
$comment =~ s/\r\n/\n/g; # Get rid of windows-style line endings.
|
||||
$comment =~ s/\r/\n/g; # Get rid of mac-style line endings.
|
||||
if ($comment =~ /^\s*$/) { # Nothin' but whitespace.
|
||||
return;
|
||||
}
|
||||
|
||||
&::SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext) " .
|
||||
"VALUES($self->{'bug_id'}, $self->{'whoid'}, now(), " . &::SqlQuote($comment) . ")");
|
||||
|
||||
&::SendSQL("UPDATE bugs SET delta_ts = now() WHERE bug_id = $self->{'bug_id'}");
|
||||
}
|
||||
|
||||
|
||||
#from o'reilley's Programming Perl
|
||||
sub display {
|
||||
my $self = shift;
|
||||
my @keys;
|
||||
if (@_ == 0) { # no further arguments
|
||||
@keys = sort keys(%$self);
|
||||
} else {
|
||||
@keys = @_; # use the ones given
|
||||
}
|
||||
foreach my $key (@keys) {
|
||||
print "\t$key => $self->{$key}\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub CommitChanges {
|
||||
|
||||
#snapshot bug
|
||||
#snapshot dependencies
|
||||
#check can change fields
|
||||
#check collision
|
||||
#lock and change fields
|
||||
#notify through mail
|
||||
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
use vars qw($AUTOLOAD);
|
||||
my $self = shift;
|
||||
my $type = ref($self) || $self;
|
||||
my $attr = $AUTOLOAD;
|
||||
|
||||
$attr =~ s/.*:://;
|
||||
return unless $attr=~ /[^A-Z]/;
|
||||
if (@_) {
|
||||
$self->{$attr} = shift;
|
||||
return;
|
||||
}
|
||||
confess ("invalid bug attribute $attr") unless $ok_field{$attr};
|
||||
if (defined $self->{$attr}) {
|
||||
return $self->{$attr};
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,303 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
#
|
||||
|
||||
package Bugzilla;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Auth;
|
||||
use Bugzilla::Auth::Login::WWW;
|
||||
use Bugzilla::CGI;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::DB;
|
||||
use Bugzilla::Template;
|
||||
use Bugzilla::User;
|
||||
|
||||
my $_template;
|
||||
sub template {
|
||||
my $class = shift;
|
||||
$_template ||= Bugzilla::Template->create();
|
||||
return $_template;
|
||||
}
|
||||
|
||||
my $_cgi;
|
||||
sub cgi {
|
||||
my $class = shift;
|
||||
$_cgi ||= new Bugzilla::CGI();
|
||||
return $_cgi;
|
||||
}
|
||||
|
||||
my $_user;
|
||||
sub user {
|
||||
my $class = shift;
|
||||
|
||||
if (not defined $_user) {
|
||||
$_user = new Bugzilla::User;
|
||||
}
|
||||
|
||||
return $_user;
|
||||
}
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
$_user = Bugzilla::Auth::Login::WWW->login($type);
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ($class, $option) = @_;
|
||||
|
||||
# If we're not logged in, go away
|
||||
return unless user->id;
|
||||
|
||||
$option = LOGOUT_CURRENT unless defined $option;
|
||||
Bugzilla::Auth::Login::WWW->logout($_user, $option);
|
||||
}
|
||||
|
||||
sub logout_user {
|
||||
my ($class, $user) = @_;
|
||||
# When we're logging out another user we leave cookies alone, and
|
||||
# therefore avoid calling Bugzilla->logout() directly.
|
||||
Bugzilla::Auth::Login::WWW->logout($user, LOGOUT_ALL);
|
||||
}
|
||||
|
||||
# just a compatibility front-end to logout_user that gets a user by id
|
||||
sub logout_user_by_id {
|
||||
my ($class, $id) = @_;
|
||||
my $user = new Bugzilla::User($id);
|
||||
$class->logout_user($user);
|
||||
}
|
||||
|
||||
# hack that invalidates credentials for a single request
|
||||
sub logout_request {
|
||||
undef $_user;
|
||||
# XXX clean this up eventually
|
||||
$::userid = 0;
|
||||
# We can't delete from $cgi->cookie, so logincookie data will remain
|
||||
# there. Don't rely on it: use Bugzilla->user->login instead!
|
||||
}
|
||||
|
||||
my $_dbh;
|
||||
my $_dbh_main;
|
||||
my $_dbh_shadow;
|
||||
sub dbh {
|
||||
my $class = shift;
|
||||
|
||||
# If we're not connected, then we must want the main db
|
||||
if (!$_dbh) {
|
||||
$_dbh = $_dbh_main = Bugzilla::DB::connect_main();
|
||||
}
|
||||
|
||||
return $_dbh;
|
||||
}
|
||||
|
||||
sub dbwritesallowed {
|
||||
my $class = shift;
|
||||
|
||||
# We can write if we are connected to the main database.
|
||||
# Note that if we don't have a shadowdb, then we claim that its ok
|
||||
# to write even if we're nominally connected to the shadowdb.
|
||||
# This is OK because this method is only used to test if misc
|
||||
# updates can be done, rather than anything complicated.
|
||||
return $class->dbh == $_dbh_main;
|
||||
}
|
||||
|
||||
sub switch_to_shadow_db {
|
||||
my $class = shift;
|
||||
|
||||
if (!$_dbh_shadow) {
|
||||
if (Param('shadowdb')) {
|
||||
$_dbh_shadow = Bugzilla::DB::connect_shadow();
|
||||
} else {
|
||||
$_dbh_shadow = $_dbh_main;
|
||||
}
|
||||
}
|
||||
|
||||
$_dbh = $_dbh_shadow;
|
||||
}
|
||||
|
||||
sub switch_to_main_db {
|
||||
my $class = shift;
|
||||
|
||||
$_dbh = $_dbh_main;
|
||||
}
|
||||
|
||||
# Private methods
|
||||
|
||||
# Per process cleanup
|
||||
sub _cleanup {
|
||||
undef $_cgi;
|
||||
undef $_user;
|
||||
|
||||
# See bug 192531. If we don't clear the possibly active statement handles,
|
||||
# then when this is called from the END block, it happens _before_ the
|
||||
# destructors in Bugzilla::DB have happened.
|
||||
# See http://rt.perl.org/rt2/Ticket/Display.html?id=17450#38810
|
||||
# Without disconnecting explicitly here, noone notices, because DBI::END
|
||||
# ends up calling DBD::mysql's $drh->disconnect_all, which is a noop.
|
||||
# This code is evil, but it needs to be done, at least until SendSQL and
|
||||
# friends can be removed
|
||||
@Bugzilla::DB::SQLStateStack = ();
|
||||
undef $Bugzilla::DB::_current_sth;
|
||||
|
||||
# When we support transactions, need to ->rollback here
|
||||
$_dbh_main->disconnect if $_dbh_main;
|
||||
$_dbh_shadow->disconnect if $_dbh_shadow and Param("shadowdb");
|
||||
undef $_dbh_main;
|
||||
undef $_dbh_shadow;
|
||||
undef $_dbh;
|
||||
}
|
||||
|
||||
sub END {
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla - Semi-persistent collection of various objects used by scripts
|
||||
and modules
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
sub someModulesSub {
|
||||
Bugzilla->dbh->prepare(...);
|
||||
Bugzilla->template->process(...);
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Several Bugzilla 'things' are used by a variety of modules and scripts. This
|
||||
includes database handles, template objects, and so on.
|
||||
|
||||
This module is a singleton intended as a central place to store these objects.
|
||||
This approach has several advantages:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
They're not global variables, so we don't have issues with them staying arround
|
||||
with mod_perl
|
||||
|
||||
=item *
|
||||
|
||||
Everything is in one central place, so its easy to access, modify, and maintain
|
||||
|
||||
=item *
|
||||
|
||||
Code in modules can get access to these objects without having to have them
|
||||
all passed from the caller, and the caller's caller, and....
|
||||
|
||||
=item *
|
||||
|
||||
We can reuse objects across requests using mod_perl where appropriate (eg
|
||||
templates), whilst destroying those which are only valid for a single request
|
||||
(such as the current user)
|
||||
|
||||
=back
|
||||
|
||||
Note that items accessible via this object are demand-loaded when requested.
|
||||
|
||||
For something to be added to this object, it should either be able to benefit
|
||||
from persistence when run under mod_perl (such as the a C<template> object),
|
||||
or should be something which is globally required by a large ammount of code
|
||||
(such as the current C<user> object).
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
Note that all C<Bugzilla> functionailty is method based; use C<Bugzilla->dbh>
|
||||
rather than C<Bugzilla::dbh>. Nothing cares about this now, but don't rely on
|
||||
that.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<template>
|
||||
|
||||
The current C<Template> object, to be used for output
|
||||
|
||||
=item C<cgi>
|
||||
|
||||
The current C<cgi> object. Note that modules should B<not> be using this in
|
||||
general. Not all Bugzilla actions are cgi requests. Its useful as a convenience
|
||||
method for those scripts/templates which are only use via CGI, though.
|
||||
|
||||
=item C<user>
|
||||
|
||||
The current C<Bugzilla::User>. C<undef> if there is no currently logged in user
|
||||
or if the login code has not yet been run.
|
||||
|
||||
=item C<login>
|
||||
|
||||
Logs in a user, returning a C<Bugzilla::User> object, or C<undef> if there is
|
||||
no logged in user. See L<Bugzilla::Auth|Bugzilla::Auth>, and
|
||||
L<Bugzilla::User|Bugzilla::User>.
|
||||
|
||||
=item C<logout($option)>
|
||||
|
||||
Logs out the current user, which involves invalidating user sessions and
|
||||
cookies. Three options are available from
|
||||
L<Bugzilla::Constants|Bugzilla::Constants>: LOGOUT_CURRENT (the
|
||||
default), LOGOUT_ALL or LOGOUT_KEEP_CURRENT.
|
||||
|
||||
=item C<logout_user($user)>
|
||||
|
||||
Logs out the specified user (invalidating all his sessions), taking a
|
||||
Bugzilla::User instance.
|
||||
|
||||
=item C<logout_by_id($id)>
|
||||
|
||||
Logs out the user with the id specified. This is a compatibility
|
||||
function to be used in callsites where there is only a userid and no
|
||||
Bugzilla::User instance.
|
||||
|
||||
=item C<logout_request>
|
||||
|
||||
Essentially, causes calls to C<Bugzilla->user> to return C<undef>. This has the
|
||||
effect of logging out a user for the current request only; cookies and
|
||||
database sessions are left intact.
|
||||
|
||||
=item C<dbh>
|
||||
|
||||
The current database handle. See L<DBI>.
|
||||
|
||||
=item C<dbwritesallowed>
|
||||
|
||||
Determines if writes to the database are permitted. This is usually used to
|
||||
determine if some general cleanup needs to occur (such as clearing the token
|
||||
table)
|
||||
|
||||
=item C<switch_to_shadow_db>
|
||||
|
||||
Switch from using the main database to using the shadow database.
|
||||
|
||||
=item C<switch_to_main_db>
|
||||
|
||||
Change the database object to refer to the main database.
|
||||
|
||||
=back
|
||||
@@ -1,324 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
|
||||
# The verification method that was successfully used upon login, if any
|
||||
my $current_verify_class = undef;
|
||||
|
||||
# 'inherit' from the main verify method
|
||||
BEGIN {
|
||||
for my $verifyclass (split /,\s*/, Param("user_verify_class")) {
|
||||
if ($verifyclass =~ /^([A-Za-z0-9_\.\-]+)$/) {
|
||||
$verifyclass = $1;
|
||||
} else {
|
||||
die "Badly-named user_verify_class '$verifyclass'";
|
||||
}
|
||||
require "Bugzilla/Auth/Verify/" . $verifyclass . ".pm";
|
||||
}
|
||||
}
|
||||
|
||||
# PRIVATE
|
||||
|
||||
# A number of features, like password change requests, require the DB
|
||||
# verification method to be on the list.
|
||||
sub has_db {
|
||||
for (split (/[\s,]+/, Param("user_verify_class"))) {
|
||||
if (/^DB$/) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# Returns the network address for a given ip
|
||||
sub get_netaddr {
|
||||
my $ipaddr = shift;
|
||||
|
||||
# Check for a valid IPv4 addr which we know how to parse
|
||||
if (!$ipaddr || $ipaddr !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $addr = unpack("N", pack("CCCC", split(/\./, $ipaddr)));
|
||||
|
||||
my $maskbits = Param('loginnetmask');
|
||||
|
||||
$addr >>= (32-$maskbits);
|
||||
$addr <<= (32-$maskbits);
|
||||
return join(".", unpack("CCCC", pack("N", $addr)));
|
||||
}
|
||||
|
||||
# This is a replacement for the inherited authenticate function
|
||||
# go through each of the available methods for each function
|
||||
sub authenticate {
|
||||
my $class = shift;
|
||||
my @args = @_;
|
||||
my @firstresult = ();
|
||||
my @result = ();
|
||||
for my $method (split /,\s*/, Param("user_verify_class")) {
|
||||
$method = "Bugzilla::Auth::Verify::" . $method;
|
||||
@result = $method->authenticate(@args);
|
||||
@firstresult = @result unless @firstresult;
|
||||
|
||||
if (($result[0] != AUTH_NODATA)&&($result[0] != AUTH_LOGINFAILED)) {
|
||||
$current_verify_class = $method;
|
||||
return @result;
|
||||
}
|
||||
}
|
||||
@result = @firstresult;
|
||||
# no auth match
|
||||
|
||||
# see if we can set $current to the first verify method that
|
||||
# will allow a new login
|
||||
|
||||
for my $method (split /,\s*/, Param("user_verify_class")) {
|
||||
$method = "Bugzilla::Auth::Verify::" . $method;
|
||||
if ($method->can_edit('new')) {
|
||||
$current_verify_class = $method;
|
||||
}
|
||||
}
|
||||
|
||||
return @result;
|
||||
}
|
||||
|
||||
sub can_edit {
|
||||
my ($class, $type) = @_;
|
||||
if ($current_verify_class) {
|
||||
return $current_verify_class->can_edit($type);
|
||||
}
|
||||
# $current_verify_class will not be set if the user isn't logged in. That
|
||||
# happens when the user is trying to create a new account, which (for now)
|
||||
# is hard-coded to work with DB.
|
||||
elsif (has_db) {
|
||||
return Bugzilla::Auth::Verify::DB->can_edit($type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth - Authentication handling for Bugzilla users
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Handles authentication for Bugzilla users.
|
||||
|
||||
Authentication from Bugzilla involves two sets of modules. One set is
|
||||
used to obtain the data (from CGI, email, etc), and the other set uses
|
||||
this data to authenticate against the datasource (the Bugzilla DB, LDAP,
|
||||
cookies, etc).
|
||||
|
||||
Modules for obtaining the data are located under L<Bugzilla::Auth::Login>, and
|
||||
modules for authenticating are located in L<Bugzilla::Auth::Verify>.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
C<Bugzilla::Auth> contains several helper methods to be used by
|
||||
authentication or login modules.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<Bugzilla::Auth::get_netaddr($ipaddr)>
|
||||
|
||||
Given an ip address, this returns the associated network address, using
|
||||
C<Param('loginnetmask')> as the netmask. This can be used to obtain data
|
||||
in order to restrict weak authentication methods (such as cookies) to
|
||||
only some addresses.
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHENTICATION
|
||||
|
||||
Authentication modules check a user's credentials (username, password,
|
||||
etc) to verify who the user is. The methods that C<Bugzilla::Auth> uses for
|
||||
authentication are wrappers that check all configured modules (via the
|
||||
C<Param('user_info_class')> and C<Param('user_verify_class')>) in sequence.
|
||||
|
||||
=head2 METHODS
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<authenticate($username, $pass)>
|
||||
|
||||
This method is passed a username and a password, and returns a list
|
||||
containing up to four return values, depending on the results of the
|
||||
authentication.
|
||||
|
||||
The first return value is one of the status codes defined in
|
||||
L<Bugzilla::Constants|Bugzilla::Constants> and described below. The
|
||||
rest of the return values are status code-specific and are explained in
|
||||
the status code descriptions.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<AUTH_OK>
|
||||
|
||||
Authentication succeeded. The second variable is the userid of the new
|
||||
user.
|
||||
|
||||
=item C<AUTH_NODATA>
|
||||
|
||||
Insufficient login data was provided by the user. This may happen in several
|
||||
cases, such as cookie authentication when the cookie is not present.
|
||||
|
||||
=item C<AUTH_ERROR>
|
||||
|
||||
An error occurred when trying to use the login mechanism. The second return
|
||||
value may contain the Bugzilla userid, but will probably be C<undef>,
|
||||
signifiying that the userid is unknown. The third value is a tag describing
|
||||
the error used by the authentication error templates to print a description
|
||||
to the user. The optional fourth argument is a hashref of values used as part
|
||||
of the tag's error descriptions.
|
||||
|
||||
This error template must have a name/location of
|
||||
I<account/auth/C<lc(authentication-type)>-error.html.tmpl>.
|
||||
|
||||
=item C<AUTH_LOGINFAILED>
|
||||
|
||||
An incorrect username or password was given. Note that for security reasons,
|
||||
both cases return the same error code. However, in the case of a valid
|
||||
username, the second argument may be the userid. The authentication
|
||||
mechanism may not always be able to discover the userid if the password is
|
||||
not known, so whether or not this argument is present is implementation
|
||||
specific. For security reasons, the presence or lack of a userid value should
|
||||
not be communicated to the user.
|
||||
|
||||
The third argument is an optional tag from the authentication server
|
||||
describing the error. The tag can be used by a template to inform the user
|
||||
about the error. Similar to C<AUTH_ERROR>, an optional hashref may be
|
||||
present as a fourth argument, to be used by the tag to give more detailed
|
||||
information.
|
||||
|
||||
=item C<AUTH_DISABLED>
|
||||
|
||||
The user successfully logged in, but their account has been disabled.
|
||||
The second argument in the returned array is the userid, and the third
|
||||
is some text explaining why the account was disabled. This text would
|
||||
typically come from the C<disabledtext> field in the C<profiles> table.
|
||||
Note that this argument is a string, not a tag.
|
||||
|
||||
=back
|
||||
|
||||
=item C<current_verify_class>
|
||||
|
||||
This scalar gets populated with the full name (eg.,
|
||||
C<Bugzilla::Auth::Verify::DB>) of the verification method being used by the
|
||||
current user. If no user is logged in, it will contain the name of the first
|
||||
method that allows new users, if any. Otherwise, it carries an undefined
|
||||
value.
|
||||
|
||||
=item C<can_edit>
|
||||
|
||||
This determines if the user's account details can be modified. It returns a
|
||||
reference to a hash with the keys C<userid>, C<login_name>, and C<realname>,
|
||||
which determine whether their respective profile values may be altered, and
|
||||
C<new>, which determines if new accounts may be created.
|
||||
|
||||
Each user verification method (chosen with C<Param('user_verify_class')> has
|
||||
its own set of can_edit values. Calls to can_edit return the appropriate
|
||||
values for the current user's login method.
|
||||
|
||||
If a user is not logged in, C<can_edit> will contain the values of the first
|
||||
verify method that allows new users to be created, if available. Otherwise it
|
||||
returns an empty hash.
|
||||
|
||||
=back
|
||||
|
||||
=head1 LOGINS
|
||||
|
||||
A login module can be used to try to log in a Bugzilla user in a
|
||||
particular way. For example,
|
||||
L<Bugzilla::Auth::Login::WWW::CGI|Bugzilla::Auth::Login::WWW::CGI>
|
||||
logs in users from CGI scripts, first by using form variables, and then
|
||||
by trying cookies as a fallback.
|
||||
|
||||
The login interface consists of the following methods:
|
||||
|
||||
=item C<login>, which takes a C<$type> argument, using constants found in
|
||||
C<Bugzilla::Constants>.
|
||||
|
||||
The login method may use various authentication modules (described
|
||||
above) to try to authenticate a user, and should return the userid on
|
||||
success, or C<undef> on failure.
|
||||
|
||||
When a login is required, but data is not present, it is the job of the
|
||||
login method to prompt the user for this data.
|
||||
|
||||
The constants accepted by C<login> include the following:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<LOGIN_OPTIONAL>
|
||||
|
||||
A login is never required to access this data. Attempting to login is
|
||||
still useful, because this allows the page to be personalised. Note that
|
||||
an incorrect login will still trigger an error, even though the lack of
|
||||
a login will be OK.
|
||||
|
||||
=item C<LOGIN_NORMAL>
|
||||
|
||||
A login may or may not be required, depending on the setting of the
|
||||
I<requirelogin> parameter.
|
||||
|
||||
=item C<LOGIN_REQUIRED>
|
||||
|
||||
A login is always required to access this data.
|
||||
|
||||
=back
|
||||
|
||||
=item C<logout>, which takes a C<Bugzilla::User> argument for the user
|
||||
being logged out, and an C<$option> argument. Possible values for
|
||||
C<$option> include:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<LOGOUT_CURRENT>
|
||||
|
||||
Log out the user and invalidate his currently registered session.
|
||||
|
||||
=item C<LOGOUT_ALL>
|
||||
|
||||
Log out the user, and invalidate all sessions the user has registered in
|
||||
Bugzilla.
|
||||
|
||||
=item C<LOGOUT_KEEP_CURRENT>
|
||||
|
||||
Invalidate all sessions the user has registered excluding his current
|
||||
session; this option should leave the user logged in. This is useful for
|
||||
user-performed password changes.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth::Login::WWW::CGI>, L<Bugzilla::Auth::Login::WWW::CGI::Cookie>, L<Bugzilla::Auth::Verify::DB>
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Login::WWW;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Config;
|
||||
|
||||
# $current_login_class stores the name of the login style that succeeded.
|
||||
my $current_login_class = undef;
|
||||
sub login_class {
|
||||
my ($class, $type) = @_;
|
||||
if ($type) {
|
||||
$current_login_class = $type;
|
||||
}
|
||||
return $current_login_class;
|
||||
}
|
||||
|
||||
# can_logout determines if a user may log out
|
||||
sub can_logout {
|
||||
return 1 if (login_class && login_class->can_logout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
|
||||
my $user = Bugzilla->user;
|
||||
|
||||
# Avoid double-logins, which may confuse the auth code
|
||||
# (double cookies, odd compat code settings, etc)
|
||||
return $user if $user->id;
|
||||
|
||||
$type = LOGIN_NORMAL unless defined $type;
|
||||
|
||||
# Log in using whatever methods are defined in user_info_class.
|
||||
# Please note the particularly strange way require() and the function
|
||||
# calls are being done, because we're calling a module that's named in
|
||||
# a string. I assure you it works, and it avoids the need for an eval().
|
||||
my $userid;
|
||||
for my $login_class (split(/,\s*/, Param('user_info_class'))) {
|
||||
require "Bugzilla/Auth/Login/WWW/" . $login_class . ".pm";
|
||||
$userid = "Bugzilla::Auth::Login::WWW::$login_class"->login($type);
|
||||
if ($userid) {
|
||||
$class->login_class("Bugzilla::Auth::Login::WWW::$login_class");
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if ($userid) {
|
||||
$user = new Bugzilla::User($userid);
|
||||
|
||||
$user->set_flags('can_logout' => $class->can_logout);
|
||||
|
||||
# Compat stuff
|
||||
$::userid = $userid;
|
||||
} else {
|
||||
Bugzilla->logout_request();
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ($class, $user, $option) = @_;
|
||||
if (can_logout) {
|
||||
$class->login_class->logout($user, $option);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login::WWW - WWW login information gathering module
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=item C<login>
|
||||
|
||||
Passes C<login> calls to each class defined in the param C<user_info_class>
|
||||
and returns a C<Bugzilla::User> object from the first one that successfully
|
||||
gathers user login information.
|
||||
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Login::WWW::CGI;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
|
||||
# 'NORMAL' logins depend on the 'requirelogin' param
|
||||
if ($type == LOGIN_NORMAL) {
|
||||
$type = Param('requirelogin') ? LOGIN_REQUIRED : LOGIN_OPTIONAL;
|
||||
}
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
# First, try the actual login method against form variables
|
||||
my $username = $cgi->param("Bugzilla_login");
|
||||
my $passwd = $cgi->param("Bugzilla_password");
|
||||
|
||||
my $authmethod = Param("user_verify_class");
|
||||
my ($authres, $userid, $extra, $info) =
|
||||
Bugzilla::Auth->authenticate($username, $passwd);
|
||||
|
||||
if ($authres == AUTH_OK) {
|
||||
# Login via username/password was correct and valid, so create
|
||||
# and send out the login cookies
|
||||
my $ipaddr = $cgi->remote_addr;
|
||||
unless ($cgi->param('Bugzilla_restrictlogin') ||
|
||||
Param('loginnetmask') == 32) {
|
||||
$ipaddr = Bugzilla::Auth::get_netaddr($ipaddr);
|
||||
}
|
||||
|
||||
# The IP address is valid, at least for comparing with itself in a
|
||||
# subsequent login
|
||||
trick_taint($ipaddr);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("INSERT INTO logincookies (userid, ipaddr, lastused)
|
||||
VALUES (?, ?, NOW())",
|
||||
undef,
|
||||
$userid, $ipaddr);
|
||||
my $logincookie = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
|
||||
|
||||
# Remember cookie only if admin has told so
|
||||
# or admin didn't forbid it and user told to remember.
|
||||
if ((Param('rememberlogin') eq 'on') ||
|
||||
((Param('rememberlogin') ne 'off') &&
|
||||
($cgi->param('Bugzilla_remember') eq 'on'))) {
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $userid,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $logincookie,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
|
||||
}
|
||||
else {
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $userid);
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $logincookie);
|
||||
|
||||
}
|
||||
}
|
||||
elsif ($authres == AUTH_NODATA) {
|
||||
# No data from the form, so try to login via cookies
|
||||
$username = $cgi->cookie("Bugzilla_login");
|
||||
$passwd = $cgi->cookie("Bugzilla_logincookie");
|
||||
|
||||
require Bugzilla::Auth::Login::WWW::CGI::Cookie;
|
||||
my $authmethod = "Cookie";
|
||||
|
||||
($authres, $userid, $extra) =
|
||||
Bugzilla::Auth::Login::WWW::CGI::Cookie->authenticate($username, $passwd);
|
||||
|
||||
# If the data for the cookie was incorrect, then treat that as
|
||||
# NODATA. This could occur if the user's IP changed, for example.
|
||||
# Give them un-loggedin access if allowed (checked below)
|
||||
$authres = AUTH_NODATA if $authres == AUTH_LOGINFAILED;
|
||||
}
|
||||
|
||||
# Now check the result
|
||||
|
||||
# An error may have occurred with the login mechanism
|
||||
if ($authres == AUTH_ERROR) {
|
||||
ThrowCodeError("auth_err",
|
||||
{ authmethod => lc($authmethod),
|
||||
userid => $userid,
|
||||
auth_err_tag => $extra,
|
||||
info => $info
|
||||
});
|
||||
}
|
||||
|
||||
# We can load the page if the login was ok, or there was no data
|
||||
# but a login wasn't required
|
||||
if ($authres == AUTH_OK ||
|
||||
($authres == AUTH_NODATA && $type == LOGIN_OPTIONAL)) {
|
||||
|
||||
# login succeded, so we're done
|
||||
return $userid;
|
||||
}
|
||||
|
||||
# No login details were given, but we require a login if the
|
||||
# page does
|
||||
if ($authres == AUTH_NODATA && $type == LOGIN_REQUIRED) {
|
||||
# Throw up the login page
|
||||
|
||||
print Bugzilla->cgi->header();
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
$template->process("account/auth/login.html.tmpl",
|
||||
{ 'target' => $cgi->url(-relative=>1),
|
||||
'form' => \%::FORM,
|
||||
'mform' => \%::MFORM,
|
||||
'caneditaccount' => Bugzilla::Auth->can_edit('new'),
|
||||
'has_db' => Bugzilla::Auth->has_db,
|
||||
}
|
||||
)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
# This seems like as good as time as any to get rid of old
|
||||
# crufty junk in the logincookies table. Get rid of any entry
|
||||
# that hasn't been used in a month.
|
||||
Bugzilla->dbh->do("DELETE FROM logincookies " .
|
||||
"WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
# The username/password may be wrong
|
||||
# Don't let the user know whether the username exists or whether
|
||||
# the password was just wrong. (This makes it harder for a cracker
|
||||
# to find account names by brute force)
|
||||
if ($authres == AUTH_LOGINFAILED) {
|
||||
ThrowUserError("invalid_username_or_password");
|
||||
}
|
||||
|
||||
# The account may be disabled
|
||||
if ($authres == AUTH_DISABLED) {
|
||||
# Clear the cookie
|
||||
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
|
||||
# and throw a user error
|
||||
ThrowUserError("account_disabled",
|
||||
{'disabled_reason' => $extra});
|
||||
}
|
||||
|
||||
# If we get here, then we've run out of options, which shouldn't happen
|
||||
ThrowCodeError("authres_unhandled", { authres => $authres,
|
||||
type => $type, });
|
||||
}
|
||||
|
||||
# This auth style allows the user to log out.
|
||||
sub can_logout { return 1; }
|
||||
|
||||
# Logs user out, according to the option provided; this consists of
|
||||
# removing entries from logincookies for the specified $user.
|
||||
sub logout {
|
||||
my ($class, $user, $option) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$option = LOGOUT_ALL unless defined $option;
|
||||
|
||||
if ($option == LOGOUT_ALL) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE userid = ?",
|
||||
undef, $user->id);
|
||||
return;
|
||||
}
|
||||
|
||||
# The LOGOUT_*_CURRENT options require a cookie
|
||||
my $cookie = Bugzilla->cgi->cookie("Bugzilla_logincookie");
|
||||
detaint_natural($cookie);
|
||||
|
||||
# These queries use both the cookie ID and the user ID as keys. Even
|
||||
# though we know the userid must match, we still check it in the SQL
|
||||
# as a sanity check, since there is no locking here, and if the user
|
||||
# logged out from two machines simultaneously, while someone else
|
||||
# logged in and got the same cookie, we could be logging the other
|
||||
# user out here. Yes, this is very very very unlikely, but why take
|
||||
# chances? - bbaetz
|
||||
if ($option == LOGOUT_KEEP_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie != ? AND userid = ?",
|
||||
undef, $cookie, $user->id);
|
||||
} elsif ($option == LOGOUT_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
|
||||
undef, $cookie, $user->id);
|
||||
} else {
|
||||
die("Invalid option $option supplied to logout()");
|
||||
}
|
||||
|
||||
if ($option != LOGOUT_KEEP_CURRENT) {
|
||||
clear_browser_cookies();
|
||||
Bugzilla->logout_request();
|
||||
}
|
||||
}
|
||||
|
||||
sub clear_browser_cookies {
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$cgi->send_cookie(-name => "Bugzilla_login",
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
$cgi->send_cookie(-name => "Bugzilla_logincookie",
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login::WWW::CGI - CGI-based logins for Bugzilla
|
||||
|
||||
=head1 SUMMARY
|
||||
|
||||
This is a L<login module|Bugzilla::Auth/"LOGIN"> for Bugzilla. Users connecting
|
||||
from a CGI script use this module to authenticate. Logouts are also handled here.
|
||||
|
||||
=head1 BEHAVIOUR
|
||||
|
||||
Users are first authenticated against the default authentication handler,
|
||||
using the CGI parameters I<Bugzilla_login> and I<Bugzilla_password>.
|
||||
|
||||
If no data is present for that, then cookies are tried, using
|
||||
L<Bugzilla::Auth::Login::WWW::CGI::Cookie>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
||||
@@ -1,115 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
|
||||
package Bugzilla::Auth::Login::WWW::CGI::Cookie;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Auth;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
|
||||
sub authenticate {
|
||||
my ($class, $login, $login_cookie) = @_;
|
||||
|
||||
return (AUTH_NODATA) unless defined $login && defined $login_cookie;
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
my $ipaddr = $cgi->remote_addr();
|
||||
my $netaddr = Bugzilla::Auth::get_netaddr($ipaddr);
|
||||
|
||||
# Anything goes for these params - they're just strings which
|
||||
# we're going to verify against the db
|
||||
trick_taint($login);
|
||||
trick_taint($login_cookie);
|
||||
trick_taint($ipaddr);
|
||||
|
||||
my $query = "SELECT profiles.userid, profiles.disabledtext " .
|
||||
"FROM logincookies, profiles " .
|
||||
"WHERE logincookies.cookie=? AND " .
|
||||
" logincookies.userid=profiles.userid AND " .
|
||||
" logincookies.userid=? AND " .
|
||||
" (logincookies.ipaddr=?";
|
||||
if (defined $netaddr) {
|
||||
trick_taint($netaddr);
|
||||
$query .= " OR logincookies.ipaddr=?";
|
||||
}
|
||||
$query .= ")";
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($userid, $disabledtext) = $dbh->selectrow_array($query, undef,
|
||||
$login_cookie,
|
||||
$login,
|
||||
$ipaddr,
|
||||
$netaddr);
|
||||
|
||||
return (AUTH_DISABLED, $userid, $disabledtext)
|
||||
if ($disabledtext);
|
||||
|
||||
if ($userid) {
|
||||
# If we logged in successfully, then update the lastused time on the
|
||||
# login cookie
|
||||
$dbh->do("UPDATE logincookies SET lastused=NOW() WHERE cookie=?",
|
||||
undef,
|
||||
$login_cookie);
|
||||
|
||||
return (AUTH_OK, $userid);
|
||||
}
|
||||
|
||||
# If we get here, then the login failed.
|
||||
return (AUTH_LOGINFAILED);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login::WWW::CGI::Cookie - cookie authentication for Bugzilla
|
||||
|
||||
=head1 SUMMARY
|
||||
|
||||
This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
|
||||
Bugzilla, which logs the user in using a persistent cookie stored in the
|
||||
C<logincookies> table.
|
||||
|
||||
The actual password is not stored in the cookie; only the userid and a
|
||||
I<logincookie> (which is used to reverify the login without requiring the
|
||||
password to be sent over the network) are. These I<logincookies> are
|
||||
restricted to certain IP addresses as a security meaure. The exact
|
||||
restriction can be specified by the admin via the C<loginnetmask> parameter.
|
||||
|
||||
This module does not ever send a cookie (It has no way of knowing when a user
|
||||
is successfully logged in). Instead L<Bugzilla::Auth::Login::WWW::CGI> handles this.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>, L<Bugzilla::Auth::Login::WWW::CGI>
|
||||
@@ -1,182 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Login::WWW::Env;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
|
||||
# XXX This does not currently work correctly with Param('requirelogin').
|
||||
# Bug 253636 will hopefully see that param's needs taken care of in a
|
||||
# parent module, but for the time being, this module does not honor
|
||||
# the param in the way that CGI.pm does.
|
||||
|
||||
my $matched_userid = '';
|
||||
my $matched_extern_id = '';
|
||||
my $disabledtext = '';
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth;
|
||||
|
||||
# Gather the environment variables
|
||||
my $env_id = $ENV{Param("auth_env_id")};
|
||||
my $env_email = $ENV{Param("auth_env_email")};
|
||||
my $env_realname = $ENV{Param("auth_env_realname")};
|
||||
|
||||
# allow undefined values to work with trick_taint
|
||||
for ($env_id, $env_email, $env_realname) { $_ ||= '' };
|
||||
# make sure the email field contains only a valid email address
|
||||
my $emailregexp = Param("emailregexp");
|
||||
$env_email =~ /($emailregexp)/;
|
||||
$env_email = $1;
|
||||
# untaint the remaining values
|
||||
trick_taint($env_id);
|
||||
trick_taint($env_realname);
|
||||
|
||||
if ($env_id | $env_email) {
|
||||
# Look in the DB for the extern_id
|
||||
if ($env_id) {
|
||||
|
||||
# Not having the email address defined but having an ID isn't
|
||||
# allowed.
|
||||
return undef unless $env_email;
|
||||
|
||||
$sth = $dbh->prepare("SELECT userid, disabledtext " .
|
||||
"FROM profiles WHERE extern_id=?");
|
||||
$sth->execute($env_id);
|
||||
my $fetched = $sth->fetch;
|
||||
if ($fetched) {
|
||||
$matched_userid = $fetched->[0];
|
||||
$disabledtext = $fetched->[1];
|
||||
}
|
||||
}
|
||||
|
||||
unless ($matched_userid) {
|
||||
# There was either no match for the external ID given, or one was
|
||||
# not present.
|
||||
#
|
||||
# Check to see if the email address is in there and has no
|
||||
# external id assigned. We test for both the login name (which we
|
||||
# also sent), and the id, so that we have a way of telling that we
|
||||
# got something instead of a bunch of NULLs
|
||||
$sth = $dbh->prepare("SELECT extern_id, userid, disabledtext " .
|
||||
"FROM profiles WHERE login_name=?");
|
||||
$sth->execute($env_email);
|
||||
|
||||
$sth->execute();
|
||||
my $fetched = $sth->fetch();
|
||||
if ($fetched) {
|
||||
($matched_extern_id, $matched_userid, $disabledtext) = @{$fetched};
|
||||
}
|
||||
if ($matched_userid) {
|
||||
if ($matched_extern_id) {
|
||||
# someone with a different external ID has that address!
|
||||
ThrowUserError("extern_id_conflict");
|
||||
}
|
||||
else
|
||||
{
|
||||
# someone with no external ID used that address, time to
|
||||
# add the ID!
|
||||
$sth = $dbh->prepare("UPDATE profiles " .
|
||||
"SET extern_id=? WHERE userid=?");
|
||||
$sth->execute($env_id, $matched_userid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Need to create a new user with that email address. Note
|
||||
# that cryptpassword has been filled in with '*', since the
|
||||
# user has no DB password.
|
||||
$sth = $dbh->prepare("INSERT INTO profiles ( " .
|
||||
"login_name, cryptpassword, " .
|
||||
"realname, disabledtext " .
|
||||
") VALUES ( ?, ?, ?, '' )");
|
||||
$sth->execute($env_email, '*', $env_realname);
|
||||
$sth = $dbh->prepare("SELECT last_insert_id()");
|
||||
$sth->execute();
|
||||
$matched_userid = $sth->fetch->[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# now that we hopefully have a username, we need to see if the data
|
||||
# has to be updated
|
||||
if ($matched_userid) {
|
||||
$sth = $dbh->prepare("SELECT login_name, realname " .
|
||||
"FROM profiles " .
|
||||
"WHERE userid=?");
|
||||
$sth->execute($matched_userid);
|
||||
my $fetched = $sth->fetch;
|
||||
my $username = $fetched->[0];
|
||||
my $this_realname = $fetched->[1];
|
||||
if ( ($username ne $env_email) ||
|
||||
($this_realname ne $env_realname) ) {
|
||||
|
||||
$sth = $dbh->prepare("UPDATE profiles " .
|
||||
"SET login_name=?, " .
|
||||
"realname=? " .
|
||||
"WHERE userid=?");
|
||||
$sth->execute($env_email,
|
||||
($env_realname || $this_realname),
|
||||
$matched_userid);
|
||||
$sth->execute;
|
||||
}
|
||||
}
|
||||
|
||||
# Now we throw an error if the user has been disabled
|
||||
if ($disabledtext) {
|
||||
ThrowUserError("account_disabled",
|
||||
{'disabled_reason' => $disabledtext});
|
||||
}
|
||||
|
||||
return $matched_userid;
|
||||
|
||||
}
|
||||
|
||||
# This auth style does not allow the user to log out.
|
||||
sub can_logout { return 0; }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Env - Environment Variable Authentication
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Many external user authentication systems supply login information to CGI
|
||||
programs via environment variables. This module checks to see if those
|
||||
variables are populated and, if so, assumes authentication was successful and
|
||||
returns the user's ID, having automatically created a new profile if
|
||||
necessary.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
How Auth Works
|
||||
==============
|
||||
Christian Reis <kiko@async.com.br>
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Authentication in Bugzilla is handled by a collection of modules that live in
|
||||
the Bugzilla::Auth package. These modules are organized hierarchically based
|
||||
upon their responsibility.
|
||||
|
||||
The authentication scheme is divided in two tasks: Login and Verify. Login
|
||||
involves gathering credentials from a user, while Verify validates them
|
||||
against an authentication service.
|
||||
|
||||
The Bugzilla parameters user_info_class and user_verify_class contain a
|
||||
list of Login and Verify modules, respectively.
|
||||
|
||||
Task: Login
|
||||
-----------
|
||||
|
||||
This task obtains user credentials based on a request. Examples of requests
|
||||
include CGI access from the Bugzilla web interface, email submissions and
|
||||
credentials supplied by standalone scripts.
|
||||
|
||||
Each type of Bugzilla front-end should have its own package. For instance,
|
||||
access via the Bugzilla web pages should go through Bugzilla::Auth::WWW.
|
||||
These packages would contain modules of their own to perform whatever extra
|
||||
functions are needed, like the CGI and Cookie modules in the case of WWW.
|
||||
|
||||
Task: Verify
|
||||
------------
|
||||
|
||||
This task validates user credentials against a user authentication service.
|
||||
|
||||
The default service in Bugzilla has been the database, which stores the
|
||||
login_name and cryptpasswd fields in the profiles table. An alternative means
|
||||
of validation, LDAP, is already supported, and other contributions would be
|
||||
appreciated.
|
||||
|
||||
The module layout is similar to the Login package, but there is no need for a
|
||||
sub-level as there is with Login request types.
|
||||
|
||||
Params
|
||||
------
|
||||
|
||||
There are two params that define behaviour for each authentication task. Each
|
||||
of them defines a comma-separated list of modules to be tried in order.
|
||||
|
||||
- user_info_class determines the module(s) used to obtain user
|
||||
credentials. This param is specific to the requests from Bugzilla web
|
||||
pages, so all of the listed modules live under
|
||||
Bugzilla::Auth::Login::WWW
|
||||
|
||||
- user_verify_class determines the module(s) used to verify credentials.
|
||||
This param is general and concerns the whole Bugzilla instance, since
|
||||
the same back end should be used regardless of what front end is used.
|
||||
|
||||
Responsibilities
|
||||
----------------
|
||||
|
||||
Bugzilla::Auth
|
||||
|
||||
This module is responsible for abstracting away as much as possible the
|
||||
login and logout tasks in Bugzilla.
|
||||
|
||||
It offers login() and logout() methods that are proxied to the selected
|
||||
login and verify packages.
|
||||
|
||||
Bugzilla::Auth::Login
|
||||
|
||||
This is a container to hold the various modules for each request type.
|
||||
|
||||
Bugzilla::Auth::Login::WWW
|
||||
|
||||
This module is responsible for abstracting away details of which web-based
|
||||
login modules exist and are in use. It offers login() and logout() methods
|
||||
that proxy through to whatever specific modules
|
||||
|
||||
Bugzilla::Auth::Verify
|
||||
|
||||
This module is responsible for abstracting away details of which
|
||||
credential verification modules exist, and should proxy calls through to
|
||||
them. There is a method that is particularly important, and which should
|
||||
be proxied through to the specific:
|
||||
|
||||
can_edit($type)
|
||||
|
||||
This method takes an argument that specifies what sort of change
|
||||
is being requested; the specific module should return 1 or 0 based
|
||||
on the fact that it implements or not the required change.
|
||||
|
||||
Current values for $type are "new" for new accounts, and "userid",
|
||||
"login_name", "realname" for their respective fields.
|
||||
|
||||
Specific Login Modules
|
||||
----------------------
|
||||
|
||||
WWW
|
||||
|
||||
The main authentication frontend; regular pages (CGIs) should use only
|
||||
this module. It offers a convenient frontend to the main functionality
|
||||
that CGIs need, using form parameters and cookies.
|
||||
|
||||
- Cookie
|
||||
|
||||
Implements part of the backend code that deals with browser
|
||||
cookies. It's actually tied in to DB.pm, so Cookie logins that use
|
||||
LDAP won't work at all.
|
||||
|
||||
LDAP
|
||||
|
||||
The other authentication module is LDAP-based; it is *only* used for
|
||||
password authentication and not for any other login-related task (it
|
||||
actually relies on the database to handle the profile information).
|
||||
|
||||
Legacy
|
||||
------
|
||||
|
||||
Bugzilla.pm
|
||||
|
||||
There is glue code that currently lives in the top-level module
|
||||
Bugzilla.pm; this module handles backwards-compatibility data that is used
|
||||
in a number of CGIs. This data has been slowly removed from the Bugzilla
|
||||
pages and eventually should go away completely, at which point Bugzilla.pm
|
||||
will be just a wrapper to conveniently offer template, cgi, dbh and user
|
||||
variables.
|
||||
|
||||
This module is meant to be used only by Bugzilla pages, and in the case of
|
||||
a reorganization which moves CGI-specific code to a subdirectory,
|
||||
Bugzilla.pm should go with it.
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::DB;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
|
||||
my $edit_options = {
|
||||
'new' => 1,
|
||||
'userid' => 0,
|
||||
'login_name' => 1,
|
||||
'realname' => 1,
|
||||
};
|
||||
|
||||
sub can_edit {
|
||||
my ($class, $type) = @_;
|
||||
return $edit_options->{$type};
|
||||
}
|
||||
|
||||
sub authenticate {
|
||||
my ($class, $username, $passwd) = @_;
|
||||
|
||||
return (AUTH_NODATA) unless defined $username && defined $passwd;
|
||||
|
||||
# We're just testing against the db: any value is ok
|
||||
trick_taint($username);
|
||||
|
||||
my $userid = $class->get_id_from_username($username);
|
||||
return (AUTH_LOGINFAILED) unless defined $userid;
|
||||
|
||||
return (AUTH_LOGINFAILED, $userid)
|
||||
unless $class->check_password($userid, $passwd);
|
||||
|
||||
# The user's credentials are okay, so delete any outstanding
|
||||
# password tokens they may have generated.
|
||||
require Bugzilla::Token;
|
||||
Bugzilla::Token::DeletePasswordTokens($userid, "user_logged_in");
|
||||
|
||||
# Account may have been disabled
|
||||
my $disabledtext = $class->get_disabled($userid);
|
||||
return (AUTH_DISABLED, $userid, $disabledtext)
|
||||
if $disabledtext ne '';
|
||||
|
||||
return (AUTH_OK, $userid);
|
||||
}
|
||||
|
||||
sub get_id_from_username {
|
||||
my ($class, $username) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT userid FROM profiles " .
|
||||
"WHERE login_name=?");
|
||||
my ($userid) = $dbh->selectrow_array($sth, undef, $username);
|
||||
return $userid;
|
||||
}
|
||||
|
||||
sub get_disabled {
|
||||
my ($class, $userid) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT disabledtext FROM profiles " .
|
||||
"WHERE userid=?");
|
||||
my ($text) = $dbh->selectrow_array($sth, undef, $userid);
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub check_password {
|
||||
my ($class, $userid, $passwd) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT cryptpassword FROM profiles " .
|
||||
"WHERE userid=?");
|
||||
my ($realcryptpwd) = $dbh->selectrow_array($sth, undef, $userid);
|
||||
|
||||
# Get the salt from the user's crypted password.
|
||||
my $salt = $realcryptpwd;
|
||||
|
||||
# Using the salt, crypt the password the user entered.
|
||||
my $enteredCryptedPassword = crypt($passwd, $salt);
|
||||
|
||||
return $enteredCryptedPassword eq $realcryptpwd;
|
||||
}
|
||||
|
||||
sub change_password {
|
||||
my ($class, $userid, $password) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cryptpassword = Crypt($password);
|
||||
$dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
|
||||
undef, $cryptpassword, $userid);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Verify::DB - database authentication for Bugzilla
|
||||
|
||||
=head1 SUMMARY
|
||||
|
||||
This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
|
||||
Bugzilla, which logs the user in using the password stored in the C<profiles>
|
||||
table. This is the most commonly used authentication module.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
||||
@@ -1,196 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::LDAP;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
|
||||
use Net::LDAP;
|
||||
|
||||
my $edit_options = {
|
||||
'new' => 0,
|
||||
'userid' => 0,
|
||||
'login_name' => 0,
|
||||
'realname' => 0,
|
||||
};
|
||||
|
||||
sub can_edit {
|
||||
my ($class, $type) = @_;
|
||||
return $edit_options->{$type};
|
||||
}
|
||||
|
||||
sub authenticate {
|
||||
my ($class, $username, $passwd) = @_;
|
||||
|
||||
# If no password was provided, then fail the authentication.
|
||||
# While it may be valid to not have an LDAP password, when you
|
||||
# bind without a password (regardless of the binddn value), you
|
||||
# will get an anonymous bind. I do not know of a way to determine
|
||||
# whether a bind is anonymous or not without making changes to the
|
||||
# LDAP access control settings
|
||||
return (AUTH_NODATA) unless $username && $passwd;
|
||||
|
||||
# We need to bind anonymously to the LDAP server. This is
|
||||
# because we need to get the Distinguished Name of the user trying
|
||||
# to log in. Some servers (such as iPlanet) allow you to have unique
|
||||
# uids spread out over a subtree of an area (such as "People"), so
|
||||
# just appending the Base DN to the uid isn't sufficient to get the
|
||||
# user's DN. For servers which don't work this way, there will still
|
||||
# be no harm done.
|
||||
my $LDAPserver = Param("LDAPserver");
|
||||
if ($LDAPserver eq "") {
|
||||
return (AUTH_ERROR, undef, "server_not_defined");
|
||||
}
|
||||
|
||||
my $LDAPport = "389"; # default LDAP port
|
||||
if($LDAPserver =~ /:/) {
|
||||
($LDAPserver, $LDAPport) = split(":",$LDAPserver);
|
||||
}
|
||||
my $LDAPconn = Net::LDAP->new($LDAPserver, port => $LDAPport, version => 3);
|
||||
if(!$LDAPconn) {
|
||||
return (AUTH_ERROR, undef, "connect_failed");
|
||||
}
|
||||
|
||||
my $mesg;
|
||||
if (Param("LDAPbinddn")) {
|
||||
my ($LDAPbinddn,$LDAPbindpass) = split(":",Param("LDAPbinddn"));
|
||||
$mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass);
|
||||
}
|
||||
else {
|
||||
$mesg = $LDAPconn->bind();
|
||||
}
|
||||
if($mesg->code) {
|
||||
return (AUTH_ERROR, undef,
|
||||
"connect_failed",
|
||||
{ errstr => $mesg->error });
|
||||
}
|
||||
|
||||
# We've got our anonymous bind; let's look up this user.
|
||||
$mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
|
||||
scope => "sub",
|
||||
filter => '(&(' . Param("LDAPuidattribute") . "=$username)" . Param("LDAPfilter") . ')',
|
||||
attrs => ['dn'],
|
||||
);
|
||||
return (AUTH_LOGINFAILED, undef, "lookup_failure")
|
||||
unless $mesg->count;
|
||||
|
||||
# Now we get the DN from this search.
|
||||
my $userDN = $mesg->shift_entry->dn;
|
||||
|
||||
# Now we attempt to bind as the specified user.
|
||||
$mesg = $LDAPconn->bind( $userDN, password => $passwd);
|
||||
|
||||
return (AUTH_LOGINFAILED) if $mesg->code;
|
||||
|
||||
# And now we're going to repeat the search, so that we can get the
|
||||
# mail attribute for this user.
|
||||
$mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
|
||||
scope => "sub",
|
||||
filter => '(&(' . Param("LDAPuidattribute") . "=$username)" . Param("LDAPfilter") . ')',
|
||||
);
|
||||
my $user_entry = $mesg->shift_entry if !$mesg->code && $mesg->count;
|
||||
if(!$user_entry || !$user_entry->exists(Param("LDAPmailattribute"))) {
|
||||
return (AUTH_ERROR, undef,
|
||||
"cannot_retreive_attr",
|
||||
{ attr => Param("LDAPmailattribute") });
|
||||
}
|
||||
|
||||
# get the mail attribute
|
||||
$username = $user_entry->get_value(Param("LDAPmailattribute"));
|
||||
# OK, so now we know that the user is valid. Lets try finding them in the
|
||||
# Bugzilla database
|
||||
|
||||
# XXX - should this part be made more generic, and placed in
|
||||
# Bugzilla::Auth? Lots of login mechanisms may have to do this, although
|
||||
# until we actually get some more, its hard to know - BB
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT userid, disabledtext " .
|
||||
"FROM profiles " .
|
||||
"WHERE login_name=?");
|
||||
my ($userid, $disabledtext) =
|
||||
$dbh->selectrow_array($sth,
|
||||
undef,
|
||||
$username);
|
||||
|
||||
# If the user doesn't exist, then they need to be added
|
||||
unless ($userid) {
|
||||
# We'll want the user's name for this.
|
||||
my $userRealName = $user_entry->get_value("displayName");
|
||||
if($userRealName eq "") {
|
||||
$userRealName = $user_entry->get_value("cn");
|
||||
}
|
||||
&::InsertNewUser($username, $userRealName);
|
||||
|
||||
($userid, $disabledtext) = $dbh->selectrow_array($sth,
|
||||
undef,
|
||||
$username);
|
||||
return (AUTH_ERROR, $userid, "no_userid")
|
||||
unless $userid;
|
||||
}
|
||||
|
||||
# we're done, so disconnect
|
||||
$LDAPconn->unbind;
|
||||
|
||||
# Test for disabled account
|
||||
return (AUTH_DISABLED, $userid, $disabledtext)
|
||||
if $disabledtext ne '';
|
||||
|
||||
# If we get to here, then the user is allowed to login, so we're done!
|
||||
return (AUTH_OK, $userid);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Verify::LDAP - LDAP based authentication for Bugzilla
|
||||
|
||||
This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
|
||||
Bugzilla, which logs the user in using an LDAP directory.
|
||||
|
||||
=head1 DISCLAIMER
|
||||
|
||||
B<This module is experimental>. It is poorly documented, and not very flexible.
|
||||
Search L<http://bugzilla.mozilla.org/> for a list of known LDAP bugs.
|
||||
|
||||
None of the core Bugzilla developers, nor any of the large installations, use
|
||||
this module, and so it has received less testing. (In fact, this iteration
|
||||
hasn't been tested at all)
|
||||
|
||||
Patches are accepted.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
||||
@@ -1,526 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dawn Endico <endico@mozilla.org>
|
||||
# Terry Weissman <terry@mozilla.org>
|
||||
# Chris Yeh <cyeh@bluemartini.com>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Dave Miller <justdave@bugzilla.org>
|
||||
|
||||
package Bugzilla::Bug;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::RelationSet;
|
||||
use vars qw($unconfirmedstate $legal_keywords @legal_platform
|
||||
@legal_priority @legal_severity @legal_opsys @legal_bugs_status
|
||||
@settable_resolution %components %versions %target_milestone
|
||||
@enterable_products %milestoneurl %prodmaxvotes);
|
||||
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
|
||||
use Bugzilla::Attachment;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Flag;
|
||||
use Bugzilla::FlagType;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
sub fields {
|
||||
# Keep this ordering in sync with bugzilla.dtd
|
||||
my @fields = qw(bug_id alias creation_ts short_desc delta_ts
|
||||
reporter_accessible cclist_accessible
|
||||
classification_id classification
|
||||
product component version rep_platform op_sys
|
||||
bug_status resolution
|
||||
bug_file_loc status_whiteboard keywords
|
||||
priority bug_severity target_milestone
|
||||
dependson blocked votes
|
||||
reporter assigned_to cc
|
||||
);
|
||||
|
||||
if (Param('useqacontact')) {
|
||||
push @fields, "qa_contact";
|
||||
}
|
||||
|
||||
if (Param('timetrackinggroup')) {
|
||||
push @fields, qw(estimated_time remaining_time actual_time);
|
||||
}
|
||||
|
||||
return @fields;
|
||||
}
|
||||
|
||||
my %ok_field;
|
||||
foreach my $key (qw(error groups
|
||||
longdescs milestoneurl attachments
|
||||
isopened isunconfirmed
|
||||
flag_types num_attachment_flag_types
|
||||
show_attachment_flags use_keywords any_flags_requesteeble
|
||||
),
|
||||
fields()) {
|
||||
$ok_field{$key}++;
|
||||
}
|
||||
|
||||
# create a new empty bug
|
||||
#
|
||||
sub new {
|
||||
my $type = shift();
|
||||
my %bug;
|
||||
|
||||
# create a ref to an empty hash and bless it
|
||||
#
|
||||
my $self = {%bug};
|
||||
bless $self, $type;
|
||||
|
||||
# construct from a hash containing a bug's info
|
||||
#
|
||||
if ($#_ == 1) {
|
||||
$self->initBug(@_);
|
||||
} else {
|
||||
confess("invalid number of arguments \($#_\)($_)");
|
||||
}
|
||||
|
||||
# bless as a Bug
|
||||
#
|
||||
return $self;
|
||||
}
|
||||
|
||||
# dump info about bug into hash unless user doesn't have permission
|
||||
# user_id 0 is used when person is not logged in.
|
||||
#
|
||||
sub initBug {
|
||||
my $self = shift();
|
||||
my ($bug_id, $user_id) = (@_);
|
||||
|
||||
$bug_id = trim($bug_id);
|
||||
|
||||
my $old_bug_id = $bug_id;
|
||||
|
||||
# If the bug ID isn't numeric, it might be an alias, so try to convert it.
|
||||
$bug_id = &::BugAliasToID($bug_id) if $bug_id !~ /^0*[1-9][0-9]*$/;
|
||||
|
||||
if ((! defined $bug_id) || (!$bug_id) || (!detaint_natural($bug_id))) {
|
||||
# no bug number given or the alias didn't match a bug
|
||||
$self->{'bug_id'} = $old_bug_id;
|
||||
$self->{'error'} = "InvalidBugId";
|
||||
return $self;
|
||||
}
|
||||
|
||||
# default userid 0, or get DBID if you used an email address
|
||||
unless (defined $user_id) {
|
||||
$user_id = 0;
|
||||
}
|
||||
else {
|
||||
if ($user_id =~ /^\@/) {
|
||||
$user_id = &::DBname_to_id($user_id);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{'who'} = new Bugzilla::User($user_id);
|
||||
|
||||
my $query = "
|
||||
SELECT
|
||||
bugs.bug_id, alias, products.classification_id, classifications.name,
|
||||
bugs.product_id, products.name, version,
|
||||
rep_platform, op_sys, bug_status, resolution, priority,
|
||||
bug_severity, bugs.component_id, components.name, 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, COALESCE(SUM(votes.vote_count), 0),
|
||||
reporter_accessible, cclist_accessible,
|
||||
estimated_time, remaining_time
|
||||
from bugs left join votes using(bug_id),
|
||||
classifications, products, components
|
||||
where bugs.bug_id = $bug_id
|
||||
AND classifications.id = products.classification_id
|
||||
AND products.id = bugs.product_id
|
||||
AND components.id = bugs.component_id
|
||||
group by bugs.bug_id";
|
||||
|
||||
&::SendSQL($query);
|
||||
my @row = ();
|
||||
|
||||
if ((@row = &::FetchSQLData()) && $self->{'who'}->can_see_bug($bug_id)) {
|
||||
my $count = 0;
|
||||
my %fields;
|
||||
foreach my $field ("bug_id", "alias", "classification_id", "classification",
|
||||
"product_id", "product", "version",
|
||||
"rep_platform", "op_sys", "bug_status", "resolution",
|
||||
"priority", "bug_severity", "component_id", "component",
|
||||
"assigned_to", "reporter", "bug_file_loc", "short_desc",
|
||||
"target_milestone", "qa_contact", "status_whiteboard",
|
||||
"creation_ts", "delta_ts", "votes",
|
||||
"reporter_accessible", "cclist_accessible",
|
||||
"estimated_time", "remaining_time")
|
||||
{
|
||||
$fields{$field} = shift @row;
|
||||
if (defined $fields{$field}) {
|
||||
$self->{$field} = $fields{$field};
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
} elsif (@row) {
|
||||
$self->{'bug_id'} = $bug_id;
|
||||
$self->{'error'} = "NotPermitted";
|
||||
return $self;
|
||||
} else {
|
||||
$self->{'bug_id'} = $bug_id;
|
||||
$self->{'error'} = "NotFound";
|
||||
return $self;
|
||||
}
|
||||
|
||||
$self->{'assigned_to'} = new Bugzilla::User($self->{'assigned_to'});
|
||||
$self->{'reporter'} = new Bugzilla::User($self->{'reporter'});
|
||||
|
||||
if (Param('useqacontact') && $self->{'qa_contact'} > 0) {
|
||||
$self->{'qa_contact'} = new Bugzilla::User($self->{'qa_contact'});
|
||||
} else {
|
||||
$self->{'qa_contact'} = undef;
|
||||
}
|
||||
|
||||
my $ccSet = new Bugzilla::RelationSet;
|
||||
$ccSet->mergeFromDB("select who from cc where bug_id=$bug_id");
|
||||
my @cc = $ccSet->toArrayOfStrings();
|
||||
if (@cc) {
|
||||
$self->{'cc'} = \@cc;
|
||||
}
|
||||
|
||||
if (@::legal_keywords) {
|
||||
&::SendSQL("SELECT keyworddefs.name
|
||||
FROM keyworddefs, keywords
|
||||
WHERE keywords.bug_id = $bug_id
|
||||
AND keyworddefs.id = keywords.keywordid
|
||||
ORDER BY keyworddefs.name");
|
||||
my @list;
|
||||
while (&::MoreSQLData()) {
|
||||
push(@list, &::FetchOneColumn());
|
||||
}
|
||||
if (@list) {
|
||||
$self->{'keywords'} = join(', ', @list);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{'attachments'} = Bugzilla::Attachment::query($self->{bug_id});
|
||||
|
||||
# The types of flags that can be set on this bug.
|
||||
# If none, no UI for setting flags will be displayed.
|
||||
my $flag_types =
|
||||
Bugzilla::FlagType::match({ 'target_type' => 'bug',
|
||||
'product_id' => $self->{'product_id'},
|
||||
'component_id' => $self->{'component_id'} });
|
||||
foreach my $flag_type (@$flag_types) {
|
||||
$flag_type->{'flags'} =
|
||||
Bugzilla::Flag::match({ 'bug_id' => $self->{bug_id},
|
||||
'type_id' => $flag_type->{'id'},
|
||||
'target_type' => 'bug',
|
||||
'is_active' => 1 });
|
||||
}
|
||||
$self->{'flag_types'} = $flag_types;
|
||||
$self->{'any_flags_requesteeble'} = grep($_->{'is_requesteeble'}, @$flag_types);
|
||||
|
||||
# The number of types of flags that can be set on attachments to this bug
|
||||
# and the number of flags on those attachments. One of these counts must be
|
||||
# greater than zero in order for the "flags" column to appear in the table
|
||||
# of attachments.
|
||||
my $num_attachment_flag_types =
|
||||
Bugzilla::FlagType::count({ 'target_type' => 'attachment',
|
||||
'product_id' => $self->{'product_id'},
|
||||
'component_id' => $self->{'component_id'} });
|
||||
my $num_attachment_flags =
|
||||
Bugzilla::Flag::count({ 'target_type' => 'attachment',
|
||||
'bug_id' => $self->{bug_id},
|
||||
'is_active' => 1 });
|
||||
|
||||
$self->{'show_attachment_flags'}
|
||||
= $num_attachment_flag_types || $num_attachment_flags;
|
||||
|
||||
$self->{'milestoneurl'} = $::milestoneurl{$self->{product}};
|
||||
|
||||
$self->{'isunconfirmed'} = ($self->{bug_status} eq $::unconfirmedstate);
|
||||
$self->{'isopened'} = &::IsOpenedState($self->{bug_status});
|
||||
|
||||
my @depends = EmitDependList("blocked", "dependson", $bug_id);
|
||||
if (@depends) {
|
||||
$self->{'dependson'} = \@depends;
|
||||
}
|
||||
my @blocked = EmitDependList("dependson", "blocked", $bug_id);
|
||||
if (@blocked) {
|
||||
$self->{'blocked'} = \@blocked;
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub dup_id {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'dup_id'} if exists $self->{'dup_id'};
|
||||
|
||||
$self->{'dup_id'} = undef;
|
||||
if ($self->{'resolution'} eq 'DUPLICATE') {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$self->{'dup_id'} =
|
||||
$dbh->selectrow_array(q{SELECT dupe_of
|
||||
FROM duplicates
|
||||
WHERE dupe = ?},
|
||||
undef,
|
||||
$self->{'bug_id'});
|
||||
}
|
||||
return $self->{'dup_id'};
|
||||
}
|
||||
|
||||
sub actual_time {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'actual_time'} if exists $self->{'actual_time'};
|
||||
|
||||
return undef unless Bugzilla->user->in_group(Param("timetrackinggroup"));
|
||||
|
||||
my $sth = Bugzilla->dbh->prepare("SELECT SUM(work_time)
|
||||
FROM longdescs
|
||||
WHERE longdescs.bug_id=?");
|
||||
$sth->execute($self->{bug_id});
|
||||
$self->{'actual_time'} = $sth->fetchrow_array();
|
||||
return $self->{'actual_time'};
|
||||
}
|
||||
|
||||
sub longdescs {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'longdescs'} if exists $self->{'longdescs'};
|
||||
|
||||
$self->{'longdescs'} = &::GetComments($self->{bug_id});
|
||||
|
||||
return $self->{'longdescs'};
|
||||
}
|
||||
|
||||
sub use_keywords {
|
||||
return @::legal_keywords;
|
||||
}
|
||||
|
||||
sub use_votes {
|
||||
my ($self) = @_;
|
||||
|
||||
return Param('usevotes')
|
||||
&& $::prodmaxvotes{$self->{product}} > 0;
|
||||
}
|
||||
|
||||
sub groups {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{'groups'} if exists $self->{'groups'};
|
||||
|
||||
my @groups;
|
||||
|
||||
# Some of this stuff needs to go into Bugzilla::User
|
||||
|
||||
# For every group, we need to know if there is ANY bug_group_map
|
||||
# record putting the current bug in that group and if there is ANY
|
||||
# user_group_map record putting the user in that group.
|
||||
# The LEFT JOINs are checking for record existence.
|
||||
#
|
||||
&::SendSQL("SELECT DISTINCT groups.id, name, description," .
|
||||
" bug_group_map.group_id IS NOT NULL," .
|
||||
" user_group_map.group_id IS NOT NULL," .
|
||||
" isactive, membercontrol, othercontrol" .
|
||||
" FROM groups" .
|
||||
" LEFT JOIN bug_group_map" .
|
||||
" ON bug_group_map.group_id = groups.id" .
|
||||
" AND bug_id = $self->{'bug_id'}" .
|
||||
" LEFT JOIN user_group_map" .
|
||||
" ON user_group_map.group_id = groups.id" .
|
||||
" AND user_id = $::userid" .
|
||||
" AND isbless = 0" .
|
||||
" LEFT JOIN group_control_map" .
|
||||
" ON group_control_map.group_id = groups.id" .
|
||||
" AND group_control_map.product_id = " . $self->{'product_id'} .
|
||||
" WHERE isbuggroup = 1");
|
||||
|
||||
while (&::MoreSQLData()) {
|
||||
my ($groupid, $name, $description, $ison, $ingroup, $isactive,
|
||||
$membercontrol, $othercontrol) = &::FetchSQLData();
|
||||
|
||||
$membercontrol ||= 0;
|
||||
|
||||
# For product groups, we only want to use the group if either
|
||||
# (1) The bit is set and not required, or
|
||||
# (2) The group is Shown or Default for members and
|
||||
# the user is a member of the group.
|
||||
if ($ison ||
|
||||
($isactive && $ingroup
|
||||
&& (($membercontrol == CONTROLMAPDEFAULT)
|
||||
|| ($membercontrol == CONTROLMAPSHOWN))
|
||||
))
|
||||
{
|
||||
my $ismandatory = $isactive
|
||||
&& ($membercontrol == CONTROLMAPMANDATORY);
|
||||
|
||||
push (@groups, { "bit" => $groupid,
|
||||
"name" => $name,
|
||||
"ison" => $ison,
|
||||
"ingroup" => $ingroup,
|
||||
"mandatory" => $ismandatory,
|
||||
"description" => $description });
|
||||
}
|
||||
}
|
||||
|
||||
$self->{'groups'} = \@groups;
|
||||
|
||||
return $self->{'groups'};
|
||||
}
|
||||
|
||||
sub user {
|
||||
my $self = shift;
|
||||
return $self->{'user'} if exists $self->{'user'};
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
my @movers = map { trim $_ } split(",", Param("movers"));
|
||||
my $canmove = Param("move-enabled") && Bugzilla->user->id &&
|
||||
(lsearch(\@movers, Bugzilla->user->login) != -1);
|
||||
|
||||
# In the below, if the person hasn't logged in, 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.
|
||||
my $privileged = (!Bugzilla->user->id)
|
||||
|| Bugzilla->user->in_group("editbugs")
|
||||
|| Bugzilla->user->id == $self->{'assigned_to'}{'id'}
|
||||
|| (Param('useqacontact') && $self->{'qa_contact'} &&
|
||||
Bugzilla->user->id == $self->{'qa_contact'}{'id'});
|
||||
my $isreporter = Bugzilla->user->id &&
|
||||
Bugzilla->user->id == $self->{'reporter'}{'id'};
|
||||
|
||||
my $canedit = $privileged || $isreporter;
|
||||
my $canconfirm = $privileged || Bugzilla->user->in_group("canconfirm");
|
||||
|
||||
$self->{'user'} = {canmove => $canmove,
|
||||
canconfirm => $canconfirm,
|
||||
canedit => $canedit,};
|
||||
return $self->{'user'};
|
||||
}
|
||||
|
||||
sub choices {
|
||||
my $self = shift;
|
||||
return $self->{'choices'} if exists $self->{'choices'};
|
||||
|
||||
&::GetVersionTable();
|
||||
|
||||
$self->{'choices'} = {};
|
||||
|
||||
# Fiddle the product list.
|
||||
my $seen_curr_prod;
|
||||
my @prodlist;
|
||||
|
||||
foreach my $product (@::enterable_products) {
|
||||
if ($product eq $self->{'product'}) {
|
||||
# if it's the product the bug is already in, it's ALWAYS in
|
||||
# the popup, period, whether the user can see it or not, and
|
||||
# regardless of the disallownew setting.
|
||||
$seen_curr_prod = 1;
|
||||
push(@prodlist, $product);
|
||||
next;
|
||||
}
|
||||
|
||||
if (!&::CanEnterProduct($product)) {
|
||||
# If we're using bug groups to restrict entry on products, and
|
||||
# this product has an entry group, and the user is not in that
|
||||
# group, we don't want to include that product in this list.
|
||||
next;
|
||||
}
|
||||
|
||||
push(@prodlist, $product);
|
||||
}
|
||||
|
||||
# The current product is part of the popup, even if new bugs are no longer
|
||||
# allowed for that product
|
||||
if (!$seen_curr_prod) {
|
||||
push (@prodlist, $self->{'product'});
|
||||
@prodlist = sort @prodlist;
|
||||
}
|
||||
|
||||
# Hack - this array contains "". See bug 106589.
|
||||
my @res = grep ($_, @::settable_resolution);
|
||||
|
||||
$self->{'choices'} =
|
||||
{
|
||||
'product' => \@prodlist,
|
||||
'rep_platform' => \@::legal_platform,
|
||||
'priority' => \@::legal_priority,
|
||||
'bug_severity' => \@::legal_severity,
|
||||
'op_sys' => \@::legal_opsys,
|
||||
'bug_status' => \@::legal_bugs_status,
|
||||
'resolution' => \@res,
|
||||
'component' => $::components{$self->{product}},
|
||||
'version' => $::versions{$self->{product}},
|
||||
'target_milestone' => $::target_milestone{$self->{product}},
|
||||
};
|
||||
|
||||
return $self->{'choices'};
|
||||
}
|
||||
|
||||
sub EmitDependList {
|
||||
my ($myfield, $targetfield, $bug_id) = (@_);
|
||||
my @list;
|
||||
&::SendSQL("select dependencies.$targetfield, bugs.bug_status
|
||||
from dependencies, bugs
|
||||
where dependencies.$myfield = $bug_id
|
||||
and bugs.bug_id = dependencies.$targetfield
|
||||
order by dependencies.$targetfield");
|
||||
while (&::MoreSQLData()) {
|
||||
my ($i, $stat) = (&::FetchSQLData());
|
||||
push @list, $i;
|
||||
}
|
||||
return @list;
|
||||
}
|
||||
|
||||
sub ValidateTime {
|
||||
my ($time, $field) = @_;
|
||||
if ($time > 99999.99 || $time < 0 || !($time =~ /^(?:\d+(?:\.\d*)?|\.\d+)$/)) {
|
||||
ThrowUserError("need_positive_number", {field => "$field"}, "abort");
|
||||
}
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
use vars qw($AUTOLOAD);
|
||||
my $attr = $AUTOLOAD;
|
||||
|
||||
$attr =~ s/.*:://;
|
||||
return unless $attr=~ /[^A-Z]/;
|
||||
confess ("invalid bug attribute $attr") unless $ok_field{$attr};
|
||||
|
||||
no strict 'refs';
|
||||
*$AUTOLOAD = sub {
|
||||
my $self = shift;
|
||||
if (defined $self->{$attr}) {
|
||||
return $self->{$attr};
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
goto &$AUTOLOAD;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,246 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::CGI;
|
||||
|
||||
use CGI qw(-no_xhtml -oldstyle_urls :private_tempfiles :unique_headers SERVER_PUSH);
|
||||
use CGI::Util qw(rearrange);
|
||||
|
||||
use base qw(CGI);
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Config;
|
||||
|
||||
# We need to disable output buffering - see bug 179174
|
||||
$| = 1;
|
||||
|
||||
# CGI.pm uses AUTOLOAD, but explicitly defines a DESTROY sub.
|
||||
# We need to do so, too, otherwise perl dies when the object is destroyed
|
||||
# and we don't have a DESTROY method (because CGI.pm's AUTOLOAD will |die|
|
||||
# on getting an unknown sub to try to call)
|
||||
sub DESTROY {};
|
||||
|
||||
sub new {
|
||||
my ($invocant, @args) = @_;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
my $self = $class->SUPER::new(@args);
|
||||
|
||||
# Make sure our outgoing cookie list is empty on each invocation
|
||||
$self->{Bugzilla_cookie_list} = [];
|
||||
|
||||
# Make sure that we don't send any charset headers
|
||||
$self->charset('');
|
||||
|
||||
# Check for errors
|
||||
# All of the Bugzilla code wants to do this, so do it here instead of
|
||||
# in each script
|
||||
|
||||
my $err = $self->cgi_error;
|
||||
|
||||
if ($err) {
|
||||
# XXX - under mod_perl we can use the request object to
|
||||
# enable the apache ErrorDocument stuff, which is localisable
|
||||
# (and localised by default under apache2).
|
||||
# This doesn't appear to be possible under mod_cgi.
|
||||
# Under mod_perl v2, though, this happens automatically, and the
|
||||
# message body is ignored.
|
||||
|
||||
# Note that this error block is only triggered by CGI.pm for malformed
|
||||
# multipart requests, and so should never happen unless there is a
|
||||
# browser bug.
|
||||
|
||||
print $self->header(-status => $err);
|
||||
|
||||
# ThrowCodeError wants to print the header, so it grabs Bugzilla->cgi
|
||||
# which creates a new Bugzilla::CGI object, which fails again, which
|
||||
# ends up here, and calls ThrowCodeError, and then recurses forever.
|
||||
# So don't use it.
|
||||
# In fact, we can't use templates at all, because we need a CGI object
|
||||
# to determine the template lang as well as the current url (from the
|
||||
# template)
|
||||
# Since this is an internal error which indicates a severe browser bug,
|
||||
# just die.
|
||||
die "CGI parsing error: $err";
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# We want this sorted plus the ability to exclude certain params
|
||||
sub canonicalise_query {
|
||||
my ($self, @exclude) = @_;
|
||||
|
||||
# Reconstruct the URL by concatenating the sorted param=value pairs
|
||||
my @parameters;
|
||||
foreach my $key (sort($self->param())) {
|
||||
# Leave this key out if it's in the exclude list
|
||||
next if lsearch(\@exclude, $key) != -1;
|
||||
|
||||
my $esc_key = url_quote($key);
|
||||
|
||||
foreach my $value ($self->param($key)) {
|
||||
if ($value) {
|
||||
my $esc_value = url_quote($value);
|
||||
|
||||
push(@parameters, "$esc_key=$esc_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return join("&", @parameters);
|
||||
}
|
||||
|
||||
# Overwrite to handle nph parameter. This should stay here until perl 5.8.1 CGI
|
||||
# has been fixed to support -nph as a parameter
|
||||
#
|
||||
sub multipart_init {
|
||||
my($self,@p) = @_;
|
||||
my($boundary,$nph,@other) = rearrange(['BOUNDARY','NPH'],@p);
|
||||
$boundary = $boundary || '------- =_aaaaaaaaaa0';
|
||||
$self->{'separator'} = "\r\n--$boundary\r\n";
|
||||
$self->{'final_separator'} = "\r\n--$boundary--\r\n";
|
||||
my $type = SERVER_PUSH($boundary);
|
||||
return $self->header(
|
||||
-nph => 0,
|
||||
-type => $type,
|
||||
(map { split "=", $_, 2 } @other),
|
||||
) . "WARNING: YOUR BROWSER DOESN'T SUPPORT THIS SERVER-PUSH TECHNOLOGY." . $self->multipart_end;
|
||||
}
|
||||
|
||||
# Override header so we can add the cookies in
|
||||
sub header {
|
||||
my $self = shift;
|
||||
|
||||
# Add the cookies in if we have any
|
||||
if (scalar(@{$self->{Bugzilla_cookie_list}})) {
|
||||
if (scalar(@_) == 1) {
|
||||
# if there's only one parameter, then it's a Content-Type.
|
||||
# Since we're adding parameters we have to name it.
|
||||
unshift(@_, '-type' => shift(@_));
|
||||
}
|
||||
unshift(@_, '-cookie' => $self->{Bugzilla_cookie_list});
|
||||
}
|
||||
|
||||
return $self->SUPER::header(@_) || "";
|
||||
}
|
||||
|
||||
# We override the entirety of multipart_start instead of falling through to
|
||||
# SUPER because the built-in one can't deal with cookies in any kind of sane
|
||||
# way. This sub is gratuitously swiped from the real CGI.pm, but fixed so
|
||||
# it actually works (but only as much as we need it to).
|
||||
sub multipart_start {
|
||||
my(@header);
|
||||
my($self,@p) = @_;
|
||||
my($type,@other) = rearrange([['TYPE','CONTENT_TYPE','CONTENT-TYPE']],@p);
|
||||
$type = $type || 'text/html';
|
||||
push(@header,"Content-Type: $type");
|
||||
|
||||
# Add the cookies in if we have any
|
||||
if (scalar(@{$self->{Bugzilla_cookie_list}})) {
|
||||
foreach my $cookie (@{$self->{Bugzilla_cookie_list}}) {
|
||||
push @header, "Set-Cookie: $cookie";
|
||||
}
|
||||
}
|
||||
|
||||
my $header = join($CGI::CRLF,@header)."${CGI::CRLF}${CGI::CRLF}";
|
||||
return $header;
|
||||
}
|
||||
|
||||
# The various parts of Bugzilla which create cookies don't want to have to
|
||||
# pass them arround to all of the callers. Instead, store them locally here,
|
||||
# and then output as required from |header|.
|
||||
sub send_cookie {
|
||||
my $self = shift;
|
||||
|
||||
# Add the default path in
|
||||
unshift(@_, '-path' => Param('cookiepath'));
|
||||
|
||||
# Use CGI::Cookie directly, because CGI.pm's |cookie| method gives the
|
||||
# current value if there isn't a -value attribute, which happens when
|
||||
# we're expiring an entry.
|
||||
require CGI::Cookie;
|
||||
my $cookie = CGI::Cookie->new(@_);
|
||||
push @{$self->{Bugzilla_cookie_list}}, $cookie;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::CGI - CGI handling for Bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::CGI;
|
||||
|
||||
my $cgi = new Bugzilla::CGI();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package inherits from the standard CGI module, to provide additional
|
||||
Bugzilla-specific functionality. In general, see L<the CGI.pm docs|CGI> for
|
||||
documention.
|
||||
|
||||
=head1 CHANGES FROM L<CGI.PM|CGI>
|
||||
|
||||
Bugzilla::CGI has some differences from L<CGI.pm|CGI>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<cgi_error> is automatically checked
|
||||
|
||||
After creating the CGI object, C<Bugzilla::CGI> automatically checks
|
||||
I<cgi_error>, and throws a CodeError if a problem is detected.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ADDITIONAL FUNCTIONS
|
||||
|
||||
I<Bugzilla::CGI> also includes additional functions.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<canonicalise_query(@exclude)>
|
||||
|
||||
This returns a sorted string of the parameters, suitable for use in a url.
|
||||
Values in C<@exclude> are not included in the result.
|
||||
|
||||
=item C<send_cookie>
|
||||
|
||||
This routine is identical to CGI.pm's C<cookie> routine, except that the cookie
|
||||
is sent to the browser, rather than returned. This should be used by all
|
||||
Bugzilla code (instead of C<cookie> or the C<-cookie> argument to C<header>),
|
||||
so that under mod_perl the headers can be sent correctly, using C<print> or
|
||||
the mod_perl APIs as appropriate.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<CGI|CGI>, L<CGI::Cookie|CGI::Cookie>
|
||||
@@ -1,366 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
|
||||
use strict;
|
||||
use lib ".";
|
||||
|
||||
# This module represents a chart.
|
||||
#
|
||||
# Note that it is perfectly legal for the 'lines' member variable of this
|
||||
# class (which is an array of Bugzilla::Series objects) to have empty members
|
||||
# in it. If this is true, the 'labels' array will also have empty members at
|
||||
# the same points.
|
||||
package Bugzilla::Chart;
|
||||
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Series;
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
# Create a ref to an empty hash and bless it
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
|
||||
if ($#_ == 0) {
|
||||
# Construct from a CGI object.
|
||||
$self->init($_[0]);
|
||||
}
|
||||
else {
|
||||
die("CGI object not passed in - invalid number of args \($#_\)($_)");
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub init {
|
||||
my $self = shift;
|
||||
my $cgi = shift;
|
||||
|
||||
# The data structure is a list of lists (lines) of Series objects.
|
||||
# There is a separate list for the labels.
|
||||
#
|
||||
# The URL encoding is:
|
||||
# line0=67&line0=73&line1=81&line2=67...
|
||||
# &label0=B+/+R+/+NEW&label1=...
|
||||
# &select0=1&select3=1...
|
||||
# &cumulate=1&datefrom=2002-02-03&dateto=2002-04-04&ctype=html...
|
||||
# >=1&labelgt=Grand+Total
|
||||
foreach my $param ($cgi->param()) {
|
||||
# Store all the lines
|
||||
if ($param =~ /^line(\d+)$/) {
|
||||
foreach my $series_id ($cgi->param($param)) {
|
||||
detaint_natural($series_id)
|
||||
|| &::ThrowCodeError("invalid_series_id");
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
push(@{$self->{'lines'}[$1]}, $series) if $series;
|
||||
}
|
||||
}
|
||||
|
||||
# Store all the labels
|
||||
if ($param =~ /^label(\d+)$/) {
|
||||
$self->{'labels'}[$1] = $cgi->param($param);
|
||||
}
|
||||
}
|
||||
|
||||
# Store the miscellaneous metadata
|
||||
$self->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0;
|
||||
$self->{'gt'} = $cgi->param('gt') ? 1 : 0;
|
||||
$self->{'labelgt'} = $cgi->param('labelgt');
|
||||
$self->{'datefrom'} = $cgi->param('datefrom');
|
||||
$self->{'dateto'} = $cgi->param('dateto');
|
||||
|
||||
# If we are cumulating, a grand total makes no sense
|
||||
$self->{'gt'} = 0 if $self->{'cumulate'};
|
||||
|
||||
# Make sure the dates are ones we are able to interpret
|
||||
foreach my $date ('datefrom', 'dateto') {
|
||||
if ($self->{$date}) {
|
||||
$self->{$date} = &::str2time($self->{$date})
|
||||
|| &::ThrowUserError("illegal_date", { date => $self->{$date}});
|
||||
}
|
||||
}
|
||||
|
||||
# datefrom can't be after dateto
|
||||
if ($self->{'datefrom'} && $self->{'dateto'} &&
|
||||
$self->{'datefrom'} > $self->{'dateto'})
|
||||
{
|
||||
&::ThrowUserError("misarranged_dates",
|
||||
{'datefrom' => $cgi->param('datefrom'),
|
||||
'dateto' => $cgi->param('dateto')});
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selected series are added to it.
|
||||
sub add {
|
||||
my $self = shift;
|
||||
my @series_ids = @_;
|
||||
|
||||
# If we are going from < 2 to >= 2 series, add the Grand Total line.
|
||||
if (!$self->{'gt'}) {
|
||||
my $current_size = scalar($self->getSeriesIDs());
|
||||
if ($current_size < 2 &&
|
||||
$current_size + scalar(@series_ids) >= 2)
|
||||
{
|
||||
$self->{'gt'} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Create new Series and push them on to the list of lines.
|
||||
# Note that new lines have no label; the display template is responsible
|
||||
# for inventing something sensible.
|
||||
foreach my $series_id (@series_ids) {
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
if ($series) {
|
||||
push(@{$self->{'lines'}}, [$series]);
|
||||
push(@{$self->{'labels'}}, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selections are removed from it.
|
||||
sub remove {
|
||||
my $self = shift;
|
||||
my @line_ids = @_;
|
||||
|
||||
foreach my $line_id (@line_ids) {
|
||||
if ($line_id == 65536) {
|
||||
# Magic value - delete Grand Total.
|
||||
$self->{'gt'} = 0;
|
||||
}
|
||||
else {
|
||||
delete($self->{'lines'}->[$line_id]);
|
||||
delete($self->{'labels'}->[$line_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Alter Chart so that the selections are summed.
|
||||
sub sum {
|
||||
my $self = shift;
|
||||
my @line_ids = @_;
|
||||
|
||||
# We can't add the Grand Total to things.
|
||||
@line_ids = grep(!/^65536$/, @line_ids);
|
||||
|
||||
# We can't add less than two things.
|
||||
return if scalar(@line_ids) < 2;
|
||||
|
||||
my @series;
|
||||
my $label = "";
|
||||
my $biggestlength = 0;
|
||||
|
||||
# We rescue the Series objects of all the series involved in the sum.
|
||||
foreach my $line_id (@line_ids) {
|
||||
my @line = @{$self->{'lines'}->[$line_id]};
|
||||
|
||||
foreach my $series (@line) {
|
||||
push(@series, $series);
|
||||
}
|
||||
|
||||
# We keep the label that labels the line with the most series.
|
||||
if (scalar(@line) > $biggestlength) {
|
||||
$biggestlength = scalar(@line);
|
||||
$label = $self->{'labels'}->[$line_id];
|
||||
}
|
||||
}
|
||||
|
||||
$self->remove(@line_ids);
|
||||
|
||||
push(@{$self->{'lines'}}, \@series);
|
||||
push(@{$self->{'labels'}}, $label);
|
||||
}
|
||||
|
||||
sub data {
|
||||
my $self = shift;
|
||||
$self->{'_data'} ||= $self->readData();
|
||||
return $self->{'_data'};
|
||||
}
|
||||
|
||||
# Convert the Chart's data into a plottable form in $self->{'_data'}.
|
||||
sub readData {
|
||||
my $self = shift;
|
||||
my @data;
|
||||
|
||||
# Note: you get a bad image if getSeriesIDs returns nothing
|
||||
# We need to handle errors better.
|
||||
my $series_ids = join(",", $self->getSeriesIDs());
|
||||
|
||||
# Work out the date boundaries for our data.
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# The date used is the one given if it's in a sensible range; otherwise,
|
||||
# it's the earliest or latest date in the database as appropriate.
|
||||
my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id IN ($series_ids)");
|
||||
$datefrom = &::str2time($datefrom);
|
||||
|
||||
if ($self->{'datefrom'} && $self->{'datefrom'} > $datefrom) {
|
||||
$datefrom = $self->{'datefrom'};
|
||||
}
|
||||
|
||||
my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " .
|
||||
"FROM series_data " .
|
||||
"WHERE series_id IN ($series_ids)");
|
||||
$dateto = &::str2time($dateto);
|
||||
|
||||
if ($self->{'dateto'} && $self->{'dateto'} < $dateto) {
|
||||
$dateto = $self->{'dateto'};
|
||||
}
|
||||
|
||||
# Prepare the query which retrieves the data for each series
|
||||
my $query = "SELECT TO_DAYS(series_date) - " .
|
||||
" TO_DAYS(FROM_UNIXTIME($datefrom)), " .
|
||||
"series_value FROM series_data " .
|
||||
"WHERE series_id = ? " .
|
||||
"AND series_date >= FROM_UNIXTIME($datefrom)";
|
||||
if ($dateto) {
|
||||
$query .= " AND series_date <= FROM_UNIXTIME($dateto)";
|
||||
}
|
||||
|
||||
my $sth = $dbh->prepare($query);
|
||||
|
||||
my $gt_index = $self->{'gt'} ? scalar(@{$self->{'lines'}}) : undef;
|
||||
my $line_index = 0;
|
||||
|
||||
foreach my $line (@{$self->{'lines'}}) {
|
||||
# Even if we end up with no data, we need an empty arrayref to prevent
|
||||
# errors in the PNG-generating code
|
||||
$data[$line_index] = [];
|
||||
|
||||
foreach my $series (@$line) {
|
||||
|
||||
# Get the data for this series and add it on
|
||||
$sth->execute($series->{'series_id'});
|
||||
my $points = $sth->fetchall_arrayref();
|
||||
|
||||
foreach my $point (@$points) {
|
||||
my ($datediff, $value) = @$point;
|
||||
$data[$line_index][$datediff] ||= 0;
|
||||
$data[$line_index][$datediff] += $value;
|
||||
|
||||
# Add to the grand total, if we are doing that
|
||||
if ($gt_index) {
|
||||
$data[$gt_index][$datediff] ||= 0;
|
||||
$data[$gt_index][$datediff] += $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$line_index++;
|
||||
}
|
||||
|
||||
# Add the x-axis labels into the data structure
|
||||
my $date_progression = generateDateProgression($datefrom, $dateto);
|
||||
unshift(@data, $date_progression);
|
||||
|
||||
if ($self->{'gt'}) {
|
||||
# Add Grand Total to label list
|
||||
push(@{$self->{'labels'}}, $self->{'labelgt'});
|
||||
|
||||
$data[$gt_index] ||= [];
|
||||
}
|
||||
|
||||
return \@data;
|
||||
}
|
||||
|
||||
# Flatten the data structure into a list of series_ids
|
||||
sub getSeriesIDs {
|
||||
my $self = shift;
|
||||
my @series_ids;
|
||||
|
||||
foreach my $line (@{$self->{'lines'}}) {
|
||||
foreach my $series (@$line) {
|
||||
push(@series_ids, $series->{'series_id'});
|
||||
}
|
||||
}
|
||||
|
||||
return @series_ids;
|
||||
}
|
||||
|
||||
# Class method to get the data necessary to populate the "select series"
|
||||
# widgets on various pages.
|
||||
sub getVisibleSeries {
|
||||
my %cats;
|
||||
|
||||
# List of groups the user is in; use -1 to make sure it's not empty.
|
||||
my $grouplist = join(", ", (-1, values(%{Bugzilla->user->groups})));
|
||||
|
||||
# Get all visible series
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " .
|
||||
"series.name, series.series_id " .
|
||||
"FROM series " .
|
||||
"INNER JOIN series_categories AS cc1 " .
|
||||
" ON series.category = cc1.id " .
|
||||
"INNER JOIN series_categories AS cc2 " .
|
||||
" ON series.subcategory = cc2.id " .
|
||||
"LEFT JOIN category_group_map AS cgm " .
|
||||
" ON series.category = cgm.category_id " .
|
||||
" AND cgm.group_id NOT IN($grouplist) " .
|
||||
"WHERE creator = " . Bugzilla->user->id . " OR " .
|
||||
" cgm.category_id IS NULL " .
|
||||
"GROUP BY series_id");
|
||||
foreach my $series (@$serieses) {
|
||||
my ($cat, $subcat, $name, $series_id) = @$series;
|
||||
$cats{$cat}{$subcat}{$name} = $series_id;
|
||||
}
|
||||
|
||||
return \%cats;
|
||||
}
|
||||
|
||||
sub generateDateProgression {
|
||||
my ($datefrom, $dateto) = @_;
|
||||
my @progression;
|
||||
|
||||
$dateto = $dateto || time();
|
||||
my $oneday = 60 * 60 * 24;
|
||||
|
||||
# When the from and to dates are converted by str2time(), you end up with
|
||||
# a time figure representing midnight at the beginning of that day. We
|
||||
# adjust the times by 1/3 and 2/3 of a day respectively to prevent
|
||||
# edge conditions in time2str().
|
||||
$datefrom += $oneday / 3;
|
||||
$dateto += (2 * $oneday) / 3;
|
||||
|
||||
while ($datefrom < $dateto) {
|
||||
push (@progression, &::time2str("%Y-%m-%d", $datefrom));
|
||||
$datefrom += $oneday;
|
||||
}
|
||||
|
||||
return \@progression;
|
||||
}
|
||||
|
||||
sub dump {
|
||||
my $self = shift;
|
||||
|
||||
# Make sure we've read in our data
|
||||
my $data = $self->data;
|
||||
|
||||
require Data::Dumper;
|
||||
print "<pre>Bugzilla::Chart object:\n";
|
||||
print Data::Dumper::Dumper($self);
|
||||
print "</pre>";
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,447 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jake <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Config;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Exporter);
|
||||
|
||||
use Bugzilla::Util;
|
||||
|
||||
# Under mod_perl, get this from a .htaccess config variable,
|
||||
# and/or default from the current 'real' dir
|
||||
# At some stage after this, it may be possible for these dir locations
|
||||
# to go into localconfig. localconfig can't be specified in a config file,
|
||||
# except possibly with mod_perl. If you move localconfig, you need to change
|
||||
# the define here.
|
||||
# $libpath is really only for mod_perl; its not yet possible to move the
|
||||
# .pms elsewhere.
|
||||
# $webdotdir must be in the webtree somewhere. Even if you use a local dot,
|
||||
# we output images to there. Also, if $webdot dir is not relative to the
|
||||
# bugzilla root directory, you'll need to change showdependancygraph.cgi to
|
||||
# set image_url to the correct location.
|
||||
# The script should really generate these graphs directly...
|
||||
# Note that if $libpath is changed, some stuff will break, notably dependancy
|
||||
# graphs (since the path will be wrong in the HTML). This will be fixed at
|
||||
# some point.
|
||||
|
||||
our $libpath = '.';
|
||||
our $localconfig = "$libpath/localconfig";
|
||||
our $datadir = "$libpath/data";
|
||||
our $templatedir = "$libpath/template";
|
||||
our $webdotdir = "$datadir/webdot";
|
||||
|
||||
# Module stuff
|
||||
@Bugzilla::Config::EXPORT = qw(Param);
|
||||
|
||||
# Don't export localvars by default - people should have to explicitly
|
||||
# ask for it, as a (probably futile) attempt to stop code using it
|
||||
# when it shouldn't
|
||||
# ChmodDataFile is here until that stuff all moves out of globals.pl
|
||||
# into this file
|
||||
@Bugzilla::Config::EXPORT_OK = qw(ChmodDataFile);
|
||||
|
||||
%Bugzilla::Config::EXPORT_TAGS =
|
||||
(
|
||||
admin => [qw(GetParamList UpdateParams SetParam WriteParams)],
|
||||
db => [qw($db_host $db_port $db_name $db_user $db_pass $db_sock)],
|
||||
locations => [qw($libpath $localconfig $datadir $templatedir $webdotdir)],
|
||||
);
|
||||
Exporter::export_ok_tags('admin', 'db', 'locations');
|
||||
|
||||
# Bugzilla version
|
||||
$Bugzilla::Config::VERSION = "2.19";
|
||||
|
||||
use Safe;
|
||||
|
||||
use vars qw(@param_list);
|
||||
|
||||
# Data::Dumper is required as needed, below. The problem is that then when
|
||||
# the code locally sets $Data::Dumper::Foo, this triggers 'used only once'
|
||||
# warnings.
|
||||
# We can't predeclare another package's vars, though, so just use them
|
||||
{
|
||||
local $Data::Dumper::Sortkeys;
|
||||
local $Data::Dumper::Terse;
|
||||
local $Data::Dumper::Indent;
|
||||
}
|
||||
|
||||
my %param;
|
||||
|
||||
# INITIALISATION CODE
|
||||
|
||||
# XXX - mod_perl - need to register Apache init handler for params
|
||||
sub _load_datafiles {
|
||||
# read in localconfig variables
|
||||
do $localconfig;
|
||||
|
||||
if (-e "$datadir/params") {
|
||||
# Handle reading old param files by munging the symbol table
|
||||
# Don't have to do this if we use safe mode, since its evaled
|
||||
# in a sandbox where $foo is in the same module as $::foo
|
||||
#local *::param = \%param;
|
||||
|
||||
# Note that checksetup.pl sets file permissions on '$datadir/params'
|
||||
|
||||
# Using Safe mode is _not_ a guarantee of safety if someone does
|
||||
# manage to write to the file. However, it won't hurt...
|
||||
# See bug 165144 for not needing to eval this at all
|
||||
my $s = new Safe;
|
||||
|
||||
$s->rdo("$datadir/params");
|
||||
die "Error reading $datadir/params: $!" if $!;
|
||||
die "Error evaluating $datadir/params: $@" if $@;
|
||||
|
||||
# Now read the param back out from the sandbox
|
||||
%param = %{$s->varglob('param')};
|
||||
}
|
||||
}
|
||||
|
||||
# Load in the datafiles
|
||||
_load_datafiles();
|
||||
|
||||
# Load in the param defintions
|
||||
unless (my $ret = do 'defparams.pl') {
|
||||
die "Couldn't parse defparams.pl: $@" if $@;
|
||||
die "Couldn't do defparams.pl: $!" unless defined $ret;
|
||||
die "Couldn't run defparams.pl" unless $ret;
|
||||
}
|
||||
|
||||
# Stick the params into a hash
|
||||
my %params;
|
||||
foreach my $item (@param_list) {
|
||||
$params{$item->{'name'}} = $item;
|
||||
}
|
||||
|
||||
# END INIT CODE
|
||||
|
||||
# Subroutines go here
|
||||
|
||||
sub Param {
|
||||
my ($param) = @_;
|
||||
|
||||
# By this stage, the param must be in the hash
|
||||
die "Can't find param named $param" unless (exists $params{$param});
|
||||
|
||||
# When module startup code runs (which is does even via -c, when using
|
||||
# |use|), we may try to grab params which don't exist yet. This affects
|
||||
# tests, so have this as a fallback for the -c case
|
||||
return $params{$param}->{default} if ($^C && not exists $param{$param});
|
||||
|
||||
# If we have a value for the param, return it
|
||||
return $param{$param} if exists $param{$param};
|
||||
|
||||
# Else error out
|
||||
die "No value for param $param (try running checksetup.pl again)";
|
||||
}
|
||||
|
||||
sub GetParamList {
|
||||
return @param_list;
|
||||
}
|
||||
|
||||
sub SetParam {
|
||||
my ($name, $value) = @_;
|
||||
|
||||
die "Unknown param $name" unless (exists $params{$name});
|
||||
|
||||
my $entry = $params{$name};
|
||||
|
||||
# sanity check the value
|
||||
|
||||
# XXX - This runs the checks. Which would be good, except that
|
||||
# check_shadowdb creates the database as a sideeffect, and so the
|
||||
# checker fails the second time arround...
|
||||
if ($name ne 'shadowdb' && exists $entry->{'checker'}) {
|
||||
my $err = $entry->{'checker'}->($value, $entry);
|
||||
die "Param $name is not valid: $err" unless $err eq '';
|
||||
}
|
||||
|
||||
$param{$name} = $value;
|
||||
}
|
||||
|
||||
sub UpdateParams {
|
||||
# --- PARAM CONVERSION CODE ---
|
||||
|
||||
# Note that this isn't particuarly 'clean' in terms of separating
|
||||
# the backend code (ie this) from the actual params.
|
||||
# We don't care about that, though
|
||||
|
||||
# Old bugzilla versions stored the version number in the params file
|
||||
# We don't want it, so get rid of it
|
||||
delete $param{'version'};
|
||||
|
||||
# Change from usebrowserinfo to defaultplatform/defaultopsys combo
|
||||
if (exists $param{'usebrowserinfo'}) {
|
||||
if (!$param{'usebrowserinfo'}) {
|
||||
if (!exists $param{'defaultplatform'}) {
|
||||
$param{'defaultplatform'} = 'Other';
|
||||
}
|
||||
if (!exists $param{'defaultopsys'}) {
|
||||
$param{'defaultopsys'} = 'other';
|
||||
}
|
||||
}
|
||||
delete $param{'usebrowserinfo'};
|
||||
}
|
||||
|
||||
# Change from a boolean for quips to multi-state
|
||||
if (exists $param{'usequip'} && !exists $param{'enablequips'}) {
|
||||
$param{'enablequips'} = $param{'usequip'} ? 'on' : 'off';
|
||||
delete $param{'usequip'};
|
||||
}
|
||||
|
||||
# Change from old product groups to controls for group_control_map
|
||||
# 2002-10-14 bug 147275 bugreport@peshkin.net
|
||||
if (exists $param{'usebuggroups'} && !exists $param{'makeproductgroups'}) {
|
||||
$param{'makeproductgroups'} = $param{'usebuggroups'};
|
||||
}
|
||||
if (exists $param{'usebuggroupsentry'}
|
||||
&& !exists $param{'useentrygroupdefault'}) {
|
||||
$param{'useentrygroupdefault'} = $param{'usebuggroupsentry'};
|
||||
}
|
||||
|
||||
# Modularise auth code
|
||||
if (exists $param{'useLDAP'} && !exists $param{'loginmethod'}) {
|
||||
$param{'loginmethod'} = $param{'useLDAP'} ? "LDAP" : "DB";
|
||||
}
|
||||
|
||||
# set verify method to whatever loginmethod was
|
||||
if (exists $param{'loginmethod'} && !exists $param{'user_verify_class'}) {
|
||||
$param{'user_verify_class'} = $param{'loginmethod'};
|
||||
delete $param{'loginmethod'};
|
||||
}
|
||||
|
||||
# --- DEFAULTS FOR NEW PARAMS ---
|
||||
|
||||
foreach my $item (@param_list) {
|
||||
my $name = $item->{'name'};
|
||||
$param{$name} = $item->{'default'} unless exists $param{$name};
|
||||
}
|
||||
|
||||
# --- REMOVE OLD PARAMS ---
|
||||
|
||||
my @oldparams = ();
|
||||
|
||||
# Remove any old params
|
||||
foreach my $item (keys %param) {
|
||||
if (!grep($_ eq $item, map ($_->{'name'}, @param_list))) {
|
||||
require Data::Dumper;
|
||||
|
||||
local $Data::Dumper::Terse = 1;
|
||||
local $Data::Dumper::Indent = 0;
|
||||
push (@oldparams, [$item, Data::Dumper->Dump([$param{$item}])]);
|
||||
delete $param{$item};
|
||||
}
|
||||
}
|
||||
|
||||
return @oldparams;
|
||||
}
|
||||
|
||||
sub WriteParams {
|
||||
require Data::Dumper;
|
||||
|
||||
# This only has an affect for Data::Dumper >= 2.12 (ie perl >= 5.8.0)
|
||||
# Its just cosmetic, though, so that doesn't matter
|
||||
local $Data::Dumper::Sortkeys = 1;
|
||||
|
||||
require File::Temp;
|
||||
my ($fh, $tmpname) = File::Temp::tempfile('params.XXXXX',
|
||||
DIR => $datadir );
|
||||
|
||||
print $fh (Data::Dumper->Dump([ \%param ], [ '*param' ]))
|
||||
|| die "Can't write param file: $!";
|
||||
|
||||
close $fh;
|
||||
|
||||
rename $tmpname, "$datadir/params"
|
||||
|| die "Can't rename $tmpname to $datadir/params: $!";
|
||||
|
||||
ChmodDataFile("$datadir/params", 0666);
|
||||
}
|
||||
|
||||
# Some files in the data directory must be world readable iff we don't have
|
||||
# a webserver group. Call this function to do this.
|
||||
# This will become a private function once all the datafile handling stuff
|
||||
# moves into this package
|
||||
|
||||
# This sub is not perldoc'd for that reason - noone should know about it
|
||||
sub ChmodDataFile {
|
||||
my ($file, $mask) = @_;
|
||||
my $perm = 0770;
|
||||
if ((stat($datadir))[2] & 0002) {
|
||||
$perm = 0777;
|
||||
}
|
||||
$perm = $perm & $mask;
|
||||
chmod $perm,$file;
|
||||
}
|
||||
|
||||
sub check_multi {
|
||||
my ($value, $param) = (@_);
|
||||
|
||||
if ($param->{'type'} eq "s") {
|
||||
unless (lsearch($param->{'choices'}, $value) >= 0) {
|
||||
return "Invalid choice '$value' for single-select list param '$param'";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
elsif ($param->{'type'} eq "m") {
|
||||
foreach my $chkParam (@$value) {
|
||||
unless (lsearch($param->{'choices'}, $chkParam) >= 0) {
|
||||
return "Invalid choice '$chkParam' for multi-select list param '$param'";
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return "Invalid param type '$param->{'type'}' for check_multi(); " .
|
||||
"contact your Bugzilla administrator";
|
||||
}
|
||||
}
|
||||
|
||||
sub check_numeric {
|
||||
my ($value) = (@_);
|
||||
if ($value !~ /^[0-9]+$/) {
|
||||
return "must be a numeric value";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
sub check_regexp {
|
||||
my ($value) = (@_);
|
||||
eval { qr/$value/ };
|
||||
return $@;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Config - Configuration parameters for Bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# Getting parameters
|
||||
use Bugzilla::Config;
|
||||
|
||||
my $fooSetting = Param('foo');
|
||||
|
||||
# Administration functions
|
||||
use Bugzilla::Config qw(:admin);
|
||||
|
||||
my @valid_params = GetParamList();
|
||||
my @removed_params = UpgradeParams();
|
||||
SetParam($param, $value);
|
||||
WriteParams();
|
||||
|
||||
# Localconfig variables may also be imported
|
||||
use Bugzilla::Config qw(:db);
|
||||
print "Connecting to $db_name as $db_user with $db_pass\n";
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package contains ways to access Bugzilla configuration parameters.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=head2 Parameters
|
||||
|
||||
Parameters can be set, retrieved, and updated.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<Param($name)>
|
||||
|
||||
Returns the Param with the specified name. Either a string, or, in the case
|
||||
of multiple-choice parameters, an array reference.
|
||||
|
||||
=item C<GetParamList()>
|
||||
|
||||
Returns the list of known parameter types, from defparams.pl. Users should not
|
||||
rely on this method; it is intended for editparams/doeditparams only
|
||||
|
||||
The format for the list is specified in defparams.pl
|
||||
|
||||
=item C<SetParam($name, $value)>
|
||||
|
||||
Sets the param named $name to $value. Values are checked using the checker
|
||||
function for the given param if one exists.
|
||||
|
||||
=item C<UpdateParams()>
|
||||
|
||||
Updates the parameters, by transitioning old params to new formats, setting
|
||||
defaults for new params, and removing obsolete ones.
|
||||
|
||||
Any removed params are returned in a list, with elements [$item, $oldvalue]
|
||||
where $item is the entry in the param list.
|
||||
|
||||
=item C<WriteParams()>
|
||||
|
||||
Writes the parameters to disk.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Parameter checking functions
|
||||
|
||||
All parameter checking functions are called with two parameters:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
The new value for the parameter
|
||||
|
||||
=item *
|
||||
|
||||
A reference to the entry in the param list for this parameter
|
||||
|
||||
=back
|
||||
|
||||
Functions should return error text, or the empty string if there was no error.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<check_multi>
|
||||
|
||||
Checks that a multi-valued parameter (ie type C<s> or type C<m>) satisfies
|
||||
its contraints.
|
||||
|
||||
=item C<check_numeric>
|
||||
|
||||
Checks that the value is a valid number
|
||||
|
||||
=item C<check_regexp>
|
||||
|
||||
Checks that the value is a valid regexp
|
||||
|
||||
=back
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dawn Endico <endico@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Jake <jake@bugzilla.org>
|
||||
# J. Paul Reed <preed@sigkill.com>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
|
||||
|
||||
package Bugzilla::Constants;
|
||||
use strict;
|
||||
use base qw(Exporter);
|
||||
|
||||
@Bugzilla::Constants::EXPORT = qw(
|
||||
CONTROLMAPNA
|
||||
CONTROLMAPSHOWN
|
||||
CONTROLMAPDEFAULT
|
||||
CONTROLMAPMANDATORY
|
||||
|
||||
AUTH_OK
|
||||
AUTH_NODATA
|
||||
AUTH_ERROR
|
||||
AUTH_LOGINFAILED
|
||||
AUTH_DISABLED
|
||||
|
||||
LOGIN_OPTIONAL
|
||||
LOGIN_NORMAL
|
||||
LOGIN_REQUIRED
|
||||
|
||||
LOGOUT_ALL
|
||||
LOGOUT_CURRENT
|
||||
LOGOUT_KEEP_CURRENT
|
||||
|
||||
GRANT_DIRECT
|
||||
GRANT_DERIVED
|
||||
GRANT_REGEXP
|
||||
|
||||
GROUP_MEMBERSHIP
|
||||
GROUP_BLESS
|
||||
GROUP_VISIBLE
|
||||
);
|
||||
|
||||
@Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
|
||||
|
||||
# CONSTANTS
|
||||
#
|
||||
# ControlMap constants for group_control_map.
|
||||
# membercontol:othercontrol => meaning
|
||||
# Na:Na => Bugs in this product may not be restricted to this
|
||||
# group.
|
||||
# Shown:Na => Members of the group may restrict bugs
|
||||
# in this product to this group.
|
||||
# Shown:Shown => Members of the group may restrict bugs
|
||||
# in this product to this group.
|
||||
# Anyone who can enter bugs in this product may initially
|
||||
# restrict bugs in this product to this group.
|
||||
# Shown:Mandatory => Members of the group may restrict bugs
|
||||
# in this product to this group.
|
||||
# Non-members who can enter bug in this product
|
||||
# will be forced to restrict it.
|
||||
# Default:Na => Members of the group may restrict bugs in this
|
||||
# product to this group and do so by default.
|
||||
# Default:Default => Members of the group may restrict bugs in this
|
||||
# product to this group and do so by default and
|
||||
# nonmembers have this option on entry.
|
||||
# Default:Mandatory => Members of the group may restrict bugs in this
|
||||
# product to this group and do so by default.
|
||||
# Non-members who can enter bug in this product
|
||||
# will be forced to restrict it.
|
||||
# Mandatory:Mandatory => Bug will be forced into this group regardless.
|
||||
# All other combinations are illegal.
|
||||
|
||||
use constant CONTROLMAPNA => 0;
|
||||
use constant CONTROLMAPSHOWN => 1;
|
||||
use constant CONTROLMAPDEFAULT => 2;
|
||||
use constant CONTROLMAPMANDATORY => 3;
|
||||
|
||||
# See Bugzilla::Auth for docs on AUTH_*, LOGIN_* and LOGOUT_*
|
||||
|
||||
use constant AUTH_OK => 0;
|
||||
use constant AUTH_NODATA => 1;
|
||||
use constant AUTH_ERROR => 2;
|
||||
use constant AUTH_LOGINFAILED => 3;
|
||||
use constant AUTH_DISABLED => 4;
|
||||
|
||||
use constant LOGIN_OPTIONAL => 0;
|
||||
use constant LOGIN_NORMAL => 1;
|
||||
use constant LOGIN_REQUIRED => 2;
|
||||
|
||||
use constant LOGOUT_ALL => 0;
|
||||
use constant LOGOUT_CURRENT => 1;
|
||||
use constant LOGOUT_KEEP_CURRENT => 2;
|
||||
|
||||
use constant contenttypes =>
|
||||
{
|
||||
"html" => "text/html" ,
|
||||
"rdf" => "application/xml" ,
|
||||
"xml" => "text/xml" ,
|
||||
"js" => "application/x-javascript" ,
|
||||
"csv" => "text/plain" ,
|
||||
"png" => "image/png" ,
|
||||
"ics" => "text/calendar" ,
|
||||
};
|
||||
|
||||
use constant GRANT_DIRECT => 0;
|
||||
use constant GRANT_DERIVED => 1;
|
||||
use constant GRANT_REGEXP => 2;
|
||||
|
||||
use constant GROUP_MEMBERSHIP => 0;
|
||||
use constant GROUP_BLESS => 1;
|
||||
use constant GROUP_VISIBLE => 2;
|
||||
|
||||
1;
|
||||
@@ -1,265 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
|
||||
package Bugzilla::DB;
|
||||
|
||||
use strict;
|
||||
|
||||
use DBI;
|
||||
|
||||
use base qw(Exporter);
|
||||
|
||||
%Bugzilla::DB::EXPORT_TAGS =
|
||||
(
|
||||
deprecated => [qw(SendSQL SqlQuote
|
||||
MoreSQLData FetchSQLData FetchOneColumn
|
||||
PushGlobalSQLState PopGlobalSQLState)
|
||||
],
|
||||
);
|
||||
Exporter::export_ok_tags('deprecated');
|
||||
|
||||
use Bugzilla::Config qw(:DEFAULT :db);
|
||||
use Bugzilla::Util;
|
||||
|
||||
# All this code is backwards compat fu. As such, its a bit ugly. Note the
|
||||
# circular dependancies on Bugzilla.pm
|
||||
# This is old cruft which will be removed, so theres not much use in
|
||||
# having a separate package for it, or otherwise trying to avoid the circular
|
||||
# dependancy
|
||||
|
||||
# XXX - mod_perl
|
||||
# These use |our| instead of |my| because they need to be cleared from
|
||||
# Bugzilla.pm. See bug 192531 for details.
|
||||
our $_current_sth;
|
||||
our @SQLStateStack = ();
|
||||
sub SendSQL {
|
||||
my ($str) = @_;
|
||||
|
||||
$_current_sth = Bugzilla->dbh->prepare($str);
|
||||
|
||||
$_current_sth->execute;
|
||||
|
||||
# This is really really ugly, but its what we get for not doing
|
||||
# error checking for 5 years. See bug 189446 and bug 192531
|
||||
$_current_sth->{RaiseError} = 0;
|
||||
}
|
||||
|
||||
# Its much much better to use bound params instead of this
|
||||
sub SqlQuote {
|
||||
my ($str) = @_;
|
||||
|
||||
# Backwards compat code
|
||||
return "''" if not defined $str;
|
||||
|
||||
my $res = Bugzilla->dbh->quote($str);
|
||||
|
||||
trick_taint($res);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
# XXX - mod_perl
|
||||
my $_fetchahead;
|
||||
sub MoreSQLData {
|
||||
return 1 if defined $_fetchahead;
|
||||
|
||||
if ($_fetchahead = $_current_sth->fetchrow_arrayref()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub FetchSQLData {
|
||||
if (defined $_fetchahead) {
|
||||
my @result = @$_fetchahead;
|
||||
undef $_fetchahead;
|
||||
return @result;
|
||||
}
|
||||
|
||||
return $_current_sth->fetchrow_array;
|
||||
}
|
||||
|
||||
sub FetchOneColumn {
|
||||
my @row = FetchSQLData();
|
||||
return $row[0];
|
||||
}
|
||||
|
||||
sub PushGlobalSQLState() {
|
||||
push @SQLStateStack, $_current_sth;
|
||||
push @SQLStateStack, $_fetchahead;
|
||||
}
|
||||
|
||||
sub PopGlobalSQLState() {
|
||||
die ("PopGlobalSQLState: stack underflow") if ( scalar(@SQLStateStack) < 1 );
|
||||
$_fetchahead = pop @SQLStateStack;
|
||||
$_current_sth = pop @SQLStateStack;
|
||||
}
|
||||
|
||||
# MODERN CODE BELOW
|
||||
|
||||
sub connect_shadow {
|
||||
die "Tried to connect to non-existent shadowdb" unless Param('shadowdb');
|
||||
|
||||
my $dsn = "DBI:mysql:host=" . Param("shadowdbhost") .
|
||||
";database=" . Param('shadowdb') . ";port=" . Param("shadowdbport");
|
||||
|
||||
$dsn .= ";mysql_socket=" . Param("shadowdbsock") if Param('shadowdbsock');
|
||||
|
||||
return _connect($dsn);
|
||||
}
|
||||
|
||||
sub connect_main {
|
||||
my $dsn = "DBI:mysql:host=$db_host;database=$db_name;port=$db_port";
|
||||
|
||||
$dsn .= ";mysql_socket=$db_sock" if $db_sock;
|
||||
|
||||
return _connect($dsn);
|
||||
}
|
||||
|
||||
sub _connect {
|
||||
my ($dsn) = @_;
|
||||
|
||||
# connect using our known info to the specified db
|
||||
# Apache::DBI will cache this when using mod_perl
|
||||
my $dbh = DBI->connect($dsn,
|
||||
'',
|
||||
'',
|
||||
{ RaiseError => 1,
|
||||
PrintError => 0,
|
||||
Username => $db_user,
|
||||
Password => $db_pass,
|
||||
ShowErrorStatement => 1,
|
||||
HandleError => \&_handle_error,
|
||||
FetchHashKeyName => 'NAME_lc',
|
||||
TaintIn => 1,
|
||||
});
|
||||
|
||||
return $dbh;
|
||||
}
|
||||
|
||||
sub _handle_error {
|
||||
require Carp;
|
||||
|
||||
$_[0] = Carp::longmess($_[0]);
|
||||
return 0; # Now let DBI handle raising the error
|
||||
}
|
||||
|
||||
my $cached_server_version;
|
||||
sub server_version {
|
||||
return $cached_server_version if defined($cached_server_version);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare('SELECT VERSION()');
|
||||
$sth->execute();
|
||||
($cached_server_version) = $sth->fetchrow_array();
|
||||
return $cached_server_version;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::DB - Database access routines, using L<DBI>
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $dbh = Bugzilla::DB->connect_main;
|
||||
my $shadow = Bugzilla::DB->connect_shadow;
|
||||
|
||||
SendSQL("SELECT COUNT(*) FROM bugs");
|
||||
my $cnt = FetchOneColumn();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This allows creation of a database handle to connect to the Bugzilla database.
|
||||
This should never be done directly; all users should use the L<Bugzilla> module
|
||||
to access the current C<dbh> instead.
|
||||
|
||||
Access to the old SendSQL-based database routines are also provided by
|
||||
importing the C<:deprecated> tag. These routines should not be used in new
|
||||
code.
|
||||
|
||||
=head1 CONNECTION
|
||||
|
||||
A new database handle to the required database can be created using this
|
||||
module. This is normally done by the L<Bugzilla> module, and so these routines
|
||||
should not be called from anywhere else.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<connect_main>
|
||||
|
||||
Connects to the main database, returning a new dbh.
|
||||
|
||||
=item C<connect_shadow>
|
||||
|
||||
Connects to the shadow database, returning a new dbh. This routine C<die>s if
|
||||
no shadow database is configured.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DEPRECATED ROUTINES
|
||||
|
||||
Several database routines are deprecated. They should not be used in new code,
|
||||
and so are not documented.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
SendSQL
|
||||
|
||||
=item *
|
||||
|
||||
SqlQuote
|
||||
|
||||
=item *
|
||||
|
||||
MoreSQLData
|
||||
|
||||
=item *
|
||||
|
||||
FetchSQLData
|
||||
|
||||
=item *
|
||||
|
||||
FetchOneColumn
|
||||
|
||||
=item *
|
||||
|
||||
PushGlobalSQLState
|
||||
|
||||
=item *
|
||||
|
||||
PopGlobalSQLState
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<DBI>
|
||||
|
||||
=cut
|
||||
@@ -1,190 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@acm.org>
|
||||
|
||||
package Bugzilla::Error;
|
||||
|
||||
use strict;
|
||||
use base qw(Exporter);
|
||||
|
||||
@Bugzilla::Error::EXPORT = qw(ThrowCodeError ThrowTemplateError ThrowUserError);
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Util;
|
||||
use Date::Format;
|
||||
|
||||
sub _throw_error {
|
||||
my ($name, $error, $vars, $unlock_tables) = @_;
|
||||
|
||||
$vars ||= {};
|
||||
|
||||
$vars->{error} = $error;
|
||||
|
||||
Bugzilla->dbh->do("UNLOCK TABLES") if $unlock_tables;
|
||||
|
||||
# If a writable data/errorlog exists, log error details there.
|
||||
if (-w "data/errorlog") {
|
||||
require Data::Dumper;
|
||||
my $mesg = "";
|
||||
for (1..75) { $mesg .= "-"; };
|
||||
$mesg .= "\n[$$] " . time2str("%D %H:%M:%S ", time());
|
||||
$mesg .= "$name $error ";
|
||||
$mesg .= "$ENV{REMOTE_ADDR} " if $ENV{REMOTE_ADDR};
|
||||
$mesg .= Bugzilla->user->login;
|
||||
$mesg .= "\n";
|
||||
my %params = Bugzilla->cgi->Vars;
|
||||
$Data::Dumper::Useqq = 1;
|
||||
for my $param (sort keys %params) {
|
||||
my $val = $params{$param};
|
||||
# obscure passwords
|
||||
$val = "*****" if $param =~ /password/i;
|
||||
# limit line length
|
||||
$val =~ s/^(.{512}).*$/$1\[CHOP\]/;
|
||||
$mesg .= "[$$] " . Data::Dumper->Dump([$val],["param($param)"]);
|
||||
}
|
||||
for my $var (sort keys %ENV) {
|
||||
my $val = $ENV{$var};
|
||||
$val = "*****" if $val =~ /password|http_pass/i;
|
||||
$mesg .= "[$$] " . Data::Dumper->Dump([$val],["env($var)"]);
|
||||
}
|
||||
open(ERRORLOGFID, ">>data/errorlog");
|
||||
print ERRORLOGFID "$mesg\n";
|
||||
close ERRORLOGFID;
|
||||
}
|
||||
|
||||
print Bugzilla->cgi->header();
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
$template->process($name, $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
sub ThrowUserError {
|
||||
_throw_error("global/user-error.html.tmpl", @_);
|
||||
}
|
||||
|
||||
sub ThrowCodeError {
|
||||
_throw_error("global/code-error.html.tmpl", @_);
|
||||
}
|
||||
|
||||
sub ThrowTemplateError {
|
||||
my ($template_err) = @_;
|
||||
|
||||
my $vars = {};
|
||||
|
||||
$vars->{'template_error_msg'} = $template_err;
|
||||
$vars->{'error'} = "template_error";
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
|
||||
# Try a template first; but if this one fails too, fall back
|
||||
# on plain old print statements.
|
||||
if (!$template->process("global/code-error.html.tmpl", $vars)) {
|
||||
my $maintainer = Param('maintainer');
|
||||
my $error = html_quote($vars->{'template_error_msg'});
|
||||
my $error2 = html_quote($template->error());
|
||||
print <<END;
|
||||
<tt>
|
||||
<p>
|
||||
Bugzilla has suffered an internal error. Please save this page and
|
||||
send it to $maintainer with details of what you were doing at the
|
||||
time this message appeared.
|
||||
</p>
|
||||
<script type="text/javascript"> <!--
|
||||
document.write("<p>URL: " + document.location + "</p>");
|
||||
// -->
|
||||
</script>
|
||||
<p>Template->process() failed twice.<br>
|
||||
First error: $error<br>
|
||||
Second error: $error2</p>
|
||||
</tt>
|
||||
END
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Error - Error handling utilities for Bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Error;
|
||||
|
||||
ThrowUserError("error_tag",
|
||||
{ foo => 'bar' });
|
||||
|
||||
# supplying "abort" to ensure tables are unlocked
|
||||
ThrowUserError("another_error_tag",
|
||||
{ foo => 'bar' }, 'abort');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Various places throughout the Bugzilla codebase need to report errors to the
|
||||
user. The C<Throw*Error> family of functions allow this to be done in a
|
||||
generic and localisable manner.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<ThrowUserError>
|
||||
|
||||
This function takes an error tag as the first argument, and an optional hashref
|
||||
of variables as a second argument. These are used by the
|
||||
I<global/user-error.html.tmpl> template to format the error, using the passed
|
||||
in variables as required.
|
||||
|
||||
An optional third argument may be supplied. If present, the error
|
||||
handling code will unlock the database tables: it is a Bugzilla standard
|
||||
to provide the string "abort" as the argument value. In the long term,
|
||||
this argument will go away, to be replaced by transactional C<rollback>
|
||||
calls. There is no timeframe for doing so, however.
|
||||
|
||||
=item C<ThrowCodeError>
|
||||
|
||||
This function is used when an internal check detects an error of some sort.
|
||||
This usually indicates a bug in Bugzilla, although it can occur if the user
|
||||
manually constructs urls without correct parameters.
|
||||
|
||||
This function's behaviour is similar to C<ThrowUserError>, except that the
|
||||
template used to display errors is I<global/code-error.html.tmpl>. In addition
|
||||
if the hashref used as the optional second argument contains a key I<variables>
|
||||
then the contents of the hashref (which is expected to be another hashref) will
|
||||
be displayed after the error message, as a debugging aid.
|
||||
|
||||
=item C<ThrowTemplateError>
|
||||
|
||||
This function should only be called if a C<template-<gt>process()> fails.
|
||||
It tries another template first, because often one template being
|
||||
broken or missing doesn't mean that they all are. But it falls back to
|
||||
a print statement as a last-ditch error.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla|Bugzilla>
|
||||
@@ -1,682 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Myk Melez <myk@mozilla.org>
|
||||
# Jouni Heikniemi <jouni@heikniemi.net>
|
||||
|
||||
################################################################################
|
||||
# Module Initialization
|
||||
################################################################################
|
||||
|
||||
# Make it harder for us to do dangerous things in Perl.
|
||||
use strict;
|
||||
|
||||
# This module implements bug and attachment flags.
|
||||
package Bugzilla::Flag;
|
||||
|
||||
use Bugzilla::FlagType;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Attachment;
|
||||
|
||||
use constant TABLES_ALREADY_LOCKED => 1;
|
||||
|
||||
# Note that this line doesn't actually import these variables for some reason,
|
||||
# so I have to use them as $::template and $::vars in the package code.
|
||||
use vars qw($template $vars);
|
||||
|
||||
# Note! This module requires that its caller have said "require CGI.pl"
|
||||
# to import relevant functions from that script and its companion globals.pl.
|
||||
|
||||
################################################################################
|
||||
# Global Variables
|
||||
################################################################################
|
||||
|
||||
# basic sets of columns and tables for getting flags from the database
|
||||
|
||||
my @base_columns =
|
||||
("is_active", "id", "type_id", "bug_id", "attach_id", "requestee_id",
|
||||
"setter_id", "status");
|
||||
|
||||
# Note: when adding tables to @base_tables, make sure to include the separator
|
||||
# (i.e. a comma or words like "LEFT OUTER JOIN") before the table name,
|
||||
# since tables take multiple separators based on the join type, and therefore
|
||||
# it is not possible to join them later using a single known separator.
|
||||
|
||||
my @base_tables = ("flags");
|
||||
|
||||
################################################################################
|
||||
# Searching/Retrieving Flags
|
||||
################################################################################
|
||||
|
||||
# !!! Implement a cache for this function!
|
||||
sub get {
|
||||
# Retrieves and returns a flag from the database.
|
||||
|
||||
my ($id) = @_;
|
||||
|
||||
my $select_clause = "SELECT " . join(", ", @base_columns);
|
||||
my $from_clause = "FROM " . join(" ", @base_tables);
|
||||
|
||||
# Execute the query, retrieve the result, and write it into a record.
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("$select_clause $from_clause WHERE flags.id = $id");
|
||||
my $flag = perlify_record(&::FetchSQLData());
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return $flag;
|
||||
}
|
||||
|
||||
sub match {
|
||||
# Queries the database for flags matching the given criteria
|
||||
# (specified as a hash of field names and their matching values)
|
||||
# and returns an array of matching records.
|
||||
|
||||
my ($criteria) = @_;
|
||||
|
||||
my $select_clause = "SELECT " . join(", ", @base_columns);
|
||||
my $from_clause = "FROM " . join(" ", @base_tables);
|
||||
|
||||
my @criteria = sqlify_criteria($criteria);
|
||||
|
||||
my $where_clause = "WHERE " . join(" AND ", @criteria);
|
||||
|
||||
# Execute the query, retrieve the results, and write them into records.
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("$select_clause $from_clause $where_clause");
|
||||
my @flags;
|
||||
while (&::MoreSQLData()) {
|
||||
my $flag = perlify_record(&::FetchSQLData());
|
||||
push(@flags, $flag);
|
||||
}
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return \@flags;
|
||||
}
|
||||
|
||||
sub count {
|
||||
# Queries the database for flags matching the given criteria
|
||||
# (specified as a hash of field names and their matching values)
|
||||
# and returns an array of matching records.
|
||||
|
||||
my ($criteria) = @_;
|
||||
|
||||
my @criteria = sqlify_criteria($criteria);
|
||||
|
||||
my $where_clause = "WHERE " . join(" AND ", @criteria);
|
||||
|
||||
# Execute the query, retrieve the result, and write it into a record.
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("SELECT COUNT(id) FROM flags $where_clause");
|
||||
my $count = &::FetchOneColumn();
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Creating and Modifying
|
||||
################################################################################
|
||||
|
||||
sub validate {
|
||||
# Validates fields containing flag modifications.
|
||||
|
||||
my ($data, $bug_id) = @_;
|
||||
|
||||
# Get a list of flags to validate. Uses the "map" function
|
||||
# to extract flag IDs from form field names by matching fields
|
||||
# whose name looks like "flag-nnn", where "nnn" is the ID,
|
||||
# and returning just the ID portion of matching field names.
|
||||
my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data);
|
||||
|
||||
foreach my $id (@ids)
|
||||
{
|
||||
my $status = $data->{"flag-$id"};
|
||||
|
||||
# Make sure the flag exists.
|
||||
my $flag = get($id);
|
||||
$flag || ThrowCodeError("flag_nonexistent", { id => $id });
|
||||
|
||||
# Note that the deletedness of the flag (is_active or not) is not
|
||||
# checked here; we do want to allow changes to deleted flags in
|
||||
# certain cases. Flag::modify() will revive the modified flags.
|
||||
# See bug 223878 for details.
|
||||
|
||||
# Make sure the user chose a valid status.
|
||||
grep($status eq $_, qw(X + - ?))
|
||||
|| ThrowCodeError("flag_status_invalid",
|
||||
{ id => $id, status => $status });
|
||||
|
||||
# Make sure the user didn't request the flag unless it's requestable.
|
||||
if ($status eq '?' && !$flag->{type}->{is_requestable}) {
|
||||
ThrowCodeError("flag_status_invalid",
|
||||
{ id => $id, status => $status });
|
||||
}
|
||||
|
||||
# Make sure the requestee is authorized to access the bug.
|
||||
# (and attachment, if this installation is using the "insider group"
|
||||
# feature and the attachment is marked private).
|
||||
if ($status eq '?'
|
||||
&& $flag->{type}->{is_requesteeble}
|
||||
&& trim($data->{"requestee-$id"}))
|
||||
{
|
||||
my $requestee_email = trim($data->{"requestee-$id"});
|
||||
if ($requestee_email ne $flag->{'requestee'}->{'email'}) {
|
||||
# We know the requestee exists because we ran
|
||||
# Bugzilla::User::match_field before getting here.
|
||||
my $requestee = Bugzilla::User->new_from_login($requestee_email);
|
||||
|
||||
# Throw an error if the user can't see the bug.
|
||||
if (!$requestee->can_see_bug($bug_id))
|
||||
{
|
||||
ThrowUserError("flag_requestee_unauthorized",
|
||||
{ flag_type => $flag->{'type'},
|
||||
requestee => $requestee,
|
||||
bug_id => $bug_id,
|
||||
attach_id =>
|
||||
$flag->{target}->{attachment}->{id} });
|
||||
}
|
||||
|
||||
# Throw an error if the target is a private attachment and
|
||||
# the requestee isn't in the group of insiders who can see it.
|
||||
if ($flag->{target}->{attachment}->{exists}
|
||||
&& $data->{'isprivate'}
|
||||
&& Param("insidergroup")
|
||||
&& !$requestee->in_group(Param("insidergroup")))
|
||||
{
|
||||
ThrowUserError("flag_requestee_unauthorized_attachment",
|
||||
{ flag_type => $flag->{'type'},
|
||||
requestee => $requestee,
|
||||
bug_id => $bug_id,
|
||||
attach_id =>
|
||||
$flag->{target}->{attachment}->{id} });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub process {
|
||||
# Processes changes to flags.
|
||||
|
||||
# The target is the bug or attachment this flag is about, the timestamp
|
||||
# is the date/time the bug was last touched (so that changes to the flag
|
||||
# can be stamped with the same date/time), the data is the form data
|
||||
# with flag fields that the user submitted, the old bug is the bug record
|
||||
# before the user made changes to it, and the new bug is the bug record
|
||||
# after the user made changes to it.
|
||||
|
||||
my ($target, $timestamp, $data, $oldbug, $newbug) = @_;
|
||||
|
||||
# Use the date/time we were given if possible (allowing calling code
|
||||
# to synchronize the comment's timestamp with those of other records).
|
||||
$timestamp = ($timestamp ? &::SqlQuote($timestamp) : "NOW()");
|
||||
|
||||
# Take a snapshot of flags before any changes.
|
||||
my $flags = match({ 'bug_id' => $target->{'bug'}->{'id'} ,
|
||||
'attach_id' => $target->{'attachment'}->{'id'} ,
|
||||
'is_active' => 1 });
|
||||
my @old_summaries;
|
||||
foreach my $flag (@$flags) {
|
||||
my $summary = $flag->{'type'}->{'name'} . $flag->{'status'};
|
||||
$summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'};
|
||||
push(@old_summaries, $summary);
|
||||
}
|
||||
|
||||
# Create new flags and update existing flags.
|
||||
my $new_flags = FormToNewFlags($target, $data);
|
||||
foreach my $flag (@$new_flags) { create($flag, $timestamp) }
|
||||
modify($data, $timestamp);
|
||||
|
||||
# In case the bug's product/component has changed, clear flags that are
|
||||
# no longer valid.
|
||||
&::SendSQL("
|
||||
SELECT flags.id
|
||||
FROM (flags INNER JOIN bugs ON flags.bug_id = bugs.bug_id)
|
||||
LEFT OUTER JOIN flaginclusions i
|
||||
ON (flags.type_id = i.type_id
|
||||
AND (bugs.product_id = i.product_id OR i.product_id IS NULL)
|
||||
AND (bugs.component_id = i.component_id OR i.component_id IS NULL))
|
||||
WHERE bugs.bug_id = $target->{'bug'}->{'id'}
|
||||
AND flags.is_active = 1
|
||||
AND i.type_id IS NULL
|
||||
");
|
||||
clear(&::FetchOneColumn()) while &::MoreSQLData();
|
||||
&::SendSQL("
|
||||
SELECT flags.id
|
||||
FROM flags, bugs, flagexclusions e
|
||||
WHERE bugs.bug_id = $target->{'bug'}->{'id'}
|
||||
AND flags.bug_id = bugs.bug_id
|
||||
AND flags.type_id = e.type_id
|
||||
AND flags.is_active = 1
|
||||
AND (bugs.product_id = e.product_id OR e.product_id IS NULL)
|
||||
AND (bugs.component_id = e.component_id OR e.component_id IS NULL)
|
||||
");
|
||||
clear(&::FetchOneColumn()) while &::MoreSQLData();
|
||||
|
||||
# Take a snapshot of flags after changes.
|
||||
$flags = match({ 'bug_id' => $target->{'bug'}->{'id'} ,
|
||||
'attach_id' => $target->{'attachment'}->{'id'} ,
|
||||
'is_active' => 1 });
|
||||
my @new_summaries;
|
||||
foreach my $flag (@$flags) {
|
||||
my $summary = $flag->{'type'}->{'name'} . $flag->{'status'};
|
||||
$summary .= "(" . $flag->{'requestee'}->login . ")" if $flag->{'requestee'};
|
||||
push(@new_summaries, $summary);
|
||||
}
|
||||
|
||||
my $old_summaries = join(", ", @old_summaries);
|
||||
my $new_summaries = join(", ", @new_summaries);
|
||||
my ($removed, $added) = &::DiffStrings($old_summaries, $new_summaries);
|
||||
if ($removed ne $added) {
|
||||
my $sql_removed = &::SqlQuote($removed);
|
||||
my $sql_added = &::SqlQuote($added);
|
||||
my $field_id = &::GetFieldID('flagtypes.name');
|
||||
my $attach_id = $target->{'attachment'}->{'id'} || 'NULL';
|
||||
&::SendSQL("INSERT INTO bugs_activity (bug_id, attach_id, who, " .
|
||||
"bug_when, fieldid, removed, added) VALUES " .
|
||||
"($target->{'bug'}->{'id'}, $attach_id, $::userid, " .
|
||||
"$timestamp, $field_id, $sql_removed, $sql_added)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub create {
|
||||
# Creates a flag record in the database.
|
||||
|
||||
my ($flag, $timestamp) = @_;
|
||||
|
||||
# Determine the ID for the flag record by retrieving the last ID used
|
||||
# and incrementing it.
|
||||
&::SendSQL("SELECT MAX(id) FROM flags");
|
||||
$flag->{'id'} = (&::FetchOneColumn() || 0) + 1;
|
||||
|
||||
# Insert a record for the flag into the flags table.
|
||||
my $attach_id = $flag->{'target'}->{'attachment'}->{'id'} || "NULL";
|
||||
my $requestee_id = $flag->{'requestee'} ? $flag->{'requestee'}->id : "NULL";
|
||||
&::SendSQL("INSERT INTO flags (id, type_id,
|
||||
bug_id, attach_id,
|
||||
requestee_id, setter_id, status,
|
||||
creation_date, modification_date)
|
||||
VALUES ($flag->{'id'},
|
||||
$flag->{'type'}->{'id'},
|
||||
$flag->{'target'}->{'bug'}->{'id'},
|
||||
$attach_id,
|
||||
$requestee_id,
|
||||
" . $flag->{'setter'}->id . ",
|
||||
'$flag->{'status'}',
|
||||
$timestamp,
|
||||
$timestamp)");
|
||||
|
||||
# Send an email notifying the relevant parties about the flag creation.
|
||||
if (($flag->{'requestee'}
|
||||
&& $flag->{'requestee'}->email_prefs->{'FlagRequestee'})
|
||||
|| $flag->{'type'}->{'cc_list'})
|
||||
{
|
||||
notify($flag, "request/email.txt.tmpl");
|
||||
}
|
||||
}
|
||||
|
||||
sub migrate {
|
||||
# Moves a flag from one attachment to another. Useful for migrating
|
||||
# a flag from an obsolete attachment to the attachment that obsoleted it.
|
||||
|
||||
my ($old_attach_id, $new_attach_id) = @_;
|
||||
|
||||
# Update the record in the flags table to point to the new attachment.
|
||||
&::SendSQL("UPDATE flags " .
|
||||
"SET attach_id = $new_attach_id , " .
|
||||
" modification_date = NOW() " .
|
||||
"WHERE attach_id = $old_attach_id");
|
||||
}
|
||||
|
||||
sub modify {
|
||||
# Modifies flags in the database when a user changes them.
|
||||
# Note that modified flags are always set active (is_active = 1) -
|
||||
# this will revive deleted flags that get changed through
|
||||
# attachment.cgi midairs. See bug 223878 for details.
|
||||
|
||||
my ($data, $timestamp) = @_;
|
||||
|
||||
# Use the date/time we were given if possible (allowing calling code
|
||||
# to synchronize the comment's timestamp with those of other records).
|
||||
$timestamp = ($timestamp ? &::SqlQuote($timestamp) : "NOW()");
|
||||
|
||||
# Extract a list of flags from the form data.
|
||||
my @ids = map(/^flag-(\d+)$/ ? $1 : (), keys %$data);
|
||||
|
||||
# Loop over flags and update their record in the database if necessary.
|
||||
# Two kinds of changes can happen to a flag: it can be set to a different
|
||||
# state, and someone else can be asked to set it. We take care of both
|
||||
# those changes.
|
||||
my @flags;
|
||||
foreach my $id (@ids) {
|
||||
my $flag = get($id);
|
||||
|
||||
my $status = $data->{"flag-$id"};
|
||||
my $requestee_email = trim($data->{"requestee-$id"});
|
||||
|
||||
|
||||
# Ignore flags the user didn't change. There are two components here:
|
||||
# either the status changes (trivial) or the requestee changes.
|
||||
# Change of either field will cause full update of the flag.
|
||||
|
||||
my $status_changed = ($status ne $flag->{'status'});
|
||||
|
||||
# Requestee is considered changed, if all of the following apply:
|
||||
# 1. Flag status is '?' (requested)
|
||||
# 2. Flag can have a requestee
|
||||
# 3. The requestee specified on the form is different from the
|
||||
# requestee specified in the db.
|
||||
|
||||
my $old_requestee =
|
||||
$flag->{'requestee'} ? $flag->{'requestee'}->login : '';
|
||||
|
||||
my $requestee_changed =
|
||||
($status eq "?" &&
|
||||
$flag->{'type'}->{'is_requesteeble'} &&
|
||||
$old_requestee ne $requestee_email);
|
||||
|
||||
next unless ($status_changed || $requestee_changed);
|
||||
|
||||
|
||||
# Since the status is validated, we know it's safe, but it's still
|
||||
# tainted, so we have to detaint it before using it in a query.
|
||||
&::trick_taint($status);
|
||||
|
||||
if ($status eq '+' || $status eq '-') {
|
||||
&::SendSQL("UPDATE flags
|
||||
SET setter_id = $::userid ,
|
||||
requestee_id = NULL ,
|
||||
status = '$status' ,
|
||||
modification_date = $timestamp ,
|
||||
is_active = 1
|
||||
WHERE id = $flag->{'id'}");
|
||||
|
||||
# Send an email notifying the relevant parties about the fulfillment.
|
||||
if ($flag->{'setter'}->email_prefs->{'FlagRequester'}
|
||||
|| $flag->{'type'}->{'cc_list'})
|
||||
{
|
||||
$flag->{'status'} = $status;
|
||||
notify($flag, "request/email.txt.tmpl");
|
||||
}
|
||||
}
|
||||
elsif ($status eq '?') {
|
||||
# Get the requestee, if any.
|
||||
my $requestee_id = "NULL";
|
||||
if ($requestee_email) {
|
||||
$requestee_id = &::DBname_to_id($requestee_email);
|
||||
$flag->{'requestee'} = new Bugzilla::User($requestee_id);
|
||||
}
|
||||
|
||||
# Update the database with the changes.
|
||||
&::SendSQL("UPDATE flags
|
||||
SET setter_id = $::userid ,
|
||||
requestee_id = $requestee_id ,
|
||||
status = '$status' ,
|
||||
modification_date = $timestamp ,
|
||||
is_active = 1
|
||||
WHERE id = $flag->{'id'}");
|
||||
|
||||
# Send an email notifying the relevant parties about the request.
|
||||
if ($flag->{'requestee'}
|
||||
&& ($flag->{'requestee'}->email_prefs->{'FlagRequestee'}
|
||||
|| $flag->{'type'}->{'cc_list'}))
|
||||
{
|
||||
notify($flag, "request/email.txt.tmpl");
|
||||
}
|
||||
}
|
||||
# The user unset the flag; set is_active = 0
|
||||
elsif ($status eq 'X') {
|
||||
clear($flag->{'id'});
|
||||
}
|
||||
|
||||
push(@flags, $flag);
|
||||
}
|
||||
|
||||
return \@flags;
|
||||
}
|
||||
|
||||
sub clear {
|
||||
my ($id) = @_;
|
||||
|
||||
my $flag = get($id);
|
||||
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("UPDATE flags SET is_active = 0 WHERE id = $id");
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
$flag->{'exists'} = 0;
|
||||
# Set the flag's status to "cleared" so the email template
|
||||
# knows why email is being sent about the request.
|
||||
$flag->{'status'} = "X";
|
||||
|
||||
notify($flag, "request/email.txt.tmpl") if $flag->{'requestee'};
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
# Utility Functions
|
||||
################################################################################
|
||||
|
||||
sub FormToNewFlags {
|
||||
my ($target, $data) = @_;
|
||||
|
||||
# Get information about the setter to add to each flag.
|
||||
# Uses a conditional to suppress Perl's "used only once" warnings.
|
||||
my $setter = new Bugzilla::User($::userid);
|
||||
|
||||
# Extract a list of flag type IDs from field names.
|
||||
my @type_ids = map(/^flag_type-(\d+)$/ ? $1 : (), keys %$data);
|
||||
@type_ids = grep($data->{"flag_type-$_"} ne 'X', @type_ids);
|
||||
|
||||
# Process the form data and create an array of flag objects.
|
||||
my @flags;
|
||||
foreach my $type_id (@type_ids) {
|
||||
my $status = $data->{"flag_type-$type_id"};
|
||||
&::trick_taint($status);
|
||||
|
||||
# Create the flag record and populate it with data from the form.
|
||||
my $flag = {
|
||||
type => Bugzilla::FlagType::get($type_id) ,
|
||||
target => $target ,
|
||||
setter => $setter ,
|
||||
status => $status
|
||||
};
|
||||
|
||||
if ($status eq "?") {
|
||||
my $requestee = $data->{"requestee_type-$type_id"};
|
||||
if ($requestee) {
|
||||
my $requestee_id = &::DBname_to_id($requestee);
|
||||
$flag->{'requestee'} = new Bugzilla::User($requestee_id);
|
||||
}
|
||||
}
|
||||
|
||||
# Add the flag to the array of flags.
|
||||
push(@flags, $flag);
|
||||
}
|
||||
|
||||
# Return the list of flags.
|
||||
return \@flags;
|
||||
}
|
||||
|
||||
# Ideally, we'd use Bug.pm, but it's way too heavyweight, and it can't be
|
||||
# made lighter without totally rewriting it, so we'll use this function
|
||||
# until that one gets rewritten.
|
||||
sub GetBug {
|
||||
# Returns a hash of information about a target bug.
|
||||
my ($id) = @_;
|
||||
|
||||
# Save the currently running query (if any) so we do not overwrite it.
|
||||
&::PushGlobalSQLState();
|
||||
|
||||
&::SendSQL("SELECT 1, short_desc, product_id, component_id,
|
||||
COUNT(bug_group_map.group_id)
|
||||
FROM bugs LEFT JOIN bug_group_map
|
||||
ON (bugs.bug_id = bug_group_map.bug_id)
|
||||
WHERE bugs.bug_id = $id
|
||||
GROUP BY bugs.bug_id");
|
||||
|
||||
my $bug = { 'id' => $id };
|
||||
|
||||
($bug->{'exists'}, $bug->{'summary'}, $bug->{'product_id'},
|
||||
$bug->{'component_id'}, $bug->{'restricted'}) = &::FetchSQLData();
|
||||
|
||||
# Restore the previously running query (if any).
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return $bug;
|
||||
}
|
||||
|
||||
sub GetTarget {
|
||||
my ($bug_id, $attach_id) = @_;
|
||||
|
||||
# Create an object representing the target bug/attachment.
|
||||
my $target = { 'exists' => 0 };
|
||||
|
||||
if ($attach_id) {
|
||||
$target->{'attachment'} = new Bugzilla::Attachment($attach_id);
|
||||
if ($bug_id) {
|
||||
# Make sure the bug and attachment IDs correspond to each other
|
||||
# (i.e. this is the bug to which this attachment is attached).
|
||||
$bug_id == $target->{'attachment'}->{'bug_id'}
|
||||
|| return { 'exists' => 0 };
|
||||
}
|
||||
$target->{'bug'} = GetBug($target->{'attachment'}->{'bug_id'});
|
||||
$target->{'exists'} = $target->{'attachment'}->{'exists'};
|
||||
$target->{'type'} = "attachment";
|
||||
}
|
||||
elsif ($bug_id) {
|
||||
$target->{'bug'} = GetBug($bug_id);
|
||||
$target->{'exists'} = $target->{'bug'}->{'exists'};
|
||||
$target->{'type'} = "bug";
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
|
||||
sub notify {
|
||||
# Sends an email notification about a flag being created or fulfilled.
|
||||
|
||||
my ($flag, $template_file) = @_;
|
||||
|
||||
# If the target bug is restricted to one or more groups, then we need
|
||||
# to make sure we don't send email about it to unauthorized users
|
||||
# on the request type's CC: list, so we have to trawl the list for users
|
||||
# not in those groups or email addresses that don't have an account.
|
||||
if ($flag->{'target'}->{'bug'}->{'restricted'}
|
||||
|| $flag->{'target'}->{'attachment'}->{'isprivate'})
|
||||
{
|
||||
my @new_cc_list;
|
||||
foreach my $cc (split(/[, ]+/, $flag->{'type'}->{'cc_list'})) {
|
||||
my $ccuser = Bugzilla::User->new_from_login($cc,
|
||||
TABLES_ALREADY_LOCKED)
|
||||
|| next;
|
||||
|
||||
next if $flag->{'target'}->{'bug'}->{'restricted'}
|
||||
&& !$ccuser->can_see_bug($flag->{'target'}->{'bug'}->{'id'});
|
||||
next if $flag->{'target'}->{'attachment'}->{'isprivate'}
|
||||
&& Param("insidergroup")
|
||||
&& !$ccuser->in_group(Param("insidergroup"));
|
||||
push(@new_cc_list, $cc.Param('emailsuffix'));
|
||||
}
|
||||
$flag->{'type'}->{'cc_list'} = join(", ", @new_cc_list);
|
||||
}
|
||||
|
||||
$flag->{'requestee'}->{'email'} .= Param('emailsuffix');
|
||||
$flag->{'setter'}->{'email'} .= Param('emailsuffix');
|
||||
$::vars->{'flag'} = $flag;
|
||||
|
||||
my $message;
|
||||
my $rv =
|
||||
$::template->process($template_file, $::vars, \$message);
|
||||
if (!$rv) {
|
||||
Bugzilla->cgi->header();
|
||||
ThrowTemplateError($::template->error());
|
||||
}
|
||||
|
||||
my $delivery_mode = Param("sendmailnow") ? "" : "-ODeliveryMode=deferred";
|
||||
open(SENDMAIL, "|/usr/lib/sendmail $delivery_mode -t -i")
|
||||
|| die "Can't open sendmail";
|
||||
print SENDMAIL $message;
|
||||
close(SENDMAIL);
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Private Functions
|
||||
################################################################################
|
||||
|
||||
sub sqlify_criteria {
|
||||
# Converts a hash of criteria into a list of SQL criteria.
|
||||
|
||||
# a reference to a hash containing the criteria (field => value)
|
||||
my ($criteria) = @_;
|
||||
|
||||
# the generated list of SQL criteria; "1=1" is a clever way of making sure
|
||||
# there's something in the list so calling code doesn't have to check list
|
||||
# size before building a WHERE clause out of it
|
||||
my @criteria = ("1=1");
|
||||
|
||||
# If the caller specified only bug or attachment flags,
|
||||
# limit the query to those kinds of flags.
|
||||
if (defined($criteria->{'target_type'})) {
|
||||
if ($criteria->{'target_type'} eq 'bug') { push(@criteria, "attach_id IS NULL") }
|
||||
elsif ($criteria->{'target_type'} eq 'attachment') { push(@criteria, "attach_id IS NOT NULL") }
|
||||
}
|
||||
|
||||
# Go through each criterion from the calling code and add it to the query.
|
||||
foreach my $field (keys %$criteria) {
|
||||
my $value = $criteria->{$field};
|
||||
next unless defined($value);
|
||||
if ($field eq 'type_id') { push(@criteria, "type_id = $value") }
|
||||
elsif ($field eq 'bug_id') { push(@criteria, "bug_id = $value") }
|
||||
elsif ($field eq 'attach_id') { push(@criteria, "attach_id = $value") }
|
||||
elsif ($field eq 'requestee_id') { push(@criteria, "requestee_id = $value") }
|
||||
elsif ($field eq 'setter_id') { push(@criteria, "setter_id = $value") }
|
||||
elsif ($field eq 'status') { push(@criteria, "status = '$value'") }
|
||||
elsif ($field eq 'is_active') { push(@criteria, "is_active = $value") }
|
||||
}
|
||||
|
||||
return @criteria;
|
||||
}
|
||||
|
||||
sub perlify_record {
|
||||
# Converts a row from the database into a Perl record.
|
||||
my ($exists, $id, $type_id, $bug_id, $attach_id,
|
||||
$requestee_id, $setter_id, $status) = @_;
|
||||
|
||||
return undef unless defined($exists);
|
||||
|
||||
my $flag =
|
||||
{
|
||||
exists => $exists ,
|
||||
id => $id ,
|
||||
type => Bugzilla::FlagType::get($type_id) ,
|
||||
target => GetTarget($bug_id, $attach_id) ,
|
||||
requestee => $requestee_id ? new Bugzilla::User($requestee_id) : undef,
|
||||
setter => new Bugzilla::User($setter_id) ,
|
||||
status => $status ,
|
||||
};
|
||||
|
||||
return $flag;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,376 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Myk Melez <myk@mozilla.org>
|
||||
|
||||
################################################################################
|
||||
# Module Initialization
|
||||
################################################################################
|
||||
|
||||
# Make it harder for us to do dangerous things in Perl.
|
||||
use strict;
|
||||
|
||||
# This module implements flag types for the flag tracker.
|
||||
package Bugzilla::FlagType;
|
||||
|
||||
# Use Bugzilla's User module which contains utilities for handling users.
|
||||
use Bugzilla::User;
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Config;
|
||||
|
||||
# Note! This module requires that its caller have said "require CGI.pl"
|
||||
# to import relevant functions from that script and its companion globals.pl.
|
||||
|
||||
################################################################################
|
||||
# Global Variables
|
||||
################################################################################
|
||||
|
||||
# basic sets of columns and tables for getting flag types from the database
|
||||
|
||||
my @base_columns =
|
||||
("1", "flagtypes.id", "flagtypes.name", "flagtypes.description",
|
||||
"flagtypes.cc_list", "flagtypes.target_type", "flagtypes.sortkey",
|
||||
"flagtypes.is_active", "flagtypes.is_requestable",
|
||||
"flagtypes.is_requesteeble", "flagtypes.is_multiplicable");
|
||||
|
||||
# Note: when adding tables to @base_tables, make sure to include the separator
|
||||
# (i.e. a comma or words like "LEFT OUTER JOIN") before the table name,
|
||||
# since tables take multiple separators based on the join type, and therefore
|
||||
# it is not possible to join them later using a single known separator.
|
||||
|
||||
my @base_tables = ("flagtypes");
|
||||
|
||||
################################################################################
|
||||
# Public Functions
|
||||
################################################################################
|
||||
|
||||
sub get {
|
||||
# Returns a hash of information about a flag type.
|
||||
|
||||
my ($id) = @_;
|
||||
|
||||
my $select_clause = "SELECT " . join(", ", @base_columns);
|
||||
my $from_clause = "FROM " . join(" ", @base_tables);
|
||||
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("$select_clause $from_clause WHERE flagtypes.id = $id");
|
||||
my @data = &::FetchSQLData();
|
||||
my $type = perlify_record(@data);
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
sub get_inclusions {
|
||||
my ($id) = @_;
|
||||
return get_clusions($id, "in");
|
||||
}
|
||||
|
||||
sub get_exclusions {
|
||||
my ($id) = @_;
|
||||
return get_clusions($id, "ex");
|
||||
}
|
||||
|
||||
sub get_clusions {
|
||||
my ($id, $type) = @_;
|
||||
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL("SELECT products.name, components.name " .
|
||||
"FROM flagtypes, flag${type}clusions " .
|
||||
"LEFT OUTER JOIN products ON flag${type}clusions.product_id = products.id " .
|
||||
"LEFT OUTER JOIN components ON flag${type}clusions.component_id = components.id " .
|
||||
"WHERE flagtypes.id = $id AND flag${type}clusions.type_id = flagtypes.id");
|
||||
my @clusions = ();
|
||||
while (&::MoreSQLData()) {
|
||||
my ($product, $component) = &::FetchSQLData();
|
||||
$product ||= "Any";
|
||||
$component ||= "Any";
|
||||
push(@clusions, "$product:$component");
|
||||
}
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return \@clusions;
|
||||
}
|
||||
|
||||
sub match {
|
||||
# Queries the database for flag types matching the given criteria
|
||||
# and returns the set of matching types.
|
||||
|
||||
my ($criteria, $include_count) = @_;
|
||||
|
||||
my @tables = @base_tables;
|
||||
my @columns = @base_columns;
|
||||
my $having = "";
|
||||
|
||||
# Include a count of the number of flags per type if requested.
|
||||
if ($include_count) {
|
||||
push(@columns, "COUNT(flags.id)");
|
||||
push(@tables, "LEFT OUTER JOIN flags ON flagtypes.id = flags.type_id");
|
||||
}
|
||||
|
||||
# Generate the SQL WHERE criteria.
|
||||
my @criteria = sqlify_criteria($criteria, \@tables, \@columns, \$having);
|
||||
|
||||
# Build the query, grouping the types if we are counting flags.
|
||||
my $select_clause = "SELECT " . join(", ", @columns);
|
||||
my $from_clause = "FROM " . join(" ", @tables);
|
||||
my $where_clause = "WHERE " . join(" AND ", @criteria);
|
||||
|
||||
my $query = "$select_clause $from_clause $where_clause";
|
||||
$query .= " GROUP BY flagtypes.id " if ($include_count || $having ne "");
|
||||
$query .= " HAVING $having " if $having ne "";
|
||||
$query .= " ORDER BY flagtypes.sortkey, flagtypes.name";
|
||||
|
||||
# Execute the query and retrieve the results.
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL($query);
|
||||
my @types;
|
||||
while (&::MoreSQLData()) {
|
||||
my @data = &::FetchSQLData();
|
||||
my $type = perlify_record(@data);
|
||||
push(@types, $type);
|
||||
}
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return \@types;
|
||||
}
|
||||
|
||||
sub count {
|
||||
# Returns the total number of flag types matching the given criteria.
|
||||
|
||||
my ($criteria) = @_;
|
||||
|
||||
# Generate query components.
|
||||
my @tables = @base_tables;
|
||||
my @columns = ("COUNT(flagtypes.id)");
|
||||
my $having = "";
|
||||
my @criteria = sqlify_criteria($criteria, \@tables, \@columns, \$having);
|
||||
|
||||
# Build the query.
|
||||
my $select_clause = "SELECT " . join(", ", @columns);
|
||||
my $from_clause = "FROM " . join(" ", @tables);
|
||||
my $where_clause = "WHERE " . join(" AND ", @criteria);
|
||||
my $query = "$select_clause $from_clause $where_clause";
|
||||
$query .= " GROUP BY flagtypes.id HAVING $having " if $having ne "";
|
||||
|
||||
# Execute the query and get the results.
|
||||
&::PushGlobalSQLState();
|
||||
&::SendSQL($query);
|
||||
my $count = &::FetchOneColumn();
|
||||
&::PopGlobalSQLState();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub validate {
|
||||
my ($data, $bug_id, $attach_id) = @_;
|
||||
|
||||
# Get a list of flag types to validate. Uses the "map" function
|
||||
# to extract flag type IDs from form field names by matching columns
|
||||
# whose name looks like "flag_type-nnn", where "nnn" is the ID,
|
||||
# and returning just the ID portion of matching field names.
|
||||
my @ids = map(/^flag_type-(\d+)$/ ? $1 : (), keys %$data);
|
||||
|
||||
foreach my $id (@ids)
|
||||
{
|
||||
my $status = $data->{"flag_type-$id"};
|
||||
|
||||
# Don't bother validating types the user didn't touch.
|
||||
next if $status eq "X";
|
||||
|
||||
# Make sure the flag type exists.
|
||||
my $flag_type = get($id);
|
||||
$flag_type
|
||||
|| ThrowCodeError("flag_type_nonexistent", { id => $id });
|
||||
|
||||
# Make sure the value of the field is a valid status.
|
||||
grep($status eq $_, qw(X + - ?))
|
||||
|| ThrowCodeError("flag_status_invalid",
|
||||
{ id => $id , status => $status });
|
||||
|
||||
# Make sure the user didn't request the flag unless it's requestable.
|
||||
if ($status eq '?' && !$flag_type->{is_requestable}) {
|
||||
ThrowCodeError("flag_status_invalid",
|
||||
{ id => $id , status => $status });
|
||||
}
|
||||
|
||||
# Make sure the requestee is authorized to access the bug
|
||||
# (and attachment, if this installation is using the "insider group"
|
||||
# feature and the attachment is marked private).
|
||||
if ($status eq '?'
|
||||
&& $flag_type->{is_requesteeble}
|
||||
&& trim($data->{"requestee_type-$id"}))
|
||||
{
|
||||
my $requestee_email = trim($data->{"requestee_type-$id"});
|
||||
|
||||
# We know the requestee exists because we ran
|
||||
# Bugzilla::User::match_field before getting here.
|
||||
my $requestee = Bugzilla::User->new_from_login($requestee_email);
|
||||
|
||||
# Throw an error if the user can't see the bug.
|
||||
if (!$requestee->can_see_bug($bug_id))
|
||||
{
|
||||
ThrowUserError("flag_requestee_unauthorized",
|
||||
{ flag_type => $flag_type,
|
||||
requestee => $requestee,
|
||||
bug_id => $bug_id,
|
||||
attach_id => $attach_id });
|
||||
}
|
||||
|
||||
# Throw an error if the target is a private attachment and
|
||||
# the requestee isn't in the group of insiders who can see it.
|
||||
if ($attach_id
|
||||
&& Param("insidergroup")
|
||||
&& $data->{'isprivate'}
|
||||
&& !$requestee->in_group(Param("insidergroup")))
|
||||
{
|
||||
ThrowUserError("flag_requestee_unauthorized_attachment",
|
||||
{ flag_type => $flag_type,
|
||||
requestee => $requestee,
|
||||
bug_id => $bug_id,
|
||||
attach_id => $attach_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub normalize {
|
||||
# Given a list of flag types, checks its flags to make sure they should
|
||||
# still exist after a change to the inclusions/exclusions lists.
|
||||
|
||||
# A list of IDs of flag types to normalize.
|
||||
my (@ids) = @_;
|
||||
|
||||
my $ids = join(", ", @ids);
|
||||
|
||||
# Check for flags whose product/component is no longer included.
|
||||
&::SendSQL("
|
||||
SELECT flags.id
|
||||
FROM (flags INNER JOIN bugs ON flags.bug_id = bugs.bug_id)
|
||||
LEFT OUTER JOIN flaginclusions AS i
|
||||
ON (flags.type_id = i.type_id
|
||||
AND (bugs.product_id = i.product_id OR i.product_id IS NULL)
|
||||
AND (bugs.component_id = i.component_id OR i.component_id IS NULL))
|
||||
WHERE flags.type_id IN ($ids)
|
||||
AND flags.is_active = 1
|
||||
AND i.type_id IS NULL
|
||||
");
|
||||
Bugzilla::Flag::clear(&::FetchOneColumn()) while &::MoreSQLData();
|
||||
|
||||
&::SendSQL("
|
||||
SELECT flags.id
|
||||
FROM flags, bugs, flagexclusions AS e
|
||||
WHERE flags.type_id IN ($ids)
|
||||
AND flags.bug_id = bugs.bug_id
|
||||
AND flags.type_id = e.type_id
|
||||
AND flags.is_active = 1
|
||||
AND (bugs.product_id = e.product_id OR e.product_id IS NULL)
|
||||
AND (bugs.component_id = e.component_id OR e.component_id IS NULL)
|
||||
");
|
||||
Bugzilla::Flag::clear(&::FetchOneColumn()) while &::MoreSQLData();
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Private Functions
|
||||
################################################################################
|
||||
|
||||
sub sqlify_criteria {
|
||||
# Converts a hash of criteria into a list of SQL criteria.
|
||||
# $criteria is a reference to the criteria (field => value),
|
||||
# $tables is a reference to an array of tables being accessed
|
||||
# by the query, $columns is a reference to an array of columns
|
||||
# being returned by the query, and $having is a reference to
|
||||
# a criterion to put into the HAVING clause.
|
||||
my ($criteria, $tables, $columns, $having) = @_;
|
||||
|
||||
# the generated list of SQL criteria; "1=1" is a clever way of making sure
|
||||
# there's something in the list so calling code doesn't have to check list
|
||||
# size before building a WHERE clause out of it
|
||||
my @criteria = ("1=1");
|
||||
|
||||
if ($criteria->{name}) {
|
||||
push(@criteria, "flagtypes.name = " . &::SqlQuote($criteria->{name}));
|
||||
}
|
||||
if ($criteria->{target_type}) {
|
||||
# The target type is stored in the database as a one-character string
|
||||
# ("a" for attachment and "b" for bug), but this function takes complete
|
||||
# names ("attachment" and "bug") for clarity, so we must convert them.
|
||||
my $target_type = &::SqlQuote(substr($criteria->{target_type}, 0, 1));
|
||||
push(@criteria, "flagtypes.target_type = $target_type");
|
||||
}
|
||||
if (exists($criteria->{is_active})) {
|
||||
my $is_active = $criteria->{is_active} ? "1" : "0";
|
||||
push(@criteria, "flagtypes.is_active = $is_active");
|
||||
}
|
||||
if ($criteria->{product_id} && $criteria->{'component_id'}) {
|
||||
my $product_id = $criteria->{product_id};
|
||||
my $component_id = $criteria->{component_id};
|
||||
|
||||
# Add inclusions to the query, which simply involves joining the table
|
||||
# by flag type ID and target product/component.
|
||||
push(@$tables, "INNER JOIN flaginclusions ON " .
|
||||
"flagtypes.id = flaginclusions.type_id");
|
||||
push(@criteria, "(flaginclusions.product_id = $product_id " .
|
||||
" OR flaginclusions.product_id IS NULL)");
|
||||
push(@criteria, "(flaginclusions.component_id = $component_id " .
|
||||
" OR flaginclusions.component_id IS NULL)");
|
||||
|
||||
# Add exclusions to the query, which is more complicated. First of all,
|
||||
# we do a LEFT JOIN so we don't miss flag types with no exclusions.
|
||||
# Then, as with inclusions, we join on flag type ID and target product/
|
||||
# component. However, since we want flag types that *aren't* on the
|
||||
# exclusions list, we count the number of exclusions records returned
|
||||
# and use a HAVING clause to weed out types with one or more exclusions.
|
||||
my $join_clause = "flagtypes.id = flagexclusions.type_id " .
|
||||
"AND (flagexclusions.product_id = $product_id " .
|
||||
"OR flagexclusions.product_id IS NULL) " .
|
||||
"AND (flagexclusions.component_id = $component_id " .
|
||||
"OR flagexclusions.component_id IS NULL)";
|
||||
push(@$tables, "LEFT JOIN flagexclusions ON ($join_clause)");
|
||||
push(@$columns, "COUNT(flagexclusions.type_id) AS num_exclusions");
|
||||
$$having = "num_exclusions = 0";
|
||||
}
|
||||
|
||||
return @criteria;
|
||||
}
|
||||
|
||||
sub perlify_record {
|
||||
# Converts data retrieved from the database into a Perl record.
|
||||
|
||||
my $type = {};
|
||||
|
||||
$type->{'exists'} = $_[0];
|
||||
$type->{'id'} = $_[1];
|
||||
$type->{'name'} = $_[2];
|
||||
$type->{'description'} = $_[3];
|
||||
$type->{'cc_list'} = $_[4];
|
||||
$type->{'target_type'} = $_[5] eq "b" ? "bug" : "attachment";
|
||||
$type->{'sortkey'} = $_[6];
|
||||
$type->{'is_active'} = $_[7];
|
||||
$type->{'is_requestable'} = $_[8];
|
||||
$type->{'is_requesteeble'} = $_[9];
|
||||
$type->{'is_multiplicable'} = $_[10];
|
||||
$type->{'flag_count'} = $_[11];
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
1;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,258 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
|
||||
use strict;
|
||||
use lib ".";
|
||||
|
||||
# This module implements a series - a set of data to be plotted on a chart.
|
||||
#
|
||||
# This Series is in the database if and only if self->{'series_id'} is defined.
|
||||
# Note that the series being in the database does not mean that the fields of
|
||||
# this object are the same as the DB entries, as the object may have been
|
||||
# altered.
|
||||
|
||||
package Bugzilla::Series;
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
||||
use constant PUBLIC_USER_ID => 0;
|
||||
|
||||
sub new {
|
||||
my $invocant = shift;
|
||||
my $class = ref($invocant) || $invocant;
|
||||
|
||||
# Create a ref to an empty hash and bless it
|
||||
my $self = {};
|
||||
bless($self, $class);
|
||||
|
||||
my $arg_count = scalar(@_);
|
||||
|
||||
# new() can return undef if you pass in a series_id and the user doesn't
|
||||
# have sufficient permissions. If you create a new series in this way,
|
||||
# you need to check for an undef return, and act appropriately.
|
||||
my $retval = $self;
|
||||
|
||||
# There are three ways of creating Series objects. Two (CGI and Parameters)
|
||||
# are for use when creating a new series. One (Database) is for retrieving
|
||||
# information on existing series.
|
||||
if ($arg_count == 1) {
|
||||
if (ref($_[0])) {
|
||||
# We've been given a CGI object to create a new Series from.
|
||||
# This series may already exist - external code needs to check
|
||||
# before it calls writeToDatabase().
|
||||
$self->initFromCGI($_[0]);
|
||||
}
|
||||
else {
|
||||
# We've been given a series_id, which should represent an existing
|
||||
# Series.
|
||||
$retval = $self->initFromDatabase($_[0]);
|
||||
}
|
||||
}
|
||||
elsif ($arg_count >= 6 && $arg_count <= 8) {
|
||||
# We've been given a load of parameters to create a new Series from.
|
||||
# Currently, undef is always passed as the first parameter; this allows
|
||||
# you to call writeToDatabase() unconditionally.
|
||||
$self->initFromParameters(@_);
|
||||
}
|
||||
else {
|
||||
die("Bad parameters passed in - invalid number of args: $arg_count");
|
||||
}
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
sub initFromDatabase {
|
||||
my $self = shift;
|
||||
my $series_id = shift;
|
||||
|
||||
&::detaint_natural($series_id)
|
||||
|| &::ThrowCodeError("invalid_series_id", { 'series_id' => $series_id });
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my @series = $dbh->selectrow_array("SELECT series.series_id, cc1.name, " .
|
||||
"cc2.name, series.name, series.creator, series.frequency, " .
|
||||
"series.query, series.public " .
|
||||
"FROM series " .
|
||||
"LEFT JOIN series_categories AS cc1 " .
|
||||
" ON series.category = cc1.id " .
|
||||
"LEFT JOIN series_categories AS cc2 " .
|
||||
" ON series.subcategory = cc2.id " .
|
||||
"LEFT JOIN category_group_map AS cgm " .
|
||||
" ON series.category = cgm.category_id " .
|
||||
"LEFT JOIN user_group_map AS ugm " .
|
||||
" ON cgm.group_id = ugm.group_id " .
|
||||
" AND ugm.user_id = " . Bugzilla->user->id .
|
||||
" AND isbless = 0 " .
|
||||
"WHERE series.series_id = $series_id AND " .
|
||||
"(public = 1 OR creator = " . Bugzilla->user->id . " OR " .
|
||||
"(ugm.group_id IS NOT NULL)) " .
|
||||
"GROUP BY series_id");
|
||||
|
||||
if (@series) {
|
||||
$self->initFromParameters(@series);
|
||||
return $self;
|
||||
}
|
||||
else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub initFromParameters {
|
||||
# Pass undef as the first parameter if you are creating a new series.
|
||||
my $self = shift;
|
||||
|
||||
($self->{'series_id'}, $self->{'category'}, $self->{'subcategory'},
|
||||
$self->{'name'}, $self->{'creator'}, $self->{'frequency'},
|
||||
$self->{'query'}, $self->{'public'}) = @_;
|
||||
}
|
||||
|
||||
sub initFromCGI {
|
||||
my $self = shift;
|
||||
my $cgi = shift;
|
||||
|
||||
$self->{'series_id'} = $cgi->param('series_id') || undef;
|
||||
if (defined($self->{'series_id'})) {
|
||||
detaint_natural($self->{'series_id'})
|
||||
|| &::ThrowCodeError("invalid_series_id",
|
||||
{ 'series_id' => $self->{'series_id'} });
|
||||
}
|
||||
|
||||
$self->{'category'} = $cgi->param('category')
|
||||
|| $cgi->param('newcategory')
|
||||
|| &::ThrowUserError("missing_category");
|
||||
|
||||
$self->{'subcategory'} = $cgi->param('subcategory')
|
||||
|| $cgi->param('newsubcategory')
|
||||
|| &::ThrowUserError("missing_subcategory");
|
||||
|
||||
$self->{'name'} = $cgi->param('name')
|
||||
|| &::ThrowUserError("missing_name");
|
||||
|
||||
$self->{'creator'} = Bugzilla->user->id;
|
||||
|
||||
$self->{'frequency'} = $cgi->param('frequency');
|
||||
detaint_natural($self->{'frequency'})
|
||||
|| &::ThrowUserError("missing_frequency");
|
||||
|
||||
$self->{'query'} = $cgi->canonicalise_query("format", "ctype", "action",
|
||||
"category", "subcategory", "name",
|
||||
"frequency", "public", "query_format");
|
||||
trick_taint($self->{'query'});
|
||||
|
||||
$self->{'public'} = $cgi->param('public') ? 1 : 0;
|
||||
|
||||
# Change 'admin' here and in series.html.tmpl, or remove the check
|
||||
# completely, if you want to change who can make series public.
|
||||
$self->{'public'} = 0 unless &::UserInGroup('admin');
|
||||
}
|
||||
|
||||
sub writeToDatabase {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("LOCK TABLES series_categories WRITE, series WRITE");
|
||||
|
||||
my $category_id = getCategoryID($self->{'category'});
|
||||
my $subcategory_id = getCategoryID($self->{'subcategory'});
|
||||
|
||||
my $exists;
|
||||
if ($self->{'series_id'}) {
|
||||
$exists =
|
||||
$dbh->selectrow_array("SELECT series_id FROM series
|
||||
WHERE series_id = $self->{'series_id'}");
|
||||
}
|
||||
|
||||
# Is this already in the database?
|
||||
if ($exists) {
|
||||
# Update existing series
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("UPDATE series SET " .
|
||||
"category = ?, subcategory = ?," .
|
||||
"name = ?, frequency = ?, public = ? " .
|
||||
"WHERE series_id = ?", undef,
|
||||
$category_id, $subcategory_id, $self->{'name'},
|
||||
$self->{'frequency'}, $self->{'public'},
|
||||
$self->{'series_id'});
|
||||
}
|
||||
else {
|
||||
# Insert the new series into the series table
|
||||
$dbh->do("INSERT INTO series (creator, category, subcategory, " .
|
||||
"name, frequency, query, public) VALUES " .
|
||||
"($self->{'creator'}, " .
|
||||
"$category_id, $subcategory_id, " .
|
||||
$dbh->quote($self->{'name'}) . ", $self->{'frequency'}," .
|
||||
$dbh->quote($self->{'query'}) . ", $self->{'public'})");
|
||||
|
||||
# Retrieve series_id
|
||||
$self->{'series_id'} = $dbh->selectrow_array("SELECT MAX(series_id) " .
|
||||
"FROM series");
|
||||
$self->{'series_id'}
|
||||
|| &::ThrowCodeError("missing_series_id", { 'series' => $self });
|
||||
}
|
||||
|
||||
$dbh->do("UNLOCK TABLES");
|
||||
}
|
||||
|
||||
# Check whether a series with this name, category and subcategory exists in
|
||||
# the DB and, if so, returns its series_id.
|
||||
sub existsInDatabase {
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
my $category_id = getCategoryID($self->{'category'});
|
||||
my $subcategory_id = getCategoryID($self->{'subcategory'});
|
||||
|
||||
trick_taint($self->{'name'});
|
||||
my $series_id = $dbh->selectrow_array("SELECT series_id " .
|
||||
"FROM series WHERE category = $category_id " .
|
||||
"AND subcategory = $subcategory_id AND name = " .
|
||||
$dbh->quote($self->{'name'}));
|
||||
|
||||
return($series_id);
|
||||
}
|
||||
|
||||
# Get a category or subcategory IDs, creating the category if it doesn't exist.
|
||||
sub getCategoryID {
|
||||
my ($category) = @_;
|
||||
my $category_id;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# This seems for the best idiom for "Do A. Then maybe do B and A again."
|
||||
while (1) {
|
||||
# We are quoting this to put it in the DB, so we can remove taint
|
||||
trick_taint($category);
|
||||
|
||||
$category_id = $dbh->selectrow_array("SELECT id " .
|
||||
"from series_categories " .
|
||||
"WHERE name =" . $dbh->quote($category));
|
||||
|
||||
last if defined($category_id);
|
||||
|
||||
$dbh->do("INSERT INTO series_categories (name) " .
|
||||
"VALUES (" . $dbh->quote($category) . ")");
|
||||
}
|
||||
|
||||
return $category_id;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,410 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Tobias Burnus <burnus@net-b.de>
|
||||
|
||||
|
||||
package Bugzilla::Template;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config qw(:DEFAULT $templatedir $datadir);
|
||||
use Bugzilla::Util;
|
||||
|
||||
# for time2str - replace by TT Date plugin??
|
||||
use Date::Format ();
|
||||
|
||||
use base qw(Template);
|
||||
|
||||
# XXX - mod_perl
|
||||
my $template_include_path;
|
||||
|
||||
# Make an ordered list out of a HTTP Accept-Language header see RFC 2616, 14.4
|
||||
# We ignore '*' and <language-range>;q=0
|
||||
# For languages with the same priority q the order remains unchanged.
|
||||
sub sortAcceptLanguage {
|
||||
sub sortQvalue { $b->{'qvalue'} <=> $a->{'qvalue'} }
|
||||
my $accept_language = $_[0];
|
||||
|
||||
# clean up string.
|
||||
$accept_language =~ s/[^A-Za-z;q=0-9\.\-,]//g;
|
||||
my @qlanguages;
|
||||
my @languages;
|
||||
foreach(split /,/, $accept_language) {
|
||||
if (m/([A-Za-z\-]+)(?:;q=(\d(?:\.\d+)))?/) {
|
||||
my $lang = $1;
|
||||
my $qvalue = $2;
|
||||
$qvalue = 1 if not defined $qvalue;
|
||||
next if $qvalue == 0;
|
||||
$qvalue = 1 if $qvalue > 1;
|
||||
push(@qlanguages, {'qvalue' => $qvalue, 'language' => $lang});
|
||||
}
|
||||
}
|
||||
|
||||
return map($_->{'language'}, (sort sortQvalue @qlanguages));
|
||||
}
|
||||
|
||||
# Returns the path to the templates based on the Accept-Language
|
||||
# settings of the user and of the available languages
|
||||
# If no Accept-Language is present it uses the defined default
|
||||
sub getTemplateIncludePath () {
|
||||
# Return cached value if available
|
||||
|
||||
# XXXX - mod_perl!
|
||||
if ($template_include_path) {
|
||||
return $template_include_path;
|
||||
}
|
||||
my $languages = trim(Param('languages'));
|
||||
if (not ($languages =~ /,/)) {
|
||||
return $template_include_path =
|
||||
["$templatedir/$languages/custom",
|
||||
"$templatedir/$languages/extension",
|
||||
"$templatedir/$languages/default"];
|
||||
}
|
||||
my @languages = sortAcceptLanguage($languages);
|
||||
my @accept_language = sortAcceptLanguage($ENV{'HTTP_ACCEPT_LANGUAGE'} || "" );
|
||||
my @usedlanguages;
|
||||
foreach my $lang (@accept_language) {
|
||||
# Per RFC 1766 and RFC 2616 any language tag matches also its
|
||||
# primary tag. That is 'en' (accept lanuage) matches 'en-us',
|
||||
# 'en-uk' etc. but not the otherway round. (This is unfortunally
|
||||
# not very clearly stated in those RFC; see comment just over 14.5
|
||||
# in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
|
||||
if(my @found = grep /^$lang(-.+)?$/i, @languages) {
|
||||
push (@usedlanguages, @found);
|
||||
}
|
||||
}
|
||||
push(@usedlanguages, Param('defaultlanguage'));
|
||||
return $template_include_path =
|
||||
[map(("$templatedir/$_/custom",
|
||||
"$templatedir/$_/extension",
|
||||
"$templatedir/$_/default"),
|
||||
@usedlanguages)];
|
||||
}
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Templatization Code
|
||||
|
||||
# Use the Toolkit Template's Stash module to add utility pseudo-methods
|
||||
# to template variables.
|
||||
use Template::Stash;
|
||||
|
||||
# Add "contains***" methods to list variables that search for one or more
|
||||
# items in a list and return boolean values representing whether or not
|
||||
# one/all/any item(s) were found.
|
||||
$Template::Stash::LIST_OPS->{ contains } =
|
||||
sub {
|
||||
my ($list, $item) = @_;
|
||||
return grep($_ eq $item, @$list);
|
||||
};
|
||||
|
||||
$Template::Stash::LIST_OPS->{ containsany } =
|
||||
sub {
|
||||
my ($list, $items) = @_;
|
||||
foreach my $item (@$items) {
|
||||
return 1 if grep($_ eq $item, @$list);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
# Allow us to still get the scalar if we use the list operation ".0" on it,
|
||||
# as we often do for defaults in query.cgi and other places.
|
||||
$Template::Stash::SCALAR_OPS->{ 0 } =
|
||||
sub {
|
||||
return $_[0];
|
||||
};
|
||||
|
||||
# Add a "substr" method to the Template Toolkit's "scalar" object
|
||||
# that returns a substring of a string.
|
||||
$Template::Stash::SCALAR_OPS->{ substr } =
|
||||
sub {
|
||||
my ($scalar, $offset, $length) = @_;
|
||||
return substr($scalar, $offset, $length);
|
||||
};
|
||||
|
||||
# Add a "truncate" method to the Template Toolkit's "scalar" object
|
||||
# that truncates a string to a certain length.
|
||||
$Template::Stash::SCALAR_OPS->{ truncate } =
|
||||
sub {
|
||||
my ($string, $length, $ellipsis) = @_;
|
||||
$ellipsis ||= "";
|
||||
|
||||
return $string if !$length || length($string) <= $length;
|
||||
|
||||
my $strlen = $length - length($ellipsis);
|
||||
my $newstr = substr($string, 0, $strlen) . $ellipsis;
|
||||
return $newstr;
|
||||
};
|
||||
|
||||
# Create the template object that processes templates and specify
|
||||
# configuration parameters that apply to all templates.
|
||||
|
||||
###############################################################################
|
||||
|
||||
# Construct the Template object
|
||||
|
||||
# Note that all of the failure cases here can't use templateable errors,
|
||||
# since we won't have a template to use...
|
||||
|
||||
sub create {
|
||||
my $class = shift;
|
||||
|
||||
# IMPORTANT - If you make any configuration changes here, make sure to
|
||||
# make them in t/004.template.t and checksetup.pl.
|
||||
|
||||
return $class->new({
|
||||
# Colon-separated list of directories containing templates.
|
||||
INCLUDE_PATH => [\&getTemplateIncludePath],
|
||||
|
||||
# Remove white-space before template directives (PRE_CHOMP) and at the
|
||||
# beginning and end of templates and template blocks (TRIM) for better
|
||||
# looking, more compact content. Use the plus sign at the beginning
|
||||
# of directives to maintain white space (i.e. [%+ DIRECTIVE %]).
|
||||
PRE_CHOMP => 1,
|
||||
TRIM => 1,
|
||||
|
||||
COMPILE_DIR => "$datadir/template",
|
||||
|
||||
# Initialize templates (f.e. by loading plugins like Hook).
|
||||
PRE_PROCESS => "global/initialize.none.tmpl",
|
||||
|
||||
# Functions for processing text within templates in various ways.
|
||||
# IMPORTANT! When adding a filter here that does not override a
|
||||
# built-in filter, please also add a stub filter to checksetup.pl
|
||||
# and t/004template.t.
|
||||
FILTERS => {
|
||||
|
||||
# Render text in required style.
|
||||
|
||||
inactive => [
|
||||
sub {
|
||||
my($context, $isinactive) = @_;
|
||||
return sub {
|
||||
return $isinactive ? '<span class="bz_inactive">'.$_[0].'</span>' : $_[0];
|
||||
}
|
||||
}, 1
|
||||
],
|
||||
|
||||
closed => [
|
||||
sub {
|
||||
my($context, $isclosed) = @_;
|
||||
return sub {
|
||||
return $isclosed ? '<span class="bz_closed">'.$_[0].'</span>' : $_[0];
|
||||
}
|
||||
}, 1
|
||||
],
|
||||
|
||||
obsolete => [
|
||||
sub {
|
||||
my($context, $isobsolete) = @_;
|
||||
return sub {
|
||||
return $isobsolete ? '<span class="bz_obsolete">'.$_[0].'</span>' : $_[0];
|
||||
}
|
||||
}, 1
|
||||
],
|
||||
|
||||
# Returns the text with backslashes, single/double quotes,
|
||||
# and newlines/carriage returns escaped for use in JS strings.
|
||||
js => sub {
|
||||
my ($var) = @_;
|
||||
$var =~ s/([\\\'\"\/])/\\$1/g;
|
||||
$var =~ s/\n/\\n/g;
|
||||
$var =~ s/\r/\\r/g;
|
||||
$var =~ s/\@/\\x40/g; # anti-spam for email addresses
|
||||
return $var;
|
||||
},
|
||||
|
||||
# HTML collapses newlines in element attributes to a single space,
|
||||
# so form elements which may have whitespace (ie comments) need
|
||||
# to be encoded using 
|
||||
# See bugs 4928, 22983 and 32000 for more details
|
||||
html_linebreak => sub {
|
||||
my ($var) = @_;
|
||||
$var =~ s/\r\n/\
/g;
|
||||
$var =~ s/\n\r/\
/g;
|
||||
$var =~ s/\r/\
/g;
|
||||
$var =~ s/\n/\
/g;
|
||||
return $var;
|
||||
},
|
||||
|
||||
xml => \&Bugzilla::Util::xml_quote ,
|
||||
|
||||
# This filter escapes characters in a variable or value string for
|
||||
# use in a query string. It escapes all characters NOT in the
|
||||
# regex set: [a-zA-Z0-9_\-.]. The 'uri' filter should be used for
|
||||
# a full URL that may have characters that need encoding.
|
||||
url_quote => \&Bugzilla::Util::url_quote ,
|
||||
|
||||
# This filter is similar to url_quote but used a \ instead of a %
|
||||
# as prefix. In addition it replaces a ' ' by a '_'.
|
||||
css_class_quote => \&Bugzilla::Util::css_class_quote ,
|
||||
|
||||
quoteUrls => \&::quoteUrls ,
|
||||
|
||||
bug_link => [ sub {
|
||||
my ($context, $bug) = @_;
|
||||
return sub {
|
||||
my $text = shift;
|
||||
return &::GetBugLink($bug, $text);
|
||||
};
|
||||
},
|
||||
1
|
||||
],
|
||||
|
||||
# In CSV, quotes are doubled, and any value containing a quote or a
|
||||
# comma is enclosed in quotes.
|
||||
csv => sub
|
||||
{
|
||||
my ($var) = @_;
|
||||
$var =~ s/\"/\"\"/g;
|
||||
if ($var !~ /^-?(\d+\.)?\d*$/) {
|
||||
$var = "\"$var\"";
|
||||
}
|
||||
return $var;
|
||||
} ,
|
||||
|
||||
# Format a filesize in bytes to a human readable value
|
||||
unitconvert => sub
|
||||
{
|
||||
my ($data) = @_;
|
||||
my $retval = "";
|
||||
my %units = (
|
||||
'KB' => 1024,
|
||||
'MB' => 1024 * 1024,
|
||||
'GB' => 1024 * 1024 * 1024,
|
||||
);
|
||||
|
||||
if ($data < 1024) {
|
||||
return "$data bytes";
|
||||
}
|
||||
else {
|
||||
my $u;
|
||||
foreach $u ('GB', 'MB', 'KB') {
|
||||
if ($data >= $units{$u}) {
|
||||
return sprintf("%.2f %s", $data/$units{$u}, $u);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
# Format a time for display (more info in Bugzilla::Util)
|
||||
time => \&Bugzilla::Util::format_time,
|
||||
|
||||
# Override html filter to obscure the '@' in user visible strings
|
||||
# See bug 120030 for details
|
||||
html => sub {
|
||||
my ($var) = Template::Filters::html_filter(@_);
|
||||
$var =~ s/\@/\@/g;
|
||||
return $var;
|
||||
},
|
||||
|
||||
# iCalendar contentline filter
|
||||
ics => [ sub {
|
||||
my ($context, @args) = @_;
|
||||
return sub {
|
||||
my ($var) = shift;
|
||||
my ($par) = shift @args;
|
||||
my ($output) = "";
|
||||
|
||||
$var =~ s/[\r\n]/ /g;
|
||||
$var =~ s/([;\\\"])/\\$1/g;
|
||||
|
||||
if ($par) {
|
||||
$output = sprintf("%s:%s", $par, $var);
|
||||
} else {
|
||||
$output = $var;
|
||||
}
|
||||
|
||||
$output =~ s/(.{75,75})/$1\n /g;
|
||||
|
||||
return $output;
|
||||
};
|
||||
},
|
||||
1
|
||||
],
|
||||
|
||||
# We force filtering of every variable in key security-critical
|
||||
# places; we have a none filter for people to use when they
|
||||
# really, really don't want a variable to be changed.
|
||||
none => sub { return $_[0]; } ,
|
||||
},
|
||||
|
||||
PLUGIN_BASE => 'Bugzilla::Template::Plugin',
|
||||
|
||||
# Default variables for all templates
|
||||
VARIABLES => {
|
||||
# Function for retrieving global parameters.
|
||||
'Param' => \&Bugzilla::Config::Param,
|
||||
|
||||
# Function to create date strings
|
||||
'time2str' => \&Date::Format::time2str,
|
||||
|
||||
# Generic linear search function
|
||||
'lsearch' => \&Bugzilla::Util::lsearch,
|
||||
|
||||
# Currently logged in user, if any
|
||||
'user' => sub { return Bugzilla->user; },
|
||||
|
||||
# UserInGroup. Deprecated - use the user.* functions instead
|
||||
'UserInGroup' => \&::UserInGroup,
|
||||
|
||||
# SendBugMail - sends mail about a bug, using Bugzilla::BugMail.pm
|
||||
'SendBugMail' => sub {
|
||||
my ($id, $mailrecipients) = (@_);
|
||||
require Bugzilla::BugMail;
|
||||
Bugzilla::BugMail::Send($id, $mailrecipients);
|
||||
},
|
||||
|
||||
# Bugzilla version
|
||||
# This could be made a ref, or even a CONSTANT with TT2.08
|
||||
'VERSION' => $Bugzilla::Config::VERSION ,
|
||||
},
|
||||
|
||||
}) || die("Template creation failed: " . $class->error());
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Template - Wrapper arround the Template Toolkit C<Template> object
|
||||
|
||||
=head1 SYNOPSYS
|
||||
|
||||
my $template = Bugzilla::Template->create;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is basically a wrapper so that the correct arguments get passed into
|
||||
the C<Template> constructor.
|
||||
|
||||
It should not be used directly by scripts or modules - instead, use
|
||||
C<Bugzilla-E<gt>instance-E<gt>template> to get an already created module.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla>, L<Template>
|
||||
@@ -1,83 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Myk Melez <myk@mozilla.org>
|
||||
#
|
||||
|
||||
package Bugzilla::Template::Plugin::Hook;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Template::Plugin);
|
||||
|
||||
sub load {
|
||||
my ($class, $context) = @_;
|
||||
return $class;
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $context) = @_;
|
||||
return bless { _CONTEXT => $context }, $class;
|
||||
}
|
||||
|
||||
sub process {
|
||||
my ($self, $hook_name) = @_;
|
||||
|
||||
my $paths = $self->{_CONTEXT}->{LOAD_TEMPLATES}->[0]->paths;
|
||||
my $template = $self->{_CONTEXT}->stash->{component}->{name};
|
||||
my @hooks = ();
|
||||
|
||||
foreach my $path (@$paths) {
|
||||
my @files = glob("$path/hook/$template/$hook_name/*.tmpl");
|
||||
|
||||
# Have to remove the templates path (INCLUDE_PATH) from the
|
||||
# file path since the template processor auto-adds it back.
|
||||
@files = map($_ =~ /^$path\/(.*)$/ ? $1 : {}, @files);
|
||||
|
||||
# Add found files to the list of hooks, but removing duplicates,
|
||||
# which can happen when there are identical hooks or duplicate
|
||||
# directories in the INCLUDE_PATH (the latter probably being a TT bug).
|
||||
foreach my $file (@files) {
|
||||
push(@hooks, $file) unless grep($file eq $_, @hooks);
|
||||
}
|
||||
}
|
||||
|
||||
my $output;
|
||||
foreach my $hook (@hooks) {
|
||||
$output .= $self->{_CONTEXT}->process($hook);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Template::Plugin::Hook
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Template Toolkit plugin to process hooks added into templates by extensions.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Template::Plugin>,
|
||||
L<http://bugzilla.mozilla.org/show_bug.cgi?id=229658>
|
||||
@@ -1,65 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Joel Peshkin <bugreport@peshkin.net>
|
||||
#
|
||||
|
||||
package Bugzilla::Template::Plugin::User;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Template::Plugin);
|
||||
|
||||
use Bugzilla::User;
|
||||
|
||||
sub new {
|
||||
my ($class, $context) = @_;
|
||||
|
||||
return bless {}, $class;
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $class = shift;
|
||||
our $AUTOLOAD;
|
||||
|
||||
$AUTOLOAD =~ s/^.*:://;
|
||||
|
||||
return if $AUTOLOAD eq 'DESTROY';
|
||||
|
||||
return Bugzilla::User->$AUTOLOAD(@_);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Template::Plugin::User
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Template Toolkit plugin to allow access to the C<User>
|
||||
object.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::User>, L<Template::Plugin>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,337 +0,0 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
|
||||
package Bugzilla::Util;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Exporter);
|
||||
@Bugzilla::Util::EXPORT = qw(is_tainted trick_taint detaint_natural
|
||||
html_quote url_quote value_quote xml_quote
|
||||
css_class_quote
|
||||
lsearch max min
|
||||
trim format_time);
|
||||
|
||||
use Bugzilla::Config;
|
||||
|
||||
# This is from the perlsec page, slightly modifed to remove a warning
|
||||
# From that page:
|
||||
# This function makes use of the fact that the presence of
|
||||
# tainted data anywhere within an expression renders the
|
||||
# entire expression tainted.
|
||||
# Don't ask me how it works...
|
||||
sub is_tainted {
|
||||
return not eval { my $foo = join('',@_), kill 0; 1; };
|
||||
}
|
||||
|
||||
sub trick_taint {
|
||||
require Carp;
|
||||
Carp::confess("Undef to trick_taint") unless defined $_[0];
|
||||
$_[0] =~ /^(.*)$/s;
|
||||
$_[0] = $1;
|
||||
return (defined($_[0]));
|
||||
}
|
||||
|
||||
sub detaint_natural {
|
||||
$_[0] =~ /^(\d+)$/;
|
||||
$_[0] = $1;
|
||||
return (defined($_[0]));
|
||||
}
|
||||
|
||||
sub html_quote {
|
||||
my ($var) = (@_);
|
||||
$var =~ s/\&/\&/g;
|
||||
$var =~ s/</\</g;
|
||||
$var =~ s/>/\>/g;
|
||||
$var =~ s/\"/\"/g;
|
||||
return $var;
|
||||
}
|
||||
|
||||
# This orignally came from CGI.pm, by Lincoln D. Stein
|
||||
sub url_quote {
|
||||
my ($toencode) = (@_);
|
||||
$toencode =~ s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg;
|
||||
return $toencode;
|
||||
}
|
||||
|
||||
sub css_class_quote {
|
||||
my ($toencode) = (@_);
|
||||
$toencode =~ s/ /_/g;
|
||||
$toencode =~ s/([^a-zA-Z0-9_\-.])/uc sprintf("&#x%x;",ord($1))/eg;
|
||||
return $toencode;
|
||||
}
|
||||
|
||||
sub value_quote {
|
||||
my ($var) = (@_);
|
||||
$var =~ s/\&/\&/g;
|
||||
$var =~ s/</\</g;
|
||||
$var =~ s/>/\>/g;
|
||||
$var =~ s/\"/\"/g;
|
||||
# See bug http://bugzilla.mozilla.org/show_bug.cgi?id=4928 for
|
||||
# explanaion of why bugzilla does this linebreak substitution.
|
||||
# This caused form submission problems in mozilla (bug 22983, 32000).
|
||||
$var =~ s/\r\n/\
/g;
|
||||
$var =~ s/\n\r/\
/g;
|
||||
$var =~ s/\r/\
/g;
|
||||
$var =~ s/\n/\
/g;
|
||||
return $var;
|
||||
}
|
||||
|
||||
sub xml_quote {
|
||||
my ($var) = (@_);
|
||||
$var =~ s/\&/\&/g;
|
||||
$var =~ s/</\</g;
|
||||
$var =~ s/>/\>/g;
|
||||
$var =~ s/\"/\"/g;
|
||||
$var =~ s/\'/\'/g;
|
||||
return $var;
|
||||
}
|
||||
|
||||
sub lsearch {
|
||||
my ($list,$item) = (@_);
|
||||
my $count = 0;
|
||||
foreach my $i (@$list) {
|
||||
if ($i eq $item) {
|
||||
return $count;
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
sub max {
|
||||
my $max = shift(@_);
|
||||
foreach my $val (@_) {
|
||||
$max = $val if $val > $max;
|
||||
}
|
||||
return $max;
|
||||
}
|
||||
|
||||
sub min {
|
||||
my $min = shift(@_);
|
||||
foreach my $val (@_) {
|
||||
$min = $val if $val < $min;
|
||||
}
|
||||
return $min;
|
||||
}
|
||||
|
||||
sub trim {
|
||||
my ($str) = @_;
|
||||
if ($str) {
|
||||
$str =~ s/^\s+//g;
|
||||
$str =~ s/\s+$//g;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
sub format_time {
|
||||
my ($time) = @_;
|
||||
|
||||
my ($year, $month, $day, $hour, $min);
|
||||
if ($time =~ m/^\d{14}$/) {
|
||||
# We appear to have a timestamp direct from MySQL
|
||||
$year = substr($time,0,4);
|
||||
$month = substr($time,4,2);
|
||||
$day = substr($time,6,2);
|
||||
$hour = substr($time,8,2);
|
||||
$min = substr($time,10,2);
|
||||
}
|
||||
elsif ($time =~ m/^(\d{4})\.(\d{2})\.(\d{2}) (\d{2}):(\d{2})(:\d{2})?$/) {
|
||||
$year = $1;
|
||||
$month = $2;
|
||||
$day = $3;
|
||||
$hour = $4;
|
||||
$min = $5;
|
||||
}
|
||||
else {
|
||||
warn "Date/Time format ($time) unrecogonzied";
|
||||
}
|
||||
|
||||
if (defined $year) {
|
||||
$time = "$year-$month-$day $hour:$min";
|
||||
$time .= " " . &::Param('timezone') if &::Param('timezone');
|
||||
}
|
||||
return $time;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Util - Generic utility functions for bugzilla
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::Util;
|
||||
|
||||
# Functions for dealing with variable tainting
|
||||
$rv = is_tainted($var);
|
||||
trick_taint($var);
|
||||
detaint_natural($var);
|
||||
|
||||
# Functions for quoting
|
||||
html_quote($var);
|
||||
url_quote($var);
|
||||
value_quote($var);
|
||||
xml_quote($var);
|
||||
|
||||
# Functions for searching
|
||||
$loc = lsearch(\@arr, $val);
|
||||
$val = max($a, $b, $c);
|
||||
$val = min($a, $b, $c);
|
||||
|
||||
# Functions for trimming variables
|
||||
$val = trim(" abc ");
|
||||
|
||||
# Functions for formatting time
|
||||
format_time($time);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package contains various utility functions which do not belong anywhere
|
||||
else.
|
||||
|
||||
B<It is not intended as a general dumping group for something which
|
||||
people feel might be useful somewhere, someday>. Do not add methods to this
|
||||
package unless it is intended to be used for a significant number of files,
|
||||
and it does not belong anywhere else.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
This package provides several types of routines:
|
||||
|
||||
=head2 Tainting
|
||||
|
||||
Several functions are available to deal with tainted variables. B<Use these
|
||||
with care> to avoid security holes.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<is_tainted>
|
||||
|
||||
Determines whether a particular variable is tainted
|
||||
|
||||
=item C<trick_taint($val)>
|
||||
|
||||
Tricks perl into untainting a particular variable.
|
||||
|
||||
Use trick_taint() when you know that there is no way that the data
|
||||
in a scalar can be tainted, but taint mode still bails on it.
|
||||
|
||||
B<WARNING!! Using this routine on data that really could be tainted defeats
|
||||
the purpose of taint mode. It should only be used on variables that have been
|
||||
sanity checked in some way and have been determined to be OK.>
|
||||
|
||||
=item C<detaint_natural($num)>
|
||||
|
||||
This routine detaints a natural number. It returns a true value if the
|
||||
value passed in was a valid natural number, else it returns false. You
|
||||
B<MUST> check the result of this routine to avoid security holes.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Quoting
|
||||
|
||||
Some values may need to be quoted from perl. However, this should in general
|
||||
be done in the template where possible.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<html_quote($val)>
|
||||
|
||||
Returns a value quoted for use in HTML, with &, E<lt>, E<gt>, and E<34> being
|
||||
replaced with their appropriate HTML entities.
|
||||
|
||||
=item C<url_quote($val)>
|
||||
|
||||
Quotes characters so that they may be included as part of a url.
|
||||
|
||||
=item C<css_class_quote($val)>
|
||||
|
||||
Quotes characters so that they may be used as CSS class names. Spaces
|
||||
are replaced by underscores.
|
||||
|
||||
=item C<value_quote($val)>
|
||||
|
||||
As well as escaping html like C<html_quote>, this routine converts newlines
|
||||
into 
, suitable for use in html attributes.
|
||||
|
||||
=item C<xml_quote($val)>
|
||||
|
||||
This is similar to C<html_quote>, except that ' is escaped to '. This
|
||||
is kept separate from html_quote partly for compatibility with previous code
|
||||
(for ') and partly for future handling of non-ASCII characters.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Searching
|
||||
|
||||
Functions for searching within a set of values.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<lsearch($list, $item)>
|
||||
|
||||
Returns the position of C<$item> in C<$list>. C<$list> must be a list
|
||||
reference.
|
||||
|
||||
If the item is not in the list, returns -1.
|
||||
|
||||
=item C<max($a, $b, ...)>
|
||||
|
||||
Returns the maximum from a set of values.
|
||||
|
||||
=item C<min($a, $b, ...)>
|
||||
|
||||
Returns the minimum from a set of values.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Trimming
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<trim($str)>
|
||||
|
||||
Removes any leading or trailing whitespace from a string. This routine does not
|
||||
modify the existing string.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Formatting Time
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<format_time($time)>
|
||||
|
||||
Takes a time and appends the timezone as defined in editparams.cgi. This routine
|
||||
will be expanded in the future to adjust for user preferences regarding what
|
||||
timezone to display times in. In the future, it may also allow for the time to be
|
||||
shown in different formats.
|
||||
|
||||
=back
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
Bugzilla Quick Start Guide
|
||||
==========================
|
||||
(or, how to get Bugzilla up and running in 10 steps)
|
||||
Christian Reis <kiko@async.com.br>
|
||||
|
||||
This express installation guide is for "normal" Bugzilla installations,
|
||||
which means a Linux or Unix system on which Apache, Perl, MySQL and
|
||||
Sendmail are available. For other configurations, please see Section 4
|
||||
of the Bugzilla Guide in the docs/ directory.
|
||||
|
||||
1. Decide from which URL and directory under your webserver root you
|
||||
will be serving the Bugzilla webpages from.
|
||||
|
||||
2. Unpack distribution into the chosen directory (there is no copying or
|
||||
installation involved).
|
||||
|
||||
3. Run ./checksetup.pl, look for unsolved requirements, install them.
|
||||
You can run checksetup as many times as necessary to check if
|
||||
everything required is installed.
|
||||
|
||||
This will usually include assorted Perl modules, MySQL and sendmail.
|
||||
|
||||
After a successful dependency check, checksetup should complain that
|
||||
localconfig needs to be edited.
|
||||
|
||||
4. Edit the localconfig file, in particular the $webservergroup and
|
||||
$db_* variables. In particular, $db_name and $db_user will define
|
||||
your database setup in step 5.
|
||||
|
||||
If you want to change platforms, operating systems, severities and
|
||||
priorities, this can also be done in localconfig at this time.
|
||||
|
||||
You should also update localconfig.js to reflect these changes. This
|
||||
includes setting the URL you chose in step 1 as the 'bugzilla' JS
|
||||
variable.
|
||||
|
||||
5. Using the name you provided as $db_name above, create a MySQL database
|
||||
for Bugzilla. You should also create a user permission for the name
|
||||
supplied as $db_user with read/write access to that database.
|
||||
|
||||
If you are not familiar with MySQL permissions, it's a good idea to
|
||||
use the mysql_setpermission script that is installed with the MySQL
|
||||
distribution, and be sure to read section 4.1.6 in the Bugzilla Guide.
|
||||
|
||||
6. Run checksetup.pl once more; if all goes well, it should set up the
|
||||
Bugzilla database for you. If not, move back to step 5.
|
||||
|
||||
checksetup.pl should ask you, this time, for the administrator's
|
||||
email address and password. These will be used for the initial
|
||||
Bugzilla administrator account.
|
||||
|
||||
7. Configure Apache (or install and configure, if you don't have it up
|
||||
yet) to point to the Bugzilla directory. You should enable and
|
||||
activate mod_cgi, and add the configuration entries
|
||||
|
||||
Options +ExecCGI
|
||||
AllowOverride Limit
|
||||
DirectoryIndex index.cgi
|
||||
|
||||
to your Bugzilla <Directory> block. You may also need
|
||||
|
||||
AddHandler cgi-script .cgi
|
||||
|
||||
if you don't have that in your Apache configuration file yet.
|
||||
|
||||
8. Visit the URL you chose for Bugzilla. Your browser should display the
|
||||
default Bugzilla home page. You should then log in as the
|
||||
administrator by following the "Log in" link and supplying the
|
||||
account information you provided in step 6.
|
||||
|
||||
9. Scroll to the bottom of the page after logging in, and select
|
||||
"parameters". Set up the relevant parameters for your local setup.
|
||||
|
||||
See section 4.2 of the Bugzilla Guide for a in-depth description of
|
||||
some of the configuration parameters available.
|
||||
|
||||
10. That's it. If anything unexpected comes up:
|
||||
|
||||
- read the error message carefully,
|
||||
- backtrack through the steps above,
|
||||
- check the official installation guide, which is section 4 in the
|
||||
Bugzilla Guide, included in the docs/ directory in various
|
||||
formats.
|
||||
|
||||
Support and installation questions should be directed to the
|
||||
mozilla-webtools@mozilla.org mailing list -- don't write to the
|
||||
developer mailing list: your post *will* be ignored if you do.
|
||||
|
||||
Further support information is at http://www.bugzilla.org/discussion.html
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
* This README is no longer used to house installation instructions. Instead,
|
||||
it contains pointers to where you may find the information you need.
|
||||
|
||||
* A quick installation guide is provided in the QUICKSTART file.
|
||||
|
||||
* Complete installation instructions are found in docs/, with a
|
||||
variety of document types available. Please refer to these documents
|
||||
when installing, configuring, and maintaining your Bugzilla
|
||||
installation. A helpful starting point is docs/txt/Bugzilla-Guide.txt,
|
||||
or with a web browser at docs/html/index.html.
|
||||
* Installation instructions are now found in docs/, with a variety of document
|
||||
types available. Please refer to these documents when installing, configuring,
|
||||
and maintaining your Bugzilla installation. A helpful starting point is
|
||||
docs/txt/Bugzilla-Guide.txt, or with a web browser at docs/html/index.html.
|
||||
|
||||
* Release notes for people upgrading to a new version of Bugzilla are
|
||||
available at docs/rel_notes.txt.
|
||||
|
||||
* If you wish to contribute to the documentation, please read docs/README.docs.
|
||||
|
||||
* The Bugzilla web site is at "http://www.bugzilla.org/". This site will
|
||||
contain the latest Bugzilla information, including how to report bugs and how
|
||||
to get help with Bugzilla.
|
||||
* The Bugzilla web site is at "http://www.mozilla.org/projects/bugzilla/".
|
||||
This site will contain the latest Bugzilla information, including how to
|
||||
report bugs and how to get help with Bugzilla.
|
||||
|
||||
@@ -29,18 +29,19 @@
|
||||
# might involve turning this into a virtual base class, and having
|
||||
# UserSet and KeywordSet types that inherit from it.
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
# XXX - mod_perl
|
||||
# Everything that uses Bugzilla::RelationSet should already have globals.pl
|
||||
# loaded so we don't want to load it here. Doing so causes a loop in Perl
|
||||
# because globals.pl turns around and does a 'use Bugzilla::RelationSet'
|
||||
# Everything that uses RelationSet should already have globals.pl loaded
|
||||
# so we don't want to load it here. Doing so causes a loop in Perl because
|
||||
# globals.pl turns around and does a 'use RelationSet'
|
||||
# See http://bugzilla.mozilla.org/show_bug.cgi?id=72862
|
||||
#require "../globals.pl";
|
||||
#require "globals.pl";
|
||||
|
||||
package Bugzilla::RelationSet;
|
||||
package RelationSet;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
|
||||
# create a new empty Bugzilla::RelationSet
|
||||
# create a new empty RelationSet
|
||||
#
|
||||
sub new {
|
||||
my $type = shift();
|
||||
@@ -61,7 +62,7 @@ sub new {
|
||||
confess("invalid number of arguments");
|
||||
}
|
||||
|
||||
# bless as a Bugzilla::RelationSet
|
||||
# bless as a RelationSet
|
||||
#
|
||||
return $self;
|
||||
}
|
||||
@@ -82,7 +83,7 @@ sub generateSqlDeltas {
|
||||
my ( $self, # instance ptr to set representing the existing state
|
||||
$endState, # instance ptr to set representing the desired state
|
||||
$table, # table where these relations are kept
|
||||
$invariantName, # column held const for a Bugzilla::RelationSet (often "bug_id")
|
||||
$invariantName, # column held const for a RelationSet (often "bug_id")
|
||||
$invariantValue, # what to hold the above column constant at
|
||||
$columnName # the column which varies (often a userid)
|
||||
) = @_;
|
||||
@@ -24,13 +24,11 @@
|
||||
################################################################################
|
||||
|
||||
# Make it harder for us to do dangerous things in Perl.
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
# Bundle the functions in this file together into the "Bugzilla::Token" package.
|
||||
package Bugzilla::Token;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Error;
|
||||
# Bundle the functions in this file together into the "Token" package.
|
||||
package Token;
|
||||
|
||||
use Date::Format;
|
||||
|
||||
@@ -78,29 +76,29 @@ sub IssueEmailChangeToken {
|
||||
my $template = $::template;
|
||||
my $vars = $::vars;
|
||||
|
||||
$vars->{'oldemailaddress'} = $old_email . Param('emailsuffix');
|
||||
$vars->{'newemailaddress'} = $new_email . Param('emailsuffix');
|
||||
$vars->{'oldemailaddress'} = $old_email . &::Param('emailsuffix');
|
||||
$vars->{'newemailaddress'} = $new_email . &::Param('emailsuffix');
|
||||
|
||||
$vars->{'max_token_age'} = $maxtokenage;
|
||||
$vars->{'token_ts'} = $token_ts;
|
||||
|
||||
$vars->{'token'} = $token;
|
||||
$vars->{'emailaddress'} = $old_email . Param('emailsuffix');
|
||||
$vars->{'emailaddress'} = $old_email . &::Param('emailsuffix');
|
||||
|
||||
my $message;
|
||||
$template->process("account/email/change-old.txt.tmpl", $vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| &::ThrowTemplateError($template->error());
|
||||
|
||||
open SENDMAIL, "|/usr/lib/sendmail -t -i";
|
||||
print SENDMAIL $message;
|
||||
close SENDMAIL;
|
||||
|
||||
$vars->{'token'} = $newtoken;
|
||||
$vars->{'emailaddress'} = $new_email . Param('emailsuffix');
|
||||
$vars->{'emailaddress'} = $new_email . &::Param('emailsuffix');
|
||||
|
||||
$message = "";
|
||||
$template->process("account/email/change-new.txt.tmpl", $vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| &::ThrowTemplateError($template->error());
|
||||
|
||||
open SENDMAIL, "|/usr/lib/sendmail -t -i";
|
||||
print SENDMAIL $message;
|
||||
@@ -125,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.
|
||||
|
||||
@@ -139,7 +137,7 @@ sub IssuePasswordToken {
|
||||
my $vars = $::vars;
|
||||
|
||||
$vars->{'token'} = $token;
|
||||
$vars->{'emailaddress'} = $loginname . Param('emailsuffix');
|
||||
$vars->{'emailaddress'} = $loginname . &::Param('emailsuffix');
|
||||
|
||||
$vars->{'max_token_age'} = $maxtokenage;
|
||||
$vars->{'token_ts'} = $token_ts;
|
||||
@@ -147,7 +145,7 @@ sub IssuePasswordToken {
|
||||
my $message = "";
|
||||
$template->process("account/password/forgotten-password.txt.tmpl",
|
||||
$vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| &::ThrowTemplateError($template->error());
|
||||
|
||||
open SENDMAIL, "|/usr/lib/sendmail -t -i";
|
||||
print SENDMAIL $message;
|
||||
@@ -157,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';
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +178,8 @@ sub GenerateUniqueToken {
|
||||
|
||||
++$tries;
|
||||
if ($tries > 100) {
|
||||
ThrowCodeError("token_generation_error");
|
||||
&::DisplayError("Something is seriously wrong with the token generation system.");
|
||||
exit;
|
||||
}
|
||||
|
||||
$token = &::GenerateRandomPassword();
|
||||
@@ -208,12 +210,15 @@ sub Cancel {
|
||||
my ($issuedate, $tokentype, $eventdata, $loginname, $realname) = &::FetchSQLData();
|
||||
|
||||
# Get the email address of the Bugzilla maintainer.
|
||||
my $maintainer = Param('maintainer');
|
||||
my $maintainer = &::Param('maintainer');
|
||||
|
||||
# Format the user's real name and email address into a single string.
|
||||
my $username = $realname ? $realname . " <" . $loginname . ">" : $loginname;
|
||||
|
||||
my $template = $::template;
|
||||
my $vars = $::vars;
|
||||
|
||||
$vars->{'emailaddress'} = $loginname . Param('emailsuffix');
|
||||
$vars->{'emailaddress'} = $username;
|
||||
$vars->{'maintainer'} = $maintainer;
|
||||
$vars->{'remoteaddress'} = $::ENV{'REMOTE_ADDR'};
|
||||
$vars->{'token'} = $token;
|
||||
@@ -226,29 +231,28 @@ sub Cancel {
|
||||
|
||||
my $message;
|
||||
$template->process("account/cancel-token.txt.tmpl", $vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|| &::ThrowTemplateError($template->error());
|
||||
|
||||
open SENDMAIL, "|/usr/lib/sendmail -t -i";
|
||||
print SENDMAIL $message;
|
||||
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 DeletePasswordTokens {
|
||||
my ($userid, $reason) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare("SELECT token " .
|
||||
"FROM tokens " .
|
||||
"WHERE userid=? AND tokentype='password'");
|
||||
$sth->execute($userid);
|
||||
while (my $token = $sth->fetchrow_array) {
|
||||
Bugzilla::Token::Cancel($token, $reason);
|
||||
}
|
||||
sub HasPasswordToken {
|
||||
# Returns a password token if the user has one.
|
||||
|
||||
my ($userid) = @_;
|
||||
|
||||
&::SendSQL("SELECT token FROM tokens
|
||||
WHERE userid = $userid AND tokentype = 'password' LIMIT 1");
|
||||
my ($token) = &::FetchSQLData();
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
sub HasEmailChangeToken {
|
||||
@@ -256,9 +260,10 @@ sub HasEmailChangeToken {
|
||||
|
||||
my ($userid) = @_;
|
||||
|
||||
&::SendSQL("SELECT token FROM tokens WHERE userid = $userid " .
|
||||
"AND (tokentype = 'emailnew' OR tokentype = 'emailold') " .
|
||||
"LIMIT 1");
|
||||
&::SendSQL("SELECT token FROM tokens
|
||||
WHERE userid = $userid
|
||||
AND tokentype = 'emailnew'
|
||||
OR tokentype = 'emailold' LIMIT 1");
|
||||
my ($token) = &::FetchSQLData();
|
||||
|
||||
return $token;
|
||||
@@ -73,7 +73,7 @@ was. Nothing uses this yet, but it still should be recorded.
|
||||
|
||||
You should also run this script to populate the new field:
|
||||
|
||||
#!/usr/bin/perl -w
|
||||
#!/usr/bonsaitools/bin/perl -w
|
||||
use diagnostics;
|
||||
use strict;
|
||||
require "globals.pl";
|
||||
@@ -149,7 +149,7 @@ that submitted the text. This bug has been fixed, so that no further changes
|
||||
like that will happen. But to fix problems that have already crept into your
|
||||
database, you can run the following perl script (which is slow and ugly, but
|
||||
does work:)
|
||||
#!/usr/bin/perl -w
|
||||
#!/usr/bonsaitools/bin/perl -w
|
||||
use diagnostics;
|
||||
use strict;
|
||||
require "globals.pl";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
366
mozilla/webtools/bugzilla/bug_form.pl
Normal file
366
mozilla/webtools/bugzilla/bug_form.pl
Normal file
@@ -0,0 +1,366 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
use RelationSet;
|
||||
|
||||
# Use the Attachment module to display attachments for the bug.
|
||||
use Attachment;
|
||||
|
||||
sub show_bug {
|
||||
# Shut up misguided -w warnings about "used only once". For some reason,
|
||||
# "use vars" chokes on me when I try it here.
|
||||
sub bug_form_pl_sillyness {
|
||||
my $zz;
|
||||
$zz = %::FORM;
|
||||
$zz = %::proddesc;
|
||||
$zz = %::prodmaxvotes;
|
||||
$zz = @::enterable_products;
|
||||
$zz = @::settable_resolution;
|
||||
$zz = $::unconfirmedstate;
|
||||
$zz = $::milestoneurl;
|
||||
$zz = $::template;
|
||||
$zz = $::vars;
|
||||
$zz = @::legal_priority;
|
||||
$zz = @::legal_platform;
|
||||
$zz = @::legal_severity;
|
||||
$zz = @::legal_bug_status;
|
||||
$zz = @::target_milestone;
|
||||
$zz = @::components;
|
||||
$zz = @::legal_keywords;
|
||||
$zz = @::versions;
|
||||
$zz = @::legal_opsys;
|
||||
}
|
||||
|
||||
# Use templates
|
||||
my $template = $::template;
|
||||
my $vars = $::vars;
|
||||
|
||||
$vars->{'GetBugLink'} = \&GetBugLink;
|
||||
$vars->{'quoteUrls'} = \"eUrls,
|
||||
$vars->{'lsearch'} = \&lsearch,
|
||||
$vars->{'header_done'} = (@_),
|
||||
|
||||
my $userid = quietly_check_login();
|
||||
|
||||
my $id = $::FORM{'id'};
|
||||
|
||||
if (!defined($id)) {
|
||||
$template->process("bug/choose.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
exit;
|
||||
}
|
||||
|
||||
my %user = %{$vars->{'user'}};
|
||||
my %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, ";
|
||||
|
||||
if ($::driver eq 'mysql') {
|
||||
$query .= "
|
||||
date_format(creation_ts, '%Y-%m-%d %H:%i'),
|
||||
delta_ts, ";
|
||||
} elsif ($::driver eq 'Pg') {
|
||||
$query .= "
|
||||
TO_CHAR(creation_ts, 'YYYY-MM-DD HH24:MI:SS'),
|
||||
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,
|
||||
delta_ts ";
|
||||
|
||||
SendSQL($query);
|
||||
|
||||
my $value;
|
||||
my @row = FetchSQLData();
|
||||
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")
|
||||
{
|
||||
$value = shift(@row);
|
||||
$bug{$field} = defined($value) ? $value : "";
|
||||
}
|
||||
|
||||
# General arrays of info about the database state
|
||||
GetVersionTable();
|
||||
|
||||
# Fiddle the product list.
|
||||
my $seen_curr_prod;
|
||||
my @prodlist;
|
||||
|
||||
foreach my $product (@::enterable_products) {
|
||||
if ($product eq $bug{'product'}) {
|
||||
# if it's the product the bug is already in, it's ALWAYS in
|
||||
# the popup, period, whether the user can see it or not, and
|
||||
# regardless of the disallownew setting.
|
||||
$seen_curr_prod = 1;
|
||||
push(@prodlist, $product);
|
||||
next;
|
||||
}
|
||||
|
||||
next if !CanSeeProduct($userid, $product);
|
||||
|
||||
push(@prodlist, $product);
|
||||
}
|
||||
|
||||
# The current product is part of the popup, even if new bugs are no longer
|
||||
# allowed for that product
|
||||
if (!$seen_curr_prod) {
|
||||
push (@prodlist, $bug{'product'});
|
||||
@prodlist = sort @prodlist;
|
||||
}
|
||||
|
||||
$vars->{'product'} = \@prodlist;
|
||||
$vars->{'rep_platform'} = \@::legal_platform;
|
||||
$vars->{'priority'} = \@::legal_priority;
|
||||
$vars->{'bug_severity'} = \@::legal_severity;
|
||||
$vars->{'op_sys'} = \@::legal_opsys;
|
||||
$vars->{'bug_status'} = \@::legal_bug_status;
|
||||
|
||||
# Hack - this array contains "" for some reason. See bug 106589.
|
||||
shift @::settable_resolution;
|
||||
$vars->{'resolution'} = \@::settable_resolution;
|
||||
|
||||
$vars->{'component_'} = $::components{$bug{'product'}};
|
||||
$vars->{'version'} = $::versions{$bug{'product'}};
|
||||
$vars->{'target_milestone'} = $::target_milestone{$bug{'product'}};
|
||||
$bug{'milestoneurl'} = $::milestoneurl{$bug{'product'}} ||
|
||||
"notargetmilestone.html";
|
||||
|
||||
$vars->{'use_votes'} = $::prodmaxvotes{$bug{'product'}};
|
||||
|
||||
# Add additional, calculated fields to the bug hash
|
||||
if (@::legal_keywords) {
|
||||
$vars->{'use_keywords'} = 1;
|
||||
|
||||
SendSQL("SELECT keyworddefs.name
|
||||
FROM keyworddefs, keywords
|
||||
WHERE keywords.bug_id = $id
|
||||
AND keyworddefs.id = keywords.keywordid
|
||||
ORDER BY keyworddefs.name");
|
||||
my @keywords;
|
||||
while (MoreSQLData()) {
|
||||
push(@keywords, FetchOneColumn());
|
||||
}
|
||||
|
||||
$bug{'keywords'} = \@keywords;
|
||||
}
|
||||
|
||||
# Attachments
|
||||
$bug{'attachments'} = Attachment::query($id);
|
||||
|
||||
# Dependencies
|
||||
my @list;
|
||||
SendSQL("SELECT dependson FROM dependencies WHERE
|
||||
blocked = $id ORDER BY dependson");
|
||||
while (MoreSQLData()) {
|
||||
my ($i) = FetchSQLData();
|
||||
push(@list, $i);
|
||||
}
|
||||
|
||||
$bug{'dependson'} = \@list;
|
||||
|
||||
my @list2;
|
||||
SendSQL("SELECT blocked FROM dependencies WHERE
|
||||
dependson = $id ORDER BY blocked");
|
||||
while (MoreSQLData()) {
|
||||
my ($i) = FetchSQLData();
|
||||
push(@list2, $i);
|
||||
}
|
||||
|
||||
$bug{'blocked'} = \@list2;
|
||||
|
||||
# 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;
|
||||
}
|
||||
|
||||
# Now get information about each group
|
||||
SendSQL("SELECT group_id, name, description " .
|
||||
"FROM groups " .
|
||||
# "WHERE group_id IN (" . join(',', @groups) . ") " .
|
||||
"ORDER BY description");
|
||||
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}))))
|
||||
{
|
||||
$user{'inallgroups'} &= $ingroup;
|
||||
|
||||
push (@groups, { "bit" => $group_id,
|
||||
"ison" => $buggroups{$group_id},
|
||||
"ingroup" => $usergroups{$group_id},
|
||||
"description" => $description });
|
||||
}
|
||||
}
|
||||
|
||||
# If the bug is restricted to a group, display checkboxes that allow
|
||||
# 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) {
|
||||
$bug{'inagroup'} = 1;
|
||||
|
||||
# Determine whether or not the bug is always accessible by the
|
||||
# reporter, QA contact, and/or users on the cc: list.
|
||||
SendSQL("SELECT reporter_accessible, cclist_accessible
|
||||
FROM bugs
|
||||
WHERE bug_id = $id
|
||||
");
|
||||
($bug{'reporter_accessible'},
|
||||
$bug{'cclist_accessible'}) = FetchSQLData();
|
||||
}
|
||||
}
|
||||
$vars->{'groups'} = \@groups;
|
||||
|
||||
my $movers = Param("movers");
|
||||
$user{'canmove'} = Param("move-enabled")
|
||||
&& (defined $::COOKIE{"Bugzilla_login"})
|
||||
&& ($::COOKIE{"Bugzilla_login"} =~ /\Q$movers\E/);
|
||||
|
||||
# User permissions
|
||||
|
||||
# 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'}
|
||||
|| UserInGroup($userid, "editbugs");
|
||||
$user{'canconfirm'} = ($::userid == 0) || UserInGroup($userid, "canconfirm");
|
||||
|
||||
# Bug states
|
||||
$bug{'isunconfirmed'} = ($bug{'bug_status'} eq $::unconfirmedstate);
|
||||
$bug{'isopened'} = IsOpenedState($bug{'bug_status'});
|
||||
|
||||
# People involved with the bug
|
||||
$bug{'assigned_to_email'} = DBID_to_name($bug{'assigned_to'});
|
||||
$bug{'assigned_to'} = DBID_to_real_or_loginname($bug{'assigned_to'});
|
||||
$bug{'reporter'} = DBID_to_real_or_loginname($bug{'reporter'});
|
||||
$bug{'qa_contact'} = $bug{'qa_contact'} > 0 ?
|
||||
DBID_to_name($bug{'qa_contact'}) : "";
|
||||
|
||||
my $ccset = new RelationSet;
|
||||
$ccset->mergeFromDB("SELECT who FROM cc WHERE bug_id=$id");
|
||||
|
||||
my @cc = $ccset->toArrayOfStrings();
|
||||
$bug{'cc'} = \@cc if $cc[0];
|
||||
|
||||
# Next bug in list (if there is one)
|
||||
my @bug_list;
|
||||
if ($::COOKIE{"BUGLIST"} && $id)
|
||||
{
|
||||
@bug_list = split(/:/, $::COOKIE{"BUGLIST"});
|
||||
}
|
||||
$vars->{'bug_list'} = \@bug_list;
|
||||
|
||||
$bug{'comments'} = GetComments($bug{'bug_id'});
|
||||
|
||||
# This is length in number of comments
|
||||
$bug{'longdesclength'} = scalar(@{$bug{'comments'}});
|
||||
|
||||
# Add the bug and user hashes to the variables
|
||||
$vars->{'bug'} = \%bug;
|
||||
$vars->{'user'} = \%user;
|
||||
|
||||
# Generate and return the UI (HTML page) from the appropriate template.
|
||||
$template->process("bug/edit.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
1;
|
||||
206
mozilla/webtools/bugzilla/bug_status.html
Executable file
206
mozilla/webtools/bugzilla/bug_status.html
Executable file
@@ -0,0 +1,206 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<HTML>
|
||||
|
||||
<!--
|
||||
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):
|
||||
|
||||
Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
-->
|
||||
|
||||
<head>
|
||||
<TITLE>A Bug's Life Cycle</TITLE>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1 ALIGN=CENTER>A Bug's Life Cycle</h1>
|
||||
|
||||
The <B>status</B> and <B>resolution</B> field define and track the
|
||||
life cycle of a bug.
|
||||
|
||||
<a name="status"></a>
|
||||
<p>
|
||||
<TABLE BORDER=1 CELLPADDING=4>
|
||||
|
||||
<TR ALIGN=CENTER VALIGN=TOP>
|
||||
<TD WIDTH="50%"><H1>STATUS</H1> <TD><H1>RESOLUTION</H1>
|
||||
|
||||
<TR VALIGN=TOP>
|
||||
<TD>The <B>status</B> field indicates the general health of a bug. Only
|
||||
certain status transitions are allowed.
|
||||
<TD>The <b>resolution</b> field indicates what happened to this bug.
|
||||
|
||||
<TR VALIGN=TOP><TD>
|
||||
<DL><DT><B>
|
||||
<A HREF="confirmhelp.html">UNCONFIRMED</A></B>
|
||||
<DD> This bug has recently been added to the database. Nobody has
|
||||
validated that this bug is true. Users who have the "canconfirm"
|
||||
permission set may confirm this bug, changing its state to NEW.
|
||||
Or, it may be directly resolved and marked RESOLVED.
|
||||
<DT><B>NEW</B>
|
||||
<DD> This bug has recently been added to the assignee's list of bugs
|
||||
and must be processed. Bugs in this state may be accepted, and
|
||||
become <B>ASSIGNED</B>, passed on to someone else, and remain
|
||||
<B>NEW</B>, or resolved and marked <B>RESOLVED</B>.
|
||||
<DT><B>ASSIGNED</B>
|
||||
<DD> This bug is not yet resolved, but is assigned to the proper
|
||||
person. From here bugs can be given to another person and become
|
||||
<B>NEW</B>, or resolved and become <B>RESOLVED</B>.
|
||||
<DT><B>REOPENED</B>
|
||||
<DD>This bug was once resolved, but the resolution was deemed
|
||||
incorrect. For example, a <B>WORKSFORME</B> bug is
|
||||
<B>REOPENED</B> when more information shows up and the bug is now
|
||||
reproducible. From here bugs are either marked <B>ASSIGNED</B>
|
||||
or <B>RESOLVED</B>.
|
||||
</DL>
|
||||
<TD>
|
||||
<DL>
|
||||
<DD> No resolution yet. All bugs which are in one of these "open" states
|
||||
have the resolution set to blank. All other bugs
|
||||
will be marked with one of the following resolutions.
|
||||
</DL>
|
||||
|
||||
<TR VALIGN=TOP><TD>
|
||||
<DL>
|
||||
<DT><B>RESOLVED</B>
|
||||
<DD> A resolution has been taken, and it is awaiting verification by
|
||||
QA. From here bugs are either re-opened and become
|
||||
<B>REOPENED</B>, are marked <B>VERIFIED</B>, or are closed for good
|
||||
and marked <B>CLOSED</B>.
|
||||
<DT><B>VERIFIED</B>
|
||||
<DD> QA has looked at the bug and the resolution and agrees that the
|
||||
appropriate resolution has been taken. Bugs remain in this state
|
||||
until the product they were reported against actually ships, at
|
||||
which point they become <B>CLOSED</B>.
|
||||
<DT><B>CLOSED</B>
|
||||
<DD> The bug is considered dead, the resolution is correct. Any zombie
|
||||
bugs who choose to walk the earth again must do so by becoming
|
||||
<B>REOPENED</B>.
|
||||
</DL>
|
||||
|
||||
<TD>
|
||||
<DL>
|
||||
<DT><B>FIXED</B>
|
||||
<DD> A fix for this bug is checked into the tree and tested.
|
||||
<DT><B>INVALID</B>
|
||||
<DD> The problem described is not a bug
|
||||
<DT><B>WONTFIX</B>
|
||||
<DD> The problem described is a bug which will never be fixed.
|
||||
<DT><B>LATER</B>
|
||||
<DD> The problem described is a bug which will not be fixed in this
|
||||
version of the product.
|
||||
<DT><B>REMIND</B>
|
||||
<DD> The problem described is a bug which will probably not be fixed in this
|
||||
version of the product, but might still be.
|
||||
<DT><B>DUPLICATE</B>
|
||||
<DD> The problem is a duplicate of an existing bug. Marking a bug
|
||||
duplicate requires the bug# of the duplicating bug and will at
|
||||
least put that bug number in the description field.
|
||||
<DT><B>WORKSFORME</B>
|
||||
<DD> All attempts at reproducing this bug were futile, reading the
|
||||
code produces no clues as to why this behavior would occur. If
|
||||
more information appears later, please re-assign the bug, for
|
||||
now, file it.
|
||||
</DL>
|
||||
</TABLE>
|
||||
|
||||
<H1>Other Fields</H1>
|
||||
|
||||
<table border=1 cellpadding=4><tr><td>
|
||||
<h2><a name="severity">Severity</a></h2>
|
||||
|
||||
This field describes the impact of a bug.
|
||||
|
||||
<p>
|
||||
<p>
|
||||
|
||||
<table>
|
||||
<tr><th>Blocker</th><td>Blocks development and/or testing work
|
||||
<tr><th>Critical</th><td>crashes, loss of data, severe memory leak
|
||||
<tr><th>Major</th><td>major loss of function
|
||||
<tr><th>Minor</th><td>minor loss of function, or other problem where easy workaround is present
|
||||
<tr><th>Trivial</th><td>cosmetic problem like misspelled words or misaligned text
|
||||
<tr><th>Enhancement</th><td>Request for enhancement
|
||||
</table>
|
||||
|
||||
</td><td>
|
||||
|
||||
<h2><a name="priority">Priority</a></h2>
|
||||
|
||||
This field describes the importance and order in which a bug should be
|
||||
fixed. This field is utilized by the programmers/engineers to
|
||||
prioritize their work to be done. The available priorities are:
|
||||
|
||||
<p>
|
||||
<p>
|
||||
|
||||
<table>
|
||||
<tr><th>P1</th><td>Most important
|
||||
<tr><th>P2</th><td>
|
||||
<tr><th>P3</th><td>
|
||||
<tr><th>P4</th><td>
|
||||
<tr><th>P5</th><td>Least important
|
||||
</table>
|
||||
</tr></table>
|
||||
|
||||
<h2><a name="rep_platform">Platform</a></h2>
|
||||
This is the hardware platform against which the bug was reported. Legal
|
||||
platforms include:
|
||||
|
||||
<UL>
|
||||
<LI> All (happens on all platform; cross-platform bug)
|
||||
<LI> Macintosh
|
||||
<LI> PC
|
||||
<LI> Sun
|
||||
<LI> HP
|
||||
</UL>
|
||||
|
||||
<b>Note:</b> Selecting the option "All" does not select bugs assigned against all platforms. It
|
||||
merely selects bugs that <b>occur</b> on all platforms.
|
||||
|
||||
<h2><a name="op_sys">Operating System</a></h2>
|
||||
This is the operating system against which the bug was reported. Legal
|
||||
operating systems include:
|
||||
|
||||
<UL>
|
||||
<LI> All (happens on all operating systems; cross-platform bug)
|
||||
<LI> Windows 95
|
||||
<LI> Mac System 8.0
|
||||
<LI> Linux
|
||||
</UL>
|
||||
|
||||
Note that the operating system implies the platform, but not always.
|
||||
For example, Linux can run on PC and Macintosh and others.
|
||||
|
||||
<h2><a name="assigned_to">Assigned To</a></h2>
|
||||
|
||||
This is the person in charge of resolving the bug. Every time this
|
||||
field changes, the status changes to <B>NEW</B> to make it easy to see
|
||||
which new bugs have appeared on a person's list.
|
||||
|
||||
The default status for queries is set to NEW, ASSIGNED and REOPENED. When
|
||||
searching for bugs that have been resolved or verified, remember to set the
|
||||
status field appropriately.
|
||||
|
||||
<hr>
|
||||
<!-- hhmts start -->
|
||||
Last modified: Sun Apr 14 12:51:23 EST 2002
|
||||
<!-- hhmts end -->
|
||||
</body> </html>
|
||||
File diff suppressed because it is too large
Load Diff
392
mozilla/webtools/bugzilla/bugwritinghelp.html
Normal file
392
mozilla/webtools/bugzilla/bugwritinghelp.html
Normal file
@@ -0,0 +1,392 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<title>Bug Writing Guidelines</title>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h1>Bug Writing Guidelines</h1>
|
||||
</center>
|
||||
|
||||
<h3>Why You Should Read This</h3>
|
||||
|
||||
<blockquote>
|
||||
<p>Simply put, the more effectively you report a bug, the more
|
||||
likely an engineer will actually fix it.</p>
|
||||
|
||||
<p>These guidelines are a general
|
||||
tutorial to teach novice and intermediate bug reporters how to compose effective bug reports. Not every sentence may precisely apply to
|
||||
your software project.</p>
|
||||
</blockquote>
|
||||
|
||||
<h3>How to Write a Useful Bug Report</h3>
|
||||
|
||||
<blockquote>
|
||||
<p>Useful bug reports are ones that get bugs fixed. A useful bug
|
||||
report normally has two qualities:</p>
|
||||
|
||||
<ol>
|
||||
<li><b>Reproducible.</b> If an engineer can't see the bug herself to prove that it exists, she'll probably stamp your bug report "WORKSFORME" or "INVALID" and move on to the next bug. Every detail you can provide helps.<br>
|
||||
<br>
|
||||
</li>
|
||||
|
||||
<li><b>Specific.</b> The quicker the engineer can isolate the bug
|
||||
to a specific area, the more likely she'll expediently fix it.
|
||||
(If a programmer or tester has to decypher a bug, they may spend
|
||||
more time cursing the submitter than solving the problem.)
|
||||
<br>
|
||||
<br>
|
||||
[ <a href="#tips" name="Anchor">Tell Me More</a> ]
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>Let's say the application you're testing is a web browser. You
|
||||
crash at foo.com, and want to write up a bug report:</p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>BAD:</b> "My browser crashed. I think I was on www.foo.com. I play golf with Bill Gates, so you better fix this problem, or I'll report you to him. By the way, your Back icon looks like a squashed rodent. UGGGLY. And my grandmother's home page is all messed up in your browser. Thx 4 UR help."
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>GOOD:</b> "I crashed each time I went to www.foo.com, using
|
||||
the 2002-02-25 build on a Windows 2000 system. I also
|
||||
rebooted into Linux, and reproduced this problem using the 2002-02-24
|
||||
Linux build.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It again crashed each time upon drawing the Foo banner at the top
|
||||
of the page. I broke apart the page, and discovered that the
|
||||
following image link will crash the application reproducibly,
|
||||
unless you remove the "border=0" attribute:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<tt><IMG SRC="http://www.foo.com/images/topics/topicfoos.gif"
|
||||
width="34" height="44" border="0" alt="News"></tt>
|
||||
</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<h3>How to Enter your Useful Bug Report into Bugzilla:</h3>
|
||||
|
||||
<blockquote>
|
||||
<p>Before you enter your bug, use Bugzilla's
|
||||
<a href="query.cgi">search page</a> to determine whether the defect you've discovered is a known, already-reported bug. If your bug is the 37th duplicate of a known issue, you're more likely to annoy the engineer. (Annoyed
|
||||
engineers fix fewer bugs.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Next, be sure to reproduce your bug using a recent
|
||||
build. Engineers tend to be most interested in problems affecting
|
||||
the code base that they're actively working on. After all, the bug you're reporting
|
||||
may already be fixed.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you've discovered a new bug using a current build, report it in
|
||||
Bugzilla:
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
<li>From your Bugzilla main page, choose
|
||||
"<a href="enter_bug.cgi">Enter a new bug</a>".</li>
|
||||
|
||||
<li>Select the product that you've found a bug in.</li>
|
||||
|
||||
<li>Enter your e-mail address, password, and press the "Login"
|
||||
button. (If you don't yet have a password, leave the password field empty,
|
||||
and press the "E-mail me a password" button instead.
|
||||
You'll quickly receive an e-mail message with your password.)</li>
|
||||
</ol>
|
||||
|
||||
<p>Now, fill out the form. Here's what it all means:</p>
|
||||
|
||||
<p><b>Where did you find the bug?</b></p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Product: In which product did you find the bug?</b><br>
|
||||
You just specified this on the last page, so you can't edit it here.</p>
|
||||
|
||||
<p><b>Version: In which product version did you find the
|
||||
bug?</b><br>
|
||||
(If applicable)</p>
|
||||
|
||||
<p><b>Component: In which component does the bug exist?</b><br>
|
||||
Bugzilla requires that you select a component to enter a bug. (Not sure which to choose?
|
||||
Click on the Component link. You'll see a description of each component, to help you make the best choice.)</p>
|
||||
|
||||
<p><b>OS: On which Operating System (OS) did you find this bug?</b>
|
||||
(e.g. Linux, Windows 2000, Mac OS 9.)<br>
|
||||
If you know the bug happens on all OSs, choose 'All'. Otherwise,
|
||||
select the OS that you found the bug on, or "Other" if your OS
|
||||
isn't listed.</p>
|
||||
</blockquote>
|
||||
|
||||
<p><b>How important is the bug?</b></p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Severity: How damaging is the bug?</b><br>
|
||||
This item defaults to 'normal'. If you're not sure what severity your bug deserves, click on the Severity link.
|
||||
You'll see a description of each severity rating. <br>
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<p><b>Who will be following up on the bug?</b></p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Assigned To: Which engineer should be responsible for fixing
|
||||
this bug?</b><br>
|
||||
Bugzilla will automatically assign the bug to a default engineer
|
||||
upon submitting a bug report. If you'd prefer to directly assign the bug to
|
||||
someone else, enter their e-mail address into this field. (To see the list of
|
||||
default engineers for each component, click on the Component
|
||||
link.)</p>
|
||||
|
||||
<p><b>Cc: Who else should receive e-mail updates on changes to this
|
||||
bug?</b><br>
|
||||
List the full e-mail addresses of other individuals who should
|
||||
receive an e-mail update upon every change to the bug report. You
|
||||
can enter as many e-mail addresses as you'd like, separated by spaces or commas, as long as those
|
||||
people have Bugzilla accounts.</p>
|
||||
</blockquote>
|
||||
|
||||
<p><b>What else can you tell the engineer about the bug?</b></p>
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p><b>Summary:</b> <b>How would you describe the bug, in
|
||||
approximately 60 or fewer characters?</b><br>
|
||||
A good summary should <b>quickly and uniquely identify a bug
|
||||
report</b>. Otherwise, an engineer cannot meaningfully identify
|
||||
your bug by its summary, and will often fail to pay attention to
|
||||
your bug report when skimming through a 10 page bug list.<br>
|
||||
<br>
|
||||
A useful summary might be
|
||||
"<tt>PCMCIA install fails on Tosh Tecra 780DVD w/ 3c589C</tt>".
|
||||
"<tt>Software fails</tt>" or "<tt>install problem</tt>" would be
|
||||
examples of a bad summary.<br>
|
||||
<br>
|
||||
[ <a href="#summary">Tell Me More</a> ]<br>
|
||||
<br>
|
||||
<b>Description: </b><br>
|
||||
Please provide a detailed problem report in this field.
|
||||
Your bug's recipients will most likely expect the following information:</p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Overview Description:</b> More detailed expansion of
|
||||
summary.</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
Drag-selecting any page crashes Mac builds in NSGetFactory
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p><b>Steps to Reproduce:</b> Minimized, easy-to-follow steps that will
|
||||
trigger the bug. Include any special setup steps.</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
1) View any web page. (I used the default sample page,
|
||||
resource:/res/samples/test0.html)
|
||||
|
||||
2) Drag-select the page. (Specifically, while holding down
|
||||
the mouse button, drag the mouse pointer downwards from any
|
||||
point in the browser's content region to the bottom of the
|
||||
browser's content region.)
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
<b>Actual Results:</b> What the application did after performing
|
||||
the above steps.
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
The application crashed. Stack crawl appended below from MacsBug.
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p><b>Expected Results:</b> What the application should have done,
|
||||
were the bug not present.</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
The window should scroll downwards. Scrolled content should be selected.
|
||||
(Or, at least, the application should not crash.)
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p><b>Build Date & Platform:</b> Date and platform of the build
|
||||
that you first encountered the bug in.</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
Build 2002-03-15 on Mac OS 9.0
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p><b>Additional Builds and Platforms:</b> Whether or not the bug
|
||||
takes place on other platforms (or browsers, if applicable).</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
- Also Occurs On
|
||||
Mozilla (2002-03-15 build on Windows NT 4.0)
|
||||
|
||||
- Doesn't Occur On
|
||||
Mozilla (2002-03-15 build on Red Hat Linux; feature not supported)
|
||||
Internet Explorer 5.0 (shipping build on Windows NT 4.0)
|
||||
Netscape Communicator 4.5 (shipping build on Mac OS 9.0)
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p><b>Additional Information:</b> Any other debugging information.
|
||||
For crashing bugs:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Win32:</b> if you receive a Dr. Watson error, please note
|
||||
the type of the crash, and the module that the application crashed
|
||||
in. (e.g. access violation in apprunner.exe)</li>
|
||||
|
||||
<li><b>Mac OS:</b> if you're running MacsBug, please provide the
|
||||
results of a <b>how</b> and an <b>sc</b>:</li>
|
||||
</ul>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
*** MACSBUG STACK CRAWL OF CRASH (Mac OS)
|
||||
Calling chain using A6/R1 links
|
||||
Back chain ISA Caller
|
||||
00000000 PPC 0BA85E74
|
||||
03AEFD80 PPC 0B742248
|
||||
03AEFD30 PPC 0B50FDDC NSGetFactory+027FC
|
||||
PowerPC unmapped memory exception at 0B512BD0 NSGetFactory+055F0
|
||||
</pre>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<p>You're done!<br>
|
||||
<br>
|
||||
After double-checking your entries for any possible errors, press
|
||||
the "Commit" button, and your bug report will now be in the
|
||||
Bugzilla database.<br>
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>More Information on Writing Good Bugs</h3>
|
||||
|
||||
<blockquote>
|
||||
<p><b><a name="tips"></a> 1. General Tips for a Useful Bug
|
||||
Report</b>
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<p>
|
||||
<b>Use an explicit structure, so your bug reports are easy to
|
||||
skim.</b> Bug report users often need immediate access to specific
|
||||
sections of your bug. If your Bugzilla installation supports the
|
||||
Bugzilla Helper, use it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Avoid cuteness if it costs clarity.</b> Nobody will be laughing
|
||||
at your funny bug title at 3:00 AM when they can't remember how to
|
||||
find your bug.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>One bug per report.</b> Completely different people typically
|
||||
fix, verify, and prioritize different bugs. If you mix a handful of
|
||||
bugs into a single report, the right people probably won't discover
|
||||
your bugs in a timely fashion, or at all. Certain bugs are also
|
||||
more important than others. It's impossible to prioritize a bug
|
||||
report when it contains four different issues, all of differing
|
||||
importance.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>No bug is too trivial to report.</b> Unless you're reading the
|
||||
source code, you can't see actual software bugs, like a dangling
|
||||
pointer -- you'll see their visible manifestations, such as the
|
||||
segfault when the application finally crashes. Severe software
|
||||
problems can manifest themselves in superficially trivial ways.
|
||||
File them anyway.<br>
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
<p><b><a name="summary"></a>2. How and Why to Write Good Bug Summaries</b>
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>You want to make a good first impression on the bug
|
||||
recipient.</b> Just like a New York Times headline guides readers
|
||||
towards a relevant article from dozens of choices, will your bug summary
|
||||
suggest that your bug report is worth reading from dozens or hundreds of
|
||||
choices?
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Conversely, a vague bug summary like <tt>install problem</tt> forces anyone
|
||||
reviewing installation bugs to waste time opening up your bug to
|
||||
determine whether it matters.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Your bug will often be searched by its summary.</b> Just as
|
||||
you'd find web pages with Google by searching by keywords through
|
||||
intuition, so will other people locate your bugs. Descriptive bug
|
||||
summaries are naturally keyword-rich, and easier to find.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, you'll find a bug titled "<tt>Dragging icons from List View to
|
||||
gnome-terminal doesn't paste path</tt>" if you search on "List",
|
||||
"terminal", or "path". Those search keywords wouldn't have found a
|
||||
bug titled "<tt>Dragging icons
|
||||
doesn't paste</tt>".
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Ask yourself, "Would someone understand my bug from just this
|
||||
summary?" If so, you've written a fine summary.
|
||||
</p>
|
||||
|
||||
<p><b>Don't write titles like these:</b></p>
|
||||
|
||||
<ol>
|
||||
<li>"Can't install" - Why can't you install? What happens when you
|
||||
try to install?</li>
|
||||
<li>"Severe Performance Problems" - ...and they occur when you do
|
||||
what?</li>
|
||||
<li>"back button does not work" - Ever? At all?</li>
|
||||
</ol>
|
||||
|
||||
<p><b>Good bug titles:</b></p>
|
||||
<ol>
|
||||
<li>"1.0 upgrade installation fails if Mozilla M18 package present"
|
||||
- Explains problem and the context.</li>
|
||||
<li>"RPM 4 installer crashes if launched on Red Hat 6.2 (RPM 3)
|
||||
system" - Explains what happens, and the context.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
|
||||
<p>(Written and maintained by
|
||||
<a href="http://www.prometheus-music.com/eli">Eli Goldberg</a>. Claudius
|
||||
Gayle, Gervase Markham, Peter Mock, Chris Pratt, Tom Schutter and Chris Yeh also
|
||||
contributed significant changes. Constructive
|
||||
<a href="mailto:eli@prometheus-music.com">suggestions</a> welcome.)</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,19 +5,14 @@
|
||||
maintainer CDATA #REQUIRED
|
||||
exporter CDATA #IMPLIED
|
||||
>
|
||||
<!ELEMENT bug (bug_id, (alias?, creation_ts, short_desc, delta_ts, reporter_accessible, cclist_accessible, classification_id, classification, product, component, version, rep_platform, op_sys, bug_status, resolution?, bug_file_loc?, status_whiteboard?, keywords*, priority, bug_severity, target_milestone?, dependson*, blocked*, votes?, reporter, assigned_to, qa_contact?, cc*, (estimated_time, remaining_time, actual_time)?, groups*, long_desc*, attachment*)?)>
|
||||
<!ELEMENT bug (bug_id, (bug_status, product, priority, version, rep_platform, assigned_to, delta_ts, component, reporter, target_milestone?, bug_severity, creation_ts, qa_contact?, op_sys, resolution?, bug_file_loc?, short_desc?, keywords*, status_whiteboard?, dependson*, blocks*, cc*, long_desc*, attachment*)?)>
|
||||
<!ATTLIST bug
|
||||
error (NotFound | NotPermitted | InvalidBugId) #IMPLIED
|
||||
>
|
||||
<!ELEMENT bug_id (#PCDATA)>
|
||||
<!ELEMENT alias (#PCDATA)>
|
||||
<!ELEMENT reporter_accessible (#PCDATA)>
|
||||
<!ELEMENT cclist_accessible (#PCDATA)>
|
||||
<!ELEMENT exporter (#PCDATA)>
|
||||
<!ELEMENT urlbase (#PCDATA)>
|
||||
<!ELEMENT bug_status (#PCDATA)>
|
||||
<!ELEMENT classification_id (#PCDATA)>
|
||||
<!ELEMENT classification (#PCDATA)>
|
||||
<!ELEMENT product (#PCDATA)>
|
||||
<!ELEMENT priority (#PCDATA)>
|
||||
<!ELEMENT version (#PCDATA)>
|
||||
@@ -37,12 +32,8 @@
|
||||
<!ELEMENT short_desc (#PCDATA)>
|
||||
<!ELEMENT keywords (#PCDATA)>
|
||||
<!ELEMENT dependson (#PCDATA)>
|
||||
<!ELEMENT blocked (#PCDATA)>
|
||||
<!ELEMENT blocks (#PCDATA)>
|
||||
<!ELEMENT cc (#PCDATA)>
|
||||
<!ELEMENT group (#PCDATA)>
|
||||
<!ELEMENT estimated_time (#PCDATA)>
|
||||
<!ELEMENT remaining_time (#PCDATA)>
|
||||
<!ELEMENT actual_time (#PCDATA)>
|
||||
<!ELEMENT long_desc (who, bug_when, thetext)>
|
||||
<!ELEMENT who (#PCDATA)>
|
||||
<!ELEMENT bug_when (#PCDATA)>
|
||||
|
||||
55
mozilla/webtools/bugzilla/Bugzilla/Template/Plugin/Bugzilla.pm → mozilla/webtools/bugzilla/changepassword.cgi
Normal file → Executable file
55
mozilla/webtools/bugzilla/Bugzilla/Template/Plugin/Bugzilla.pm → mozilla/webtools/bugzilla/changepassword.cgi
Normal file → Executable file
@@ -1,3 +1,4 @@
|
||||
#!/usr/bonsaitools/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
@@ -17,48 +18,22 @@
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
#
|
||||
|
||||
package Bugzilla::Template::Plugin::Bugzilla;
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Template::Plugin);
|
||||
print q{Content-type: text/html
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
sub new {
|
||||
my ($class, $context) = @_;
|
||||
|
||||
return bless {}, $class;
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="Refresh"
|
||||
CONTENT="0; URL=userprefs.cgi">
|
||||
</HEAD>
|
||||
<BODY>
|
||||
This URL is obsolete. Forwarding you to the correct one.
|
||||
<P>
|
||||
Going to <A HREF="userprefs.cgi">userprefs.cgi</A>
|
||||
<BR>
|
||||
</BODY>
|
||||
</HTML>
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $class = shift;
|
||||
our $AUTOLOAD;
|
||||
|
||||
$AUTOLOAD =~ s/^.*:://;
|
||||
|
||||
return if $AUTOLOAD eq 'DESTROY';
|
||||
|
||||
return Bugzilla->$AUTOLOAD(@_);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Template::Plugin::Bugzilla
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Template Toolkit plugin to allow access to the persistent C<Bugzilla>
|
||||
object.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla>, L<Template::Plugin>
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
#!/usr/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
|
||||
# Glossary:
|
||||
# series: An individual, defined set of data plotted over time.
|
||||
# data set: What a series is called in the UI.
|
||||
# line: A set of one or more series, to be summed and drawn as a single
|
||||
# line when the series is plotted.
|
||||
# chart: A set of lines
|
||||
#
|
||||
# So when you select rows in the UI, you are selecting one or more lines, not
|
||||
# series.
|
||||
|
||||
# Generic Charting TODO:
|
||||
#
|
||||
# JS-less chart creation - hard.
|
||||
# Broken image on error or no data - need to do much better.
|
||||
# Centralise permission checking, so UserInGroup('editbugs') not scattered
|
||||
# everywhere.
|
||||
# User documentation :-)
|
||||
#
|
||||
# Bonus:
|
||||
# Offer subscription when you get a "series already exists" error?
|
||||
|
||||
use strict;
|
||||
use lib qw(.);
|
||||
|
||||
require "CGI.pl";
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Chart;
|
||||
use Bugzilla::Series;
|
||||
|
||||
use vars qw($cgi $template $vars);
|
||||
|
||||
# Go back to query.cgi if we are adding a boolean chart parameter.
|
||||
if (grep(/^cmd-/, $cgi->param())) {
|
||||
my $params = $cgi->canonicalise_query("format", "ctype", "action");
|
||||
print "Location: query.cgi?format=" . $cgi->param('query_format') .
|
||||
($params ? "&$params" : "") . "\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
my $action = $cgi->param('action');
|
||||
my $series_id = $cgi->param('series_id');
|
||||
|
||||
# Because some actions are chosen by buttons, we can't encode them as the value
|
||||
# of the action param, because that value is localisation-dependent. So, we
|
||||
# encode it in the name, as "action-<action>". Some params even contain the
|
||||
# series_id they apply to (e.g. subscribe, unsubscribe.)
|
||||
my @actions = grep(/^action-/, $cgi->param());
|
||||
if ($actions[0] && $actions[0] =~ /^action-([^\d]+)(\d*)$/) {
|
||||
$action = $1;
|
||||
$series_id = $2 if $2;
|
||||
}
|
||||
|
||||
$action ||= "assemble";
|
||||
|
||||
# Go to buglist.cgi if we are doing a search.
|
||||
if ($action eq "search") {
|
||||
my $params = $cgi->canonicalise_query("format", "ctype", "action");
|
||||
print "Location: buglist.cgi" . ($params ? "?$params" : "") . "\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
UserInGroup(Param("chartgroup"))
|
||||
|| ThrowUserError("authorization_failure",
|
||||
{action => "use this feature"});
|
||||
|
||||
# Only admins may create public queries
|
||||
UserInGroup('admin') || $cgi->delete('public');
|
||||
|
||||
# All these actions relate to chart construction.
|
||||
if ($action =~ /^(assemble|add|remove|sum|subscribe|unsubscribe)$/) {
|
||||
# These two need to be done before the creation of the Chart object, so
|
||||
# that the changes they make will be reflected in it.
|
||||
if ($action =~ /^subscribe|unsubscribe$/) {
|
||||
detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
$series->$action($::userid);
|
||||
}
|
||||
|
||||
my $chart = new Bugzilla::Chart($cgi);
|
||||
|
||||
if ($action =~ /^remove|sum$/) {
|
||||
$chart->$action(getSelectedLines());
|
||||
}
|
||||
elsif ($action eq "add") {
|
||||
my @series_ids = getAndValidateSeriesIDs();
|
||||
$chart->add(@series_ids);
|
||||
}
|
||||
|
||||
view($chart);
|
||||
}
|
||||
elsif ($action eq "plot") {
|
||||
plot();
|
||||
}
|
||||
elsif ($action eq "wrap") {
|
||||
# For CSV "wrap", we go straight to "plot".
|
||||
if ($cgi->param('ctype') && $cgi->param('ctype') eq "csv") {
|
||||
plot();
|
||||
}
|
||||
else {
|
||||
wrap();
|
||||
}
|
||||
}
|
||||
elsif ($action eq "create") {
|
||||
assertCanCreate($cgi);
|
||||
|
||||
my $series = new Bugzilla::Series($cgi);
|
||||
|
||||
if (!$series->existsInDatabase()) {
|
||||
$series->writeToDatabase();
|
||||
$vars->{'message'} = "series_created";
|
||||
}
|
||||
else {
|
||||
ThrowUserError("series_already_exists", {'series' => $series});
|
||||
}
|
||||
|
||||
$vars->{'series'} = $series;
|
||||
|
||||
print "Content-Type: text/html\n\n";
|
||||
$template->process("global/message.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
elsif ($action eq "edit") {
|
||||
detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
|
||||
assertCanEdit($series_id);
|
||||
|
||||
my $series = new Bugzilla::Series($series_id);
|
||||
|
||||
edit($series);
|
||||
}
|
||||
elsif ($action eq "alter") {
|
||||
# This is the "commit" action for editing a series
|
||||
detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
|
||||
assertCanEdit($series_id);
|
||||
|
||||
my $series = new Bugzilla::Series($cgi);
|
||||
|
||||
# We need to check if there is _another_ series in the database with
|
||||
# our (potentially new) name. So we call existsInDatabase() to see if
|
||||
# the return value is us or some other series we need to avoid stomping
|
||||
# on.
|
||||
my $id_of_series_in_db = $series->existsInDatabase();
|
||||
if (defined($id_of_series_in_db) &&
|
||||
$id_of_series_in_db != $series->{'series_id'})
|
||||
{
|
||||
ThrowUserError("series_already_exists", {'series' => $series});
|
||||
}
|
||||
|
||||
$series->writeToDatabase();
|
||||
$vars->{'changes_saved'} = 1;
|
||||
|
||||
edit($series);
|
||||
}
|
||||
else {
|
||||
ThrowCodeError("unknown_action");
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
# Find any selected series and return either the first or all of them.
|
||||
sub getAndValidateSeriesIDs {
|
||||
my @series_ids = grep(/^\d+$/, $cgi->param("name"));
|
||||
|
||||
return wantarray ? @series_ids : $series_ids[0];
|
||||
}
|
||||
|
||||
# Return a list of IDs of all the lines selected in the UI.
|
||||
sub getSelectedLines {
|
||||
my @ids = map { /^select(\d+)$/ ? $1 : () } $cgi->param();
|
||||
|
||||
return @ids;
|
||||
}
|
||||
|
||||
# Check if the user is the owner of series_id or is an admin.
|
||||
sub assertCanEdit {
|
||||
my ($series_id) = @_;
|
||||
|
||||
return if UserInGroup("admin");
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $iscreator = $dbh->selectrow_array("SELECT creator = ? FROM series " .
|
||||
"WHERE series_id = ?", undef,
|
||||
$::userid, $series_id);
|
||||
$iscreator || ThrowUserError("illegal_series_edit");
|
||||
}
|
||||
|
||||
# Check if the user is permitted to create this series with these parameters.
|
||||
sub assertCanCreate {
|
||||
my ($cgi) = shift;
|
||||
|
||||
UserInGroup("editbugs") || ThrowUserError("illegal_series_creation");
|
||||
|
||||
# Check permission for frequency
|
||||
my $min_freq = 7;
|
||||
if ($cgi->param('frequency') < $min_freq && !UserInGroup("admin")) {
|
||||
ThrowUserError("illegal_frequency", { 'minimum' => $min_freq });
|
||||
}
|
||||
}
|
||||
|
||||
sub validateWidthAndHeight {
|
||||
$vars->{'width'} = $cgi->param('width');
|
||||
$vars->{'height'} = $cgi->param('height');
|
||||
|
||||
if (defined($vars->{'width'})) {
|
||||
(detaint_natural($vars->{'width'}) && $vars->{'width'} > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
}
|
||||
|
||||
if (defined($vars->{'height'})) {
|
||||
(detaint_natural($vars->{'height'}) && $vars->{'height'} > 0)
|
||||
|| ThrowCodeError("invalid_dimensions");
|
||||
}
|
||||
|
||||
# The equivalent of 2000 square seems like a very reasonable maximum size.
|
||||
# This is merely meant to prevent accidental or deliberate DOS, and should
|
||||
# have no effect in practice.
|
||||
if ($vars->{'width'} && $vars->{'height'}) {
|
||||
(($vars->{'width'} * $vars->{'height'}) <= 4000000)
|
||||
|| ThrowUserError("chart_too_large");
|
||||
}
|
||||
}
|
||||
|
||||
sub edit {
|
||||
my $series = shift;
|
||||
|
||||
$vars->{'category'} = Bugzilla::Chart::getVisibleSeries();
|
||||
$vars->{'creator'} = new Bugzilla::User($series->{'creator'});
|
||||
$vars->{'default'} = $series;
|
||||
|
||||
print "Content-Type: text/html\n\n";
|
||||
$template->process("reports/edit-series.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
sub plot {
|
||||
validateWidthAndHeight();
|
||||
$vars->{'chart'} = new Bugzilla::Chart($cgi);
|
||||
|
||||
my $format = &::GetFormat("reports/chart",
|
||||
"",
|
||||
$cgi->param('ctype'));
|
||||
|
||||
# Debugging PNGs is a pain; we need to be able to see the error messages
|
||||
if ($cgi->param('debug')) {
|
||||
print "Content-Type: text/html\n\n";
|
||||
$vars->{'chart'}->dump();
|
||||
}
|
||||
|
||||
print "Content-Type: $format->{'ctype'}\n\n";
|
||||
$template->process($format->{'template'}, $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
sub wrap {
|
||||
validateWidthAndHeight();
|
||||
|
||||
# We create a Chart object so we can validate the parameters
|
||||
my $chart = new Bugzilla::Chart($cgi);
|
||||
|
||||
$vars->{'time'} = time();
|
||||
|
||||
$vars->{'imagebase'} = $cgi->canonicalise_query(
|
||||
"action", "action-wrap", "ctype", "format", "width", "height",
|
||||
"Bugzilla_login", "Bugzilla_password");
|
||||
|
||||
print "Content-Type:text/html\n\n";
|
||||
$template->process("reports/chart.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
|
||||
sub view {
|
||||
my $chart = shift;
|
||||
|
||||
# Set defaults
|
||||
foreach my $field ('category', 'subcategory', 'name', 'ctype') {
|
||||
$vars->{'default'}{$field} = $cgi->param($field) || 0;
|
||||
}
|
||||
|
||||
# Pass the state object to the display UI.
|
||||
$vars->{'chart'} = $chart;
|
||||
$vars->{'category'} = Bugzilla::Chart::getVisibleSeries();
|
||||
|
||||
print "Content-Type: text/html\n\n";
|
||||
|
||||
# If we have having problems with bad data, we can set debug=1 to dump
|
||||
# the data structure.
|
||||
$chart->dump() if $cgi->param('debug');
|
||||
|
||||
$template->process("reports/create-chart.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/perl -wT
|
||||
#!/usr/bonsaitools/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
@@ -19,8 +19,8 @@
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
use lib qw(.);
|
||||
@@ -32,38 +32,57 @@ use vars qw(
|
||||
$vars
|
||||
);
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
require "CGI.pl";
|
||||
|
||||
Bugzilla->login();
|
||||
# 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;
|
||||
|
||||
GetVersionTable();
|
||||
# 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,
|
||||
});
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
# 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
|
||||
# they get displayed in.
|
||||
my @masterlist = ("opendate", "changeddate", "bug_severity", "priority",
|
||||
"rep_platform", "assigned_to", "assigned_to_realname",
|
||||
"reporter", "reporter_realname", "bug_status",
|
||||
"resolution");
|
||||
|
||||
if (Param("useclassification")) {
|
||||
push(@masterlist, "classification");
|
||||
}
|
||||
ConnectToDatabase();
|
||||
GetVersionTable();
|
||||
|
||||
push(@masterlist, ("product", "component", "version", "op_sys", "votes"));
|
||||
my @masterlist = ("opendate", "changeddate", "severity", "priority",
|
||||
"platform", "owner", "reporter", "status", "resolution",
|
||||
"product", "component", "version", "os", "votes");
|
||||
|
||||
if (Param("usebugaliases")) {
|
||||
unshift(@masterlist, "alias");
|
||||
}
|
||||
if (Param("usetargetmilestone")) {
|
||||
push(@masterlist, "target_milestone");
|
||||
}
|
||||
if (Param("useqacontact")) {
|
||||
push(@masterlist, "qa_contact");
|
||||
push(@masterlist, "qa_contact_realname");
|
||||
}
|
||||
if (Param("usestatuswhiteboard")) {
|
||||
push(@masterlist, "status_whiteboard");
|
||||
@@ -72,70 +91,66 @@ if (@::legal_keywords) {
|
||||
push(@masterlist, "keywords");
|
||||
}
|
||||
|
||||
if (UserInGroup(Param("timetrackinggroup"))) {
|
||||
push(@masterlist, ("estimated_time", "remaining_time", "actual_time",
|
||||
"percentage_complete"));
|
||||
}
|
||||
|
||||
push(@masterlist, ("short_desc", "short_short_desc"));
|
||||
push(@masterlist, ("summary", "summaryfull"));
|
||||
|
||||
$vars->{'masterlist'} = \@masterlist;
|
||||
$vars->{masterlist} = \@masterlist;
|
||||
|
||||
my @collist;
|
||||
if (defined $cgi->param('rememberedquery')) {
|
||||
if (defined $::FORM{'rememberedquery'}) {
|
||||
my $splitheader = 0;
|
||||
if (defined $cgi->param('resetit')) {
|
||||
if (defined $::FORM{'resetit'}) {
|
||||
@collist = @::default_column_list;
|
||||
} else {
|
||||
foreach my $i (@masterlist) {
|
||||
if (defined $cgi->param("column_$i")) {
|
||||
if (defined $::FORM{"column_$i"}) {
|
||||
push @collist, $i;
|
||||
}
|
||||
}
|
||||
if (defined $cgi->param('splitheader')) {
|
||||
$splitheader = $cgi->param('splitheader');
|
||||
if (exists $::FORM{'splitheader'}) {
|
||||
$splitheader = $::FORM{'splitheader'};
|
||||
}
|
||||
}
|
||||
my $list = join(" ", @collist);
|
||||
my $urlbase = Param("urlbase");
|
||||
|
||||
$cgi->send_cookie(-name => 'COLUMNLIST',
|
||||
-value => $list,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
$cgi->send_cookie(-name => 'SPLITHEADER',
|
||||
-value => $cgi->param('splitheader'),
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
|
||||
$vars->{'message'} = "change_columns";
|
||||
$vars->{'redirect_url'} = "buglist.cgi?".$cgi->param('rememberedquery');
|
||||
|
||||
# If we're running on Microsoft IIS, using cgi->redirect discards
|
||||
# the Set-Cookie lines -- workaround is to use the old-fashioned
|
||||
# redirection mechanism. See bug 214466 for details.
|
||||
if ($ENV{'SERVER_SOFTWARE'} =~ /Microsoft-IIS/) {
|
||||
print $cgi->header(-type => "text/html",
|
||||
-refresh => "0; URL=$vars->{'redirect_url'}");
|
||||
} else {
|
||||
print $cgi->redirect($vars->{'redirect_url'});
|
||||
}
|
||||
|
||||
$template->process("global/message.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
my $cookiepath = Param("cookiepath");
|
||||
print "Set-Cookie: COLUMNLIST=$list ; path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
|
||||
print "Set-Cookie: SPLITHEADER=$::FORM{'splitheader'} ; path=$cookiepath ; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
|
||||
print "Refresh: 0; URL=buglist.cgi?$::FORM{'rememberedquery'}\n";
|
||||
print "\n";
|
||||
print "<META HTTP-EQUIV=Refresh CONTENT=\"1; URL=$urlbase"."buglist.cgi?$::FORM{'rememberedquery'}\">\n";
|
||||
print "<TITLE>What a hack.</TITLE>\n";
|
||||
PutHeader ("Change columns");
|
||||
print "Resubmitting your query with new columns...\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if (defined $cgi->cookie('COLUMNLIST')) {
|
||||
@collist = split(/ /, $cgi->cookie('COLUMNLIST'));
|
||||
if (defined $::COOKIE{'COLUMNLIST'}) {
|
||||
@collist = split(/ /, $::COOKIE{'COLUMNLIST'});
|
||||
} else {
|
||||
@collist = @::default_column_list;
|
||||
}
|
||||
|
||||
$vars->{'collist'} = \@collist;
|
||||
$vars->{'splitheader'} = $cgi->cookie('SPLITHEADER') ? 1 : 0;
|
||||
$vars->{collist} = \@collist;
|
||||
|
||||
$vars->{'buffer'} = $::buffer;
|
||||
$vars->{splitheader} = 0;
|
||||
if ($::COOKIE{'SPLITHEADER'}) {
|
||||
$vars->{splitheader} = 1;
|
||||
}
|
||||
|
||||
my %desc = ();
|
||||
foreach my $i (@masterlist) {
|
||||
$desc{$i} = $i;
|
||||
}
|
||||
|
||||
$desc{'summary'} = "Summary (first 60 characters)";
|
||||
$desc{'summaryfull'} = "Full Summary";
|
||||
|
||||
$vars->{desc} = \%desc;
|
||||
$vars->{buffer} = $::buffer;
|
||||
|
||||
# Generate and return the UI (HTML page) from the appropriate template.
|
||||
print $cgi->header();
|
||||
print "Content-type: text/html\n\n";
|
||||
$template->process("list/change-columns.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/perl -w
|
||||
#!/usr/bonsaitools/bin/perl -w
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
@@ -20,105 +20,46 @@
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>,
|
||||
# Harrison Page <harrison@netscape.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Richard Walters <rwalters@qualcomm.com>
|
||||
# Jean-Sebastien Guay <jean_seb@hybride.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
|
||||
# Run me out of cron at midnight to collect Bugzilla statistics.
|
||||
#
|
||||
# To run new charts for a specific date, pass it in on the command line in
|
||||
# ISO (2004-08-14) format.
|
||||
|
||||
|
||||
use AnyDBM_File;
|
||||
use diagnostics;
|
||||
use strict;
|
||||
use IO::Handle;
|
||||
use vars @::legal_product;
|
||||
|
||||
use lib ".";
|
||||
require "globals.pl";
|
||||
use Bugzilla::Search;
|
||||
use Bugzilla::User;
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Config qw(:DEFAULT $datadir);
|
||||
|
||||
# Turn off output buffering (probably needed when displaying output feedback
|
||||
# in the regenerate mode.)
|
||||
$| = 1;
|
||||
|
||||
# Tidy up after graphing module
|
||||
# tidy up after graphing module
|
||||
if (chdir("graphs")) {
|
||||
unlink <./*.gif>;
|
||||
unlink <./*.png>;
|
||||
chdir("..");
|
||||
}
|
||||
|
||||
ConnectToDatabase(1);
|
||||
GetVersionTable();
|
||||
|
||||
Bugzilla->switch_to_shadow_db();
|
||||
|
||||
# To recreate the daily statistics, run "collectstats.pl --regenerate" .
|
||||
my $regenerate = 0;
|
||||
if ($#ARGV >= 0 && $ARGV[0] eq "--regenerate") {
|
||||
shift(@ARGV);
|
||||
$regenerate = 1;
|
||||
}
|
||||
|
||||
my @myproducts;
|
||||
push( @myproducts, "-All-", @::legal_product );
|
||||
|
||||
my $tstart = time;
|
||||
foreach (@myproducts) {
|
||||
my $dir = "$datadir/mining";
|
||||
my $dir = "data/mining";
|
||||
|
||||
&check_data_dir ($dir);
|
||||
|
||||
if ($regenerate) {
|
||||
®enerate_stats($dir, $_);
|
||||
} else {
|
||||
&collect_stats($dir, $_);
|
||||
}
|
||||
&collect_stats ($dir, $_);
|
||||
}
|
||||
my $tend = time;
|
||||
# Uncomment the following line for performance testing.
|
||||
#print "Total time taken " . delta_time($tstart, $tend) . "\n";
|
||||
|
||||
&calculate_dupes();
|
||||
|
||||
CollectSeriesData();
|
||||
|
||||
{
|
||||
local $ENV{'GATEWAY_INTERFACE'} = 'cmdline';
|
||||
local $ENV{'REQUEST_METHOD'} = 'GET';
|
||||
local $ENV{'QUERY_STRING'} = 'ctype=rdf';
|
||||
|
||||
my $perl = $^X;
|
||||
trick_taint($perl);
|
||||
|
||||
# Generate a static RDF file containing the default view of the duplicates data.
|
||||
open(CGI, "$perl -T duplicates.cgi |")
|
||||
|| die "can't fork duplicates.cgi: $!";
|
||||
open(RDF, ">$datadir/duplicates.tmp")
|
||||
|| die "can't write to $datadir/duplicates.tmp: $!";
|
||||
my $headers_done = 0;
|
||||
while (<CGI>) {
|
||||
print RDF if $headers_done;
|
||||
$headers_done = 1 if $_ eq "\n";
|
||||
}
|
||||
close CGI;
|
||||
close RDF;
|
||||
}
|
||||
if (-s "$datadir/duplicates.tmp") {
|
||||
rename("$datadir/duplicates.rdf", "$datadir/duplicates-old.rdf");
|
||||
rename("$datadir/duplicates.tmp", "$datadir/duplicates.rdf");
|
||||
}
|
||||
|
||||
sub check_data_dir {
|
||||
my $dir = shift;
|
||||
|
||||
if (! -d $dir) {
|
||||
mkdir $dir, 0755;
|
||||
chmod 0755, $dir;
|
||||
if (! -d) {
|
||||
mkdir $dir, 0777;
|
||||
chmod 0777, $dir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +67,6 @@ sub collect_stats {
|
||||
my $dir = shift;
|
||||
my $product = shift;
|
||||
my $when = localtime (time);
|
||||
my $product_id = get_product_id($product) unless $product eq '-All-';
|
||||
|
||||
die "Unknown product $product" unless ($product_id or $product eq '-All-');
|
||||
|
||||
# NB: Need to mangle the product for the filename, but use the real
|
||||
# product name in the query
|
||||
@@ -142,9 +80,9 @@ sub collect_stats {
|
||||
|
||||
foreach my $status ('NEW', 'ASSIGNED', 'REOPENED', 'UNCONFIRMED', 'RESOLVED', 'VERIFIED', 'CLOSED') {
|
||||
if( $product eq "-All-" ) {
|
||||
SendSQL("SELECT COUNT(bug_status) FROM bugs WHERE bug_status='$status'");
|
||||
SendSQL("select count(bug_status) from bugs where bug_status='$status'");
|
||||
} else {
|
||||
SendSQL("SELECT COUNT(bug_status) FROM bugs WHERE bug_status='$status' AND product_id=$product_id");
|
||||
SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'");
|
||||
}
|
||||
|
||||
push @row, FetchOneColumn();
|
||||
@@ -152,9 +90,9 @@ sub collect_stats {
|
||||
|
||||
foreach my $resolution ('FIXED', 'INVALID', 'WONTFIX', 'LATER', 'REMIND', 'DUPLICATE', 'WORKSFORME', 'MOVED') {
|
||||
if( $product eq "-All-" ) {
|
||||
SendSQL("SELECT COUNT(resolution) FROM bugs WHERE resolution='$resolution'");
|
||||
SendSQL("select count(resolution) from bugs where resolution='$resolution'");
|
||||
} else {
|
||||
SendSQL("SELECT COUNT(resolution) FROM bugs WHERE resolution='$resolution' AND product_id=$product_id");
|
||||
SendSQL("select count(resolution) from bugs where resolution='$resolution' and product='$product'");
|
||||
}
|
||||
|
||||
push @row, FetchOneColumn();
|
||||
@@ -174,7 +112,6 @@ FIN
|
||||
|
||||
print DATA (join '|', @row) . "\n";
|
||||
close DATA;
|
||||
chmod 0644, $file;
|
||||
} else {
|
||||
print "$0: $file, $!";
|
||||
}
|
||||
@@ -194,12 +131,11 @@ sub calculate_dupes {
|
||||
# Save % count here in a date-named file
|
||||
# so we can read it back in to do changed counters
|
||||
# First, delete it if it exists, so we don't add to the contents of an old file
|
||||
if (my @files = <$datadir/duplicates/dupes$today*>) {
|
||||
map { trick_taint($_) } @files;
|
||||
if (my @files = <data/duplicates/dupes$today*>) {
|
||||
unlink @files;
|
||||
}
|
||||
|
||||
dbmopen(%count, "$datadir/duplicates/dupes$today", 0644) || die "Can't open DBM dupes file: $!";
|
||||
dbmopen(%count, "data/duplicates/dupes$today", 0644) || die "Can't open DBM dupes file: $!";
|
||||
|
||||
# Create a hash with key "a bug number", value "bug which that bug is a
|
||||
# direct dupe of" - straight from the duplicates table.
|
||||
@@ -254,165 +190,6 @@ sub calculate_dupes {
|
||||
dbmclose(%count);
|
||||
}
|
||||
|
||||
# This regenerates all statistics from the database.
|
||||
sub regenerate_stats {
|
||||
my $dir = shift;
|
||||
my $product = shift;
|
||||
my $when = localtime(time());
|
||||
|
||||
my $tstart = time();
|
||||
|
||||
# NB: Need to mangle the product for the filename, but use the real
|
||||
# product name in the query
|
||||
my $file_product = $product;
|
||||
$file_product =~ s/\//-/gs;
|
||||
my $file = join '/', $dir, $file_product;
|
||||
|
||||
my @bugs;
|
||||
|
||||
my $and_product = "";
|
||||
my $from_product = "";
|
||||
|
||||
if ($product ne '-All-') {
|
||||
$and_product = "AND bugs.product_id = products.id " .
|
||||
"AND products.name = " . SqlQuote($product) . " ";
|
||||
$from_product = ", products";
|
||||
}
|
||||
|
||||
# Determine the start date from the date the first bug in the
|
||||
# database was created, and the end date from the current day.
|
||||
# If there were no bugs in the search, return early.
|
||||
SendSQL("SELECT to_days(creation_ts) AS start, " .
|
||||
"to_days(current_date) AS end, " .
|
||||
"to_days('1970-01-01') " .
|
||||
"FROM bugs $from_product WHERE to_days(creation_ts) != 'NULL' " .
|
||||
$and_product .
|
||||
"ORDER BY start LIMIT 1");
|
||||
|
||||
my ($start, $end, $base) = FetchSQLData();
|
||||
if (!defined $start) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (open DATA, ">$file") {
|
||||
DATA->autoflush(1);
|
||||
print DATA <<FIN;
|
||||
# Bugzilla Daily Bug Stats
|
||||
#
|
||||
# Do not edit me! This file is generated.
|
||||
#
|
||||
# fields: DATE|NEW|ASSIGNED|REOPENED|UNCONFIRMED|RESOLVED|VERIFIED|CLOSED|FIXED|INVALID|WONTFIX|LATER|REMIND|DUPLICATE|WORKSFORME|MOVED
|
||||
# Product: $product
|
||||
# Created: $when
|
||||
FIN
|
||||
# For each day, generate a line of statistics.
|
||||
my $total_days = $end - $start;
|
||||
for (my $day = $start + 1; $day <= $end; $day++) {
|
||||
# Some output feedback
|
||||
my $percent_done = ($day - $start - 1) * 100 / $total_days;
|
||||
printf "\rRegenerating $product \[\%.1f\%\%]", $percent_done;
|
||||
|
||||
# Get a list of bugs that were created the previous day, and
|
||||
# add those bugs to the list of bugs for this product.
|
||||
SendSQL("SELECT bug_id FROM bugs $from_product " .
|
||||
"WHERE bugs.creation_ts < from_days(" . ($day - 1) . ") " .
|
||||
"AND bugs.creation_ts >= from_days(" . ($day - 2) . ") " .
|
||||
$and_product .
|
||||
" ORDER BY bug_id");
|
||||
|
||||
my @row;
|
||||
while (@row = FetchSQLData()) {
|
||||
push @bugs, $row[0];
|
||||
}
|
||||
|
||||
# For each bug that existed on that day, determine its status
|
||||
# at the beginning of the day. If there were no status
|
||||
# changes on or after that day, the status was the same as it
|
||||
# is today, which can be found in the bugs table. Otherwise,
|
||||
# the status was equal to the first "previous value" entry in
|
||||
# the bugs_activity table for that bug made on or after that
|
||||
# day.
|
||||
my %bugcount;
|
||||
my @logstates = qw(NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED
|
||||
VERIFIED CLOSED);
|
||||
my @logresolutions = qw(FIXED INVALID WONTFIX LATER REMIND
|
||||
DUPLICATE WORKSFORME MOVED);
|
||||
foreach (@logstates) {
|
||||
$bugcount{$_} = 0;
|
||||
}
|
||||
|
||||
foreach (@logresolutions) {
|
||||
$bugcount{$_} = 0;
|
||||
}
|
||||
|
||||
for my $bug (@bugs) {
|
||||
# First, get information on various bug states.
|
||||
SendSQL("SELECT bugs_activity.removed " .
|
||||
"FROM bugs_activity,fielddefs " .
|
||||
"WHERE bugs_activity.fieldid = fielddefs.fieldid " .
|
||||
"AND fielddefs.name = 'bug_status' " .
|
||||
"AND bugs_activity.bug_id = $bug " .
|
||||
"AND bugs_activity.bug_when >= from_days($day) " .
|
||||
"ORDER BY bugs_activity.bug_when LIMIT 1");
|
||||
|
||||
my $status;
|
||||
if (@row = FetchSQLData()) {
|
||||
$status = $row[0];
|
||||
} else {
|
||||
SendSQL("SELECT bug_status FROM bugs WHERE bug_id = $bug");
|
||||
$status = FetchOneColumn();
|
||||
}
|
||||
|
||||
if (defined $bugcount{$status}) {
|
||||
$bugcount{$status}++;
|
||||
}
|
||||
|
||||
# Next, get information on various bug resolutions.
|
||||
SendSQL("SELECT bugs_activity.removed " .
|
||||
"FROM bugs_activity,fielddefs " .
|
||||
"WHERE bugs_activity.fieldid = fielddefs.fieldid " .
|
||||
"AND fielddefs.name = 'resolution' " .
|
||||
"AND bugs_activity.bug_id = $bug " .
|
||||
"AND bugs_activity.bug_when >= from_days($day) " .
|
||||
"ORDER BY bugs_activity.bug_when LIMIT 1");
|
||||
|
||||
if (@row = FetchSQLData()) {
|
||||
$status = $row[0];
|
||||
} else {
|
||||
SendSQL("SELECT resolution FROM bugs WHERE bug_id = $bug");
|
||||
$status = FetchOneColumn();
|
||||
}
|
||||
|
||||
if (defined $bugcount{$status}) {
|
||||
$bugcount{$status}++;
|
||||
}
|
||||
}
|
||||
|
||||
# Generate a line of output containing the date and counts
|
||||
# of bugs in each state.
|
||||
my $date = sqlday($day, $base);
|
||||
print DATA "$date";
|
||||
foreach (@logstates) {
|
||||
print DATA "|$bugcount{$_}";
|
||||
}
|
||||
|
||||
foreach (@logresolutions) {
|
||||
print DATA "|$bugcount{$_}";
|
||||
}
|
||||
|
||||
print DATA "\n";
|
||||
}
|
||||
|
||||
# Finish up output feedback for this product.
|
||||
my $tend = time;
|
||||
print "\rRegenerating $product \[100.0\%] - " .
|
||||
delta_time($tstart, $tend) . "\n";
|
||||
|
||||
close DATA;
|
||||
chmod 0640, $file;
|
||||
}
|
||||
}
|
||||
|
||||
sub today {
|
||||
my ($dom, $mon, $year) = (localtime(time))[3, 4, 5];
|
||||
return sprintf "%04d%02d%02d", 1900 + $year, ++$mon, $dom;
|
||||
@@ -423,86 +200,3 @@ sub today_dash {
|
||||
return sprintf "%04d-%02d-%02d", 1900 + $year, ++$mon, $dom;
|
||||
}
|
||||
|
||||
sub sqlday {
|
||||
my ($day, $base) = @_;
|
||||
$day = ($day - $base) * 86400;
|
||||
my ($dom, $mon, $year) = (gmtime($day))[3, 4, 5];
|
||||
return sprintf "%04d%02d%02d", 1900 + $year, ++$mon, $dom;
|
||||
}
|
||||
|
||||
sub delta_time {
|
||||
my $tstart = shift;
|
||||
my $tend = shift;
|
||||
my $delta = $tend - $tstart;
|
||||
my $hours = int($delta/3600);
|
||||
my $minutes = int($delta/60) - ($hours * 60);
|
||||
my $seconds = $delta - ($minutes * 60) - ($hours * 3600);
|
||||
return sprintf("%02d:%02d:%02d" , $hours, $minutes, $seconds);
|
||||
}
|
||||
|
||||
sub CollectSeriesData {
|
||||
# We need some way of randomising the distribution of series, such that
|
||||
# all of the series which are to be run every 7 days don't run on the same
|
||||
# day. This is because this might put the server under severe load if a
|
||||
# particular frequency, such as once a week, is very common. We achieve
|
||||
# this by only running queries when:
|
||||
# (days_since_epoch + series_id) % frequency = 0. So they'll run every
|
||||
# <frequency> days, but the start date depends on the series_id.
|
||||
my $days_since_epoch = int(time() / (60 * 60 * 24));
|
||||
my $today = $ARGV[0] || today_dash();
|
||||
|
||||
# We save a copy of the main $dbh and then switch to the shadow and get
|
||||
# that one too. Remember, these may be the same.
|
||||
Bugzilla->switch_to_main_db();
|
||||
my $dbh = Bugzilla->dbh;
|
||||
Bugzilla->switch_to_shadow_db();
|
||||
my $shadow_dbh = Bugzilla->dbh;
|
||||
|
||||
my $serieses = $dbh->selectall_hashref("SELECT series_id, query, creator " .
|
||||
"FROM series " .
|
||||
"WHERE frequency != 0 AND " .
|
||||
"($days_since_epoch + series_id) % frequency = 0",
|
||||
"series_id");
|
||||
|
||||
# We prepare the insertion into the data table, for efficiency.
|
||||
my $sth = $dbh->prepare("INSERT INTO series_data " .
|
||||
"(series_id, series_date, series_value) " .
|
||||
"VALUES (?, " . $dbh->quote($today) . ", ?)");
|
||||
|
||||
# We delete from the table beforehand, to avoid SQL errors if people run
|
||||
# collectstats.pl twice on the same day.
|
||||
my $deletesth = $dbh->prepare("DELETE FROM series_data
|
||||
WHERE series_id = ? AND series_date = " .
|
||||
$dbh->quote($today));
|
||||
|
||||
foreach my $series_id (keys %$serieses) {
|
||||
# We set up the user for Search.pm's permission checking - each series
|
||||
# runs with the permissions of its creator.
|
||||
my $user = new Bugzilla::User($serieses->{$series_id}->{'creator'});
|
||||
|
||||
my $cgi = new Bugzilla::CGI($serieses->{$series_id}->{'query'});
|
||||
my $search = new Bugzilla::Search('params' => $cgi,
|
||||
'fields' => ["bugs.bug_id"],
|
||||
'user' => $user);
|
||||
my $sql = $search->getSQL();
|
||||
|
||||
my $data;
|
||||
|
||||
# We can't die if we get dodgy SQL back for whatever reason, so we
|
||||
# eval() this and, if it fails, just ignore it and carry on.
|
||||
# One day we might even log an error.
|
||||
eval {
|
||||
$data = $shadow_dbh->selectall_arrayref($sql);
|
||||
};
|
||||
|
||||
if (!$@) {
|
||||
# We need to count the returned rows. Without subselects, we can't
|
||||
# do this directly in the SQL for all queries. So we do it by hand.
|
||||
my $count = scalar(@$data) || 0;
|
||||
|
||||
$deletesth->execute($series_id);
|
||||
$sth->execute($series_id, $count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/usr/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Myk Melez <myk@mozilla.org>
|
||||
|
||||
################################################################################
|
||||
# Script Initialization
|
||||
################################################################################
|
||||
|
||||
# Make it harder for us to do dangerous things in Perl.
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
# Include the Bugzilla CGI and general utility library.
|
||||
use lib qw(.);
|
||||
require "CGI.pl";
|
||||
|
||||
# Retrieve this installation's configuration.
|
||||
GetVersionTable();
|
||||
|
||||
# Suppress "used only once" warnings.
|
||||
use vars
|
||||
qw(
|
||||
@legal_priority
|
||||
@legal_severity
|
||||
@legal_platform
|
||||
@legal_opsys
|
||||
@legal_resolution
|
||||
|
||||
@legal_components
|
||||
@legal_target_milestone
|
||||
@legal_versions
|
||||
@legal_keywords
|
||||
);
|
||||
|
||||
# Use the global template variables defined in globals.pl
|
||||
# to generate the output.
|
||||
use vars qw($template $vars);
|
||||
|
||||
# Pass a bunch of Bugzilla configuration to the templates.
|
||||
$vars->{'priority'} = \@::legal_priority;
|
||||
$vars->{'severity'} = \@::legal_severity;
|
||||
$vars->{'platform'} = \@::legal_platform;
|
||||
$vars->{'op_sys'} = \@::legal_opsys;
|
||||
$vars->{'keyword'} = \@::legal_keywords;
|
||||
$vars->{'resolution'} = \@::legal_resolution;
|
||||
$vars->{'status'} = \@::legal_bug_status;
|
||||
|
||||
# Include lists of products, components, versions, and target milestones.
|
||||
my $selectables = GetSelectableProductHash();
|
||||
foreach my $selectable (keys %$selectables) {
|
||||
$vars->{$selectable} = $selectables->{$selectable};
|
||||
}
|
||||
|
||||
# Create separate lists of open versus resolved statuses. This should really
|
||||
# be made part of the configuration.
|
||||
my @open_status;
|
||||
my @closed_status;
|
||||
foreach my $status (@::legal_bug_status) {
|
||||
IsOpenedState($status) ? push(@open_status, $status)
|
||||
: push(@closed_status, $status);
|
||||
}
|
||||
$vars->{'open_status'} = \@open_status;
|
||||
$vars->{'closed_status'} = \@closed_status;
|
||||
|
||||
# Generate a list of fields that can be queried.
|
||||
$vars->{'field'} = [GetFieldDefs()];
|
||||
|
||||
# Determine how the user would like to receive the output;
|
||||
# default is JavaScript.
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $format = GetFormat("config", scalar($cgi->param('format')),
|
||||
scalar($cgi->param('ctype')) || "js");
|
||||
|
||||
# Return HTTP headers.
|
||||
print "Content-Type: $format->{'ctype'}\n\n";
|
||||
|
||||
# Generate the configuration file and return it to the user.
|
||||
$template->process($format->{'template'}, $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
168
mozilla/webtools/bugzilla/confirmhelp.html
Normal file
168
mozilla/webtools/bugzilla/confirmhelp.html
Normal file
@@ -0,0 +1,168 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html><head>
|
||||
|
||||
<!--
|
||||
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) 2000 Netscape Communications Corporation. All
|
||||
Rights Reserved.
|
||||
|
||||
Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
-->
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<title>Understanding the UNCONFIRMED state, and other recent changes</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Understanding the UNCONFIRMED state, and other recent changes</h1>
|
||||
|
||||
<p>
|
||||
[This document is aimed primarily at people who have used Bugzilla
|
||||
before the UNCONFIRMED state was implemented. It might be helpful for
|
||||
newer users as well.]
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
New bugs in some products will now show up in a new state,
|
||||
UNCONFIRMED. This means that we have nobody has confirmed that the
|
||||
bug is real. Very busy engineers will probably generally ignore
|
||||
UNCONFIRMED that have been assigned to them, until they have been
|
||||
confirmed in one way or another. (Engineers with more time will
|
||||
hopefully glance over their UNCONFIRMED bugs regularly.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <a href="bug_status.html">page describing bug fields</a> has been
|
||||
updated to include UNCONFIRMED.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There are two basic ways that a bug can become confirmed (and enter
|
||||
the NEW) state.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li> A user with the appropriate permissions (see below for more on
|
||||
permissions) decides that the bug is a valid one, and confirms
|
||||
it. We hope to gather a small army of responsible volunteers
|
||||
to regularly go through bugs for us.</li>
|
||||
<li> The bug gathers a certain number of votes. <b>Any</b> valid Bugzilla user may vote for
|
||||
bugs (each user gets a certain number of bugs); any UNCONFIRMED bug which
|
||||
gets enough votes becomes automatically confirmed, and enters the NEW state.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
One implication of this is that it is worth your time to search the
|
||||
bug system for duplicates of your bug to vote on them, before
|
||||
submitting your own bug. If we can spread around knowledge of this
|
||||
fact, it ought to help cut down the number of duplicate bugs in the
|
||||
system.
|
||||
</p>
|
||||
|
||||
<h2>Permissions.</h2>
|
||||
|
||||
<p>
|
||||
Users now have a certain set of permissions. To see your permissions,
|
||||
check out the
|
||||
<a href="userprefs.cgi?bank=permissions">user preferences</a> page.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have the "Can confirm a bug" permission, then you will be able
|
||||
to move UNCONFIRMED bugs into the NEW state.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have the "Can edit all aspects of any bug" permission, then you
|
||||
can tweak anything about any bug. If not, you may only edit those
|
||||
bugs that you have submitted, or that you have assigned to you (or
|
||||
qa-assigned to you). However, anyone may add a comment to any bug.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some people (initially, the initial owners and initial qa-contacts for
|
||||
components in the system) have the ability to give the above two
|
||||
permissions to other people. So, if you really feel that you ought to
|
||||
have one of these permissions, a good person to ask (via private
|
||||
email, please!) is the person who is assigned a relevant bug.
|
||||
</p>
|
||||
|
||||
<h2>Other details.</h2>
|
||||
|
||||
<p>
|
||||
An initial stab was taken to decide who would be given which of the
|
||||
above permissions. This was determined by some simple heurstics of
|
||||
who was assigned bugs, and who the default owners of bugs were, and a
|
||||
look at people who seem to have submitted several bugs that appear to
|
||||
have been interesting and valid. Inevitably, we have failed to give
|
||||
someone the permissions they deserve. Please don't take it
|
||||
personally; just bear with us as we shake out the new system.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
People with one of the two bits above can easily confirm their own
|
||||
bugs, so bugs they submit will actually start out in the NEW state.
|
||||
They can override this when submitting a bug.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
People can ACCEPT or RESOLVE a bug assigned to them, even if they
|
||||
aren't allowed to confirm it. However, the system remembers, and if
|
||||
the bug gets REOPENED or reassigned to someone else, it will revert
|
||||
back to the UNCONFIRMED state. If the bug has ever been confirmed,
|
||||
then REOPENing or reassigning will cause it to go to the NEW or
|
||||
REOPENED state.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that only some products support the UNCONFIRMED state. In other
|
||||
products, all new bugs will automatically start in the NEW state.
|
||||
</p>
|
||||
|
||||
<h2>Things still to be done.</h2>
|
||||
|
||||
<p>
|
||||
There probably ought to be a way to get a bug back into the
|
||||
UNCONFIRMED state, but there isn't yet.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If a person has submitted several bugs that get confirmed, then this
|
||||
is probably a person who understands the system well, and deserves the
|
||||
"Can confirm a bug" permission. This kind of person should be
|
||||
detected and promoted automatically.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There should also be a way to automatically promote people to get the
|
||||
"Can edit all aspects of any bug" permission.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The "enter a new bug" page needs to be revamped with easy ways for new
|
||||
people to educate themselves on the benefit of searching for a bug
|
||||
like the one they're about to submit and voting on it, rather than
|
||||
adding a new useless duplicate.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<!-- hhmts start -->
|
||||
Last modified: Sun Apr 14 12:55:14 EST 2002
|
||||
<!-- hhmts end -->
|
||||
</p>
|
||||
</body> </html>
|
||||
@@ -29,6 +29,7 @@ push @INC, "../."; # this script now lives in contrib
|
||||
|
||||
require "globals.pl";
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
my $EMAIL_TRANSFORM_NONE = "email_transform_none";
|
||||
|
||||
@@ -3,52 +3,20 @@ Things in here have not necessarily been tested or tried by anyone
|
||||
except the original contributor, so tred carefully. But it may still
|
||||
be useful to you.
|
||||
|
||||
This file is encoded in UTF8 for purposes of contributor names.
|
||||
|
||||
This directory includes:
|
||||
|
||||
bugzilla-submit/ -- A standalone bug submission program.
|
||||
|
||||
mysqld-watcher.pl -- This script can be installed as a frequent cron
|
||||
job to clean up stalled/dead queries.
|
||||
|
||||
sendbugmail.pl -- This script is a drop-in replacement for the
|
||||
'processmail' script which used to be shipped
|
||||
with Bugzilla, but was replaced by the
|
||||
Bugzilla/BugMail.pm Perl module. You can use
|
||||
this script if you were previously calling
|
||||
processmail from other scripts external to
|
||||
Bugzilla. See the comments at the top of
|
||||
the file for usage information. Contributed
|
||||
by Nick Barnes of Ravenbrook Limited.
|
||||
|
||||
gnatsparse/ -- A Python script used to import a GNATS database
|
||||
into Bugzilla.
|
||||
|
||||
gnats2bz.pl -- A perl script to help import bugs from a GNATS
|
||||
database into a Bugzilla database. Contributed by
|
||||
Tom Schutter <tom@platte.com>
|
||||
|
||||
bug_email.pl -- A perl script that can receive email containing
|
||||
bug reports (email-interface). Contributed by
|
||||
Klaas Freitag <freitag@SuSE.de>
|
||||
|
||||
README.Mailif -- Readme describing the mail interface.
|
||||
bugmail_help.html -- User help page for the mail interface.
|
||||
|
||||
yp_nomail.sh -- Script you can run via cron that regularly updates
|
||||
the nomail file for terminated employees
|
||||
|
||||
bugzilla_ldapsync.rb -- Script you can run via cron that queries an LDAP
|
||||
server for e-mail addresses to add Bugzilla users
|
||||
for. Will optionally disable Bugzilla users with
|
||||
no matching LDAP record. Contributed by Thomas
|
||||
Stromberg <thomas+bugzilla@stromberg.org>
|
||||
|
||||
syncLDAP.pl -- Script you can run via cron that queries an LDAP
|
||||
server for users and e-mail addresses and adds
|
||||
missing users to Bugzilla. Can disable/update
|
||||
non-existing/changed information. Contributed by
|
||||
Andreas Höfler <andreas.hoefler@bearingpoint.com>
|
||||
mysqld-watcher.pl -- This script can be installed as a frequent cron
|
||||
job to clean up stalled/dead queries.
|
||||
gnats2bz.pl -- A perl script to help import bugs from a GNATS
|
||||
database into a Bugzilla database. Contributed by
|
||||
Tom Schutter <tom@platte.com>
|
||||
|
||||
bug_email.pl -- A perl script that can receive email containing
|
||||
bug reports (email-interface). Contributed by
|
||||
Klaas Freitag <freitag@SuSE.de>
|
||||
|
||||
README.Mailif -- Readme describing the mail interface.
|
||||
bugmail_help.html -- User help page for the mail interface.
|
||||
|
||||
yp_nomail.sh -- Script you can run via cron that regularly updates
|
||||
the nomail file for terminated employees
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
# Gregor Fischer <fischer@suse.de>
|
||||
# Klaas Freitag <freitag@suse.de>
|
||||
# Seth Landsman <seth@dworkin.net>
|
||||
# Ludovic Dubost <ludovic@pobox.com>
|
||||
###############################################################
|
||||
# Bugzilla: Create a new bug via email
|
||||
###############################################################
|
||||
@@ -38,7 +37,7 @@
|
||||
#
|
||||
# You need to work with bug_email.pl the MIME::Parser installed.
|
||||
#
|
||||
# $Id: bug_email.pl,v 1.20 2004-04-09 02:23:09 jocuri%softhome.net Exp $
|
||||
# $Id: bug_email.pl,v 1.9 2001-05-25 12:48:47 jake%acutex.net Exp $
|
||||
###############################################################
|
||||
|
||||
# 02/12/2000 (SML)
|
||||
@@ -59,16 +58,6 @@
|
||||
# any email submitted bug will be entered with a product of PENDING, if no other product is
|
||||
# specified in the email.
|
||||
|
||||
# 10/21/2003 (Ludovic)
|
||||
# - added $DEFAULT_VERSION, similar to product and component above
|
||||
# - added command line switches to override version, product, and component, so separate
|
||||
# email addresses can be used for different product/component/version combinations.
|
||||
# Example for procmail:
|
||||
# # Feed mail to stdin of bug_email.pl
|
||||
# :0 Ec
|
||||
# * !^Subject: .*[Bug .*]
|
||||
# RESULT=|(cd $BUGZILLA_HOME/contrib && ./bug_email.pl -p='Tier_3_Operations' -c='General' )
|
||||
|
||||
# Next round of revisions :
|
||||
# - querying a bug over email
|
||||
# - appending a bug over email
|
||||
@@ -77,23 +66,14 @@
|
||||
# - integrate some setup in the checksetup.pl script
|
||||
# - gpg signatures for security
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
use MIME::Parser;
|
||||
|
||||
BEGIN {
|
||||
chdir '..'; # this script lives in contrib
|
||||
push @INC, "contrib/.";
|
||||
push @INC, ".";
|
||||
}
|
||||
push @INC, "../."; # this script now lives in contrib
|
||||
|
||||
require "globals.pl";
|
||||
use BugzillaEmail;
|
||||
use Bugzilla::Config qw(:DEFAULT $datadir);
|
||||
|
||||
use lib ".";
|
||||
use lib "../";
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::BugMail;
|
||||
require "BugzillaEmail.pm";
|
||||
|
||||
my @mailerrors = (); # Buffer for Errors in the mail
|
||||
my @mailwarnings = (); # Buffer for Warnings found in the mail
|
||||
@@ -114,7 +94,6 @@ my $Message_ID;
|
||||
# change to use default product / component functionality
|
||||
my $DEFAULT_PRODUCT = "PENDING";
|
||||
my $DEFAULT_COMPONENT = "PENDING";
|
||||
my $DEFAULT_VERSION = "unspecified";
|
||||
|
||||
###############################################################
|
||||
# storeAttachments
|
||||
@@ -152,8 +131,8 @@ sub storeAttachments( $$ )
|
||||
print "Error while reading attachment $decoded_file!\n";
|
||||
next;
|
||||
}
|
||||
# print "unlinking $datadir/mimedump-tmp/$decoded_file";
|
||||
# unlink "$datadir/mimedump-tmp/$decoded_file";
|
||||
# print "unlinking data/mimedump-tmp/$decoded_file";
|
||||
# unlink "data/mimedump-tmp/$decoded_file";
|
||||
} else {
|
||||
# data is in the scalar
|
||||
$data = $decoded_file;
|
||||
@@ -215,7 +194,7 @@ sub CheckPermissions {
|
||||
sub CheckProduct {
|
||||
my $Product = shift;
|
||||
|
||||
SendSQL("select name from products where name = " . SqlQuote($Product));
|
||||
SendSQL("select product from products where product='$Product'");
|
||||
my $Result = FetchOneColumn();
|
||||
if (lc($Result) eq lc($Product)) {
|
||||
return $Result;
|
||||
@@ -230,7 +209,7 @@ sub CheckComponent {
|
||||
my $Product = shift;
|
||||
my $Component = shift;
|
||||
|
||||
SendSQL("select components.name from components, products where components.product_id = products.id AND products.name=" . SqlQuote($Product) . " and components.name=" . SqlQuote($Component));
|
||||
SendSQL("select value from components where program=" . SqlQuote($Product) . " and value=" . SqlQuote($Component) . "");
|
||||
my $Result = FetchOneColumn();
|
||||
if (lc($Result) eq lc($Component)) {
|
||||
return $Result;
|
||||
@@ -245,7 +224,7 @@ sub CheckVersion {
|
||||
my $Product = shift;
|
||||
my $Version = shift;
|
||||
|
||||
SendSQL("select value from versions, products where versions.product_id = products.id AND products.name=" . SqlQuote($Product) . " and value=" . SqlQuote($Version));
|
||||
SendSQL("select value from versions where program=" . SqlQuote($Product) . " and value=" . SqlQuote($Version) . "");
|
||||
my $Result = FetchOneColumn();
|
||||
if (lc($Result) eq lc($Version)) {
|
||||
return $Result;
|
||||
@@ -263,7 +242,7 @@ sub Reply( $$$$ ) {
|
||||
die "Cannot find sender-email-address" unless defined( $Sender );
|
||||
|
||||
if( $test ) {
|
||||
open( MAIL, '>>', "$datadir/bug_email_test.log" );
|
||||
open( MAIL, ">>data/bug_email_test.log" );
|
||||
}
|
||||
else {
|
||||
open( MAIL, "| /usr/sbin/sendmail -t" );
|
||||
@@ -575,6 +554,52 @@ END
|
||||
return( $ret );
|
||||
|
||||
}
|
||||
###############################################################
|
||||
# groupBitToString( $ )
|
||||
# converts a given number back to the groupsetting-names
|
||||
# This function accepts single numbers as added bits or
|
||||
# Strings with +-Signs
|
||||
sub groupBitToString( $ )
|
||||
{
|
||||
my ($bits) = @_;
|
||||
my $type;
|
||||
my @bitlist = ();
|
||||
my $ret = "";
|
||||
|
||||
if( $bits =~ /^\d+$/ ) { # only numbers
|
||||
$type = 1;
|
||||
|
||||
} elsif( $bits =~ /^(\s*\d+\s*\+\s*)+/ ) {
|
||||
$type = 2;
|
||||
} else {
|
||||
# Error: unknown format !
|
||||
$type = 0;
|
||||
}
|
||||
|
||||
$bits =~ s/\s*//g;
|
||||
#
|
||||
# Query for groupset-Information
|
||||
SendSQL( "Select Bit,Name, Description from groups where isbuggroup=1" );
|
||||
my @line;
|
||||
while( MoreSQLData() ){
|
||||
@line = FetchSQLData();
|
||||
|
||||
if( $type == 1 ) {
|
||||
if( ((0+$bits) & (0+$line[0])) == 0+$line[0] ) {
|
||||
$ret .= sprintf( "%s ", $line[1] );
|
||||
}
|
||||
} elsif( $type == 2 ) {
|
||||
if( $bits =~ /$line[0]/ ) {
|
||||
$ret .= sprintf( "%s ", $line[1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( $ret );
|
||||
}
|
||||
|
||||
|
||||
|
||||
#------------------------------
|
||||
#
|
||||
# dump_entity ENTITY, NAME
|
||||
@@ -614,8 +639,7 @@ sub dump_entity {
|
||||
if( $msg_part =~ /^attachment/ ) {
|
||||
# Attached File
|
||||
my $des = $entity->head->get('Content-Description');
|
||||
$des ||= $entity->head->recommended_filename;
|
||||
$des ||= "unnamed attachment";
|
||||
$des ||= "";
|
||||
|
||||
if( defined( $body->path )) { # Data is on disk
|
||||
$on_disk = 1;
|
||||
@@ -685,22 +709,6 @@ sub extractControls( $ )
|
||||
foreach( @ARGV ) {
|
||||
$restricted = 1 if ( /-r/ );
|
||||
$test = 1 if ( /-t/ );
|
||||
|
||||
if ( /-p=['"]?(.+)['"]?/ )
|
||||
{
|
||||
$DEFAULT_PRODUCT = $1;
|
||||
}
|
||||
|
||||
if ( /-c=['"]?(.+)["']?/ )
|
||||
{
|
||||
$DEFAULT_COMPONENT = $1;
|
||||
}
|
||||
|
||||
if ( /-v=['"]?(.+)["']?/ )
|
||||
{
|
||||
$DEFAULT_VERSION = $1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#
|
||||
@@ -728,10 +736,10 @@ my $parser = new MIME::Parser;
|
||||
|
||||
# Create and set the output directory:
|
||||
# FIXME: There should be a $BUGZILLA_HOME variable (SML)
|
||||
(-d "$datadir/mimedump-tmp") or mkdir "$datadir/mimedump-tmp",0755 or die "mkdir: $!";
|
||||
(-w "$datadir/mimedump-tmp") or die "can't write to directory";
|
||||
(-d "../data/mimedump-tmp") or mkdir "../data/mimedump-tmp",0755 or die "mkdir: $!";
|
||||
(-w "../data/mimedump-tmp") or die "can't write to directory";
|
||||
|
||||
$parser->output_dir("$datadir/mimedump-tmp");
|
||||
$parser->output_dir("../data/mimedump-tmp");
|
||||
|
||||
# Read the MIME message:
|
||||
my $entity = $parser->read(\*STDIN) or die "couldn't parse MIME stream";
|
||||
@@ -746,15 +754,19 @@ die (" *** Cant find Sender-adress in sent mail ! ***\n" ) unless defined( $Send
|
||||
chomp( $Sender );
|
||||
chomp( $Message_ID );
|
||||
|
||||
ConnectToDatabase();
|
||||
|
||||
$SenderShort = $Sender;
|
||||
$SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/;
|
||||
|
||||
$SenderShort = findUser($SenderShort);
|
||||
|
||||
print "SenderShort is $SenderShort\n";
|
||||
if (!defined($SenderShort)) {
|
||||
$SenderShort = $Sender;
|
||||
$SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/;
|
||||
}
|
||||
print "The sendershort is now $SenderShort\n";
|
||||
|
||||
my $Subject = "";
|
||||
$Subject = $entity->get( 'Subject' );
|
||||
@@ -826,10 +838,10 @@ if (! CheckPermissions("CreateBugs", $SenderShort ) ) {
|
||||
|
||||
# Set QA
|
||||
if (Param("useqacontact")) {
|
||||
SendSQL("select initialqacontact from components, products where components.product_id = products.id AND products.name=" .
|
||||
SendSQL("select initialqacontact from components where program=" .
|
||||
SqlQuote($Control{'product'}) .
|
||||
" and components.name=" . SqlQuote($Control{'component'}));
|
||||
$Control{'qa_contact'} = FetchOneColumn();
|
||||
" and value=" . SqlQuote($Control{'component'}));
|
||||
$Control{'qacontact'} = FetchOneColumn();
|
||||
}
|
||||
|
||||
# Set Assigned - assigned_to depends on the product, cause initialowner
|
||||
@@ -849,7 +861,7 @@ if ( $Product eq "" ) {
|
||||
|
||||
$Text .= "Valid products are:\n\t";
|
||||
|
||||
SendSQL("select name from products ORDER BY name");
|
||||
SendSQL("select product from products");
|
||||
@all_products = FetchAllSQLData();
|
||||
$Text .= join( "\n\t", @all_products ) . "\n\n";
|
||||
$Text .= horLine();
|
||||
@@ -889,7 +901,7 @@ if ( $Component eq "" ) {
|
||||
foreach my $prod ( @all_products ) {
|
||||
$Text .= "\nValid components for product `$prod' are: \n\t";
|
||||
|
||||
SendSQL("SELECT components.name FROM components, products WHERE components.product_id=products.id AND products.name = " . SqlQuote($prod));
|
||||
SendSQL("select value from components where program=" . SqlQuote( $prod ) . "");
|
||||
@val_components = FetchAllSQLData();
|
||||
|
||||
$Text .= join( "\n\t", @val_components ) . "\n";
|
||||
@@ -922,10 +934,9 @@ if ( defined($Control{'assigned_to'})
|
||||
&& $Control{'assigned_to'} !~ /^\s*$/ ) {
|
||||
$Control{'assigned_to'} = DBname_to_id($Control{'assigned_to'});
|
||||
} else {
|
||||
SendSQL("select initialowner from components, products where " .
|
||||
" components.product_id=products.id AND products.name=" .
|
||||
SendSQL("select initialowner from components where program=" .
|
||||
SqlQuote($Control{'product'}) .
|
||||
" and components.name=" . SqlQuote($Control{'component'}));
|
||||
" and value=" . SqlQuote($Control{'component'}));
|
||||
$Control{'assigned_to'} = FetchOneColumn();
|
||||
}
|
||||
|
||||
@@ -954,7 +965,7 @@ CheckSystem( );
|
||||
|
||||
### Check values ...
|
||||
# Version
|
||||
my $Version = "$DEFAULT_VERSION";
|
||||
my $Version = "";
|
||||
$Version = CheckVersion( $Control{'product'}, $Control{'version'} ) if( defined( $Control{'version'}));
|
||||
if ( $Version eq "" ) {
|
||||
my $Text = "You did not send a value for the required key \@version!\n\n";
|
||||
@@ -969,7 +980,7 @@ if ( $Version eq "" ) {
|
||||
foreach my $prod ( @all_products ) {
|
||||
$Text .= "Valid versions for product " . SqlQuote( $prod ) . " are: \n\t";
|
||||
|
||||
SendSQL("select value from versions, products where versions.product_id=products.id AND products.name=" . SqlQuote( $prod ));
|
||||
SendSQL("select value from versions where program=" . SqlQuote( $prod ) . "");
|
||||
@all_versions = FetchAllSQLData();
|
||||
$anz_versions = @all_versions;
|
||||
$Text .= join( "\n\t", @all_versions ) . "\n" ;
|
||||
@@ -997,29 +1008,97 @@ $Control{'version'} = $Version;
|
||||
# GroupsSet: Protections for Bug info. This paramter controls the visiblility of the
|
||||
# given bug. An Error in the given Buggroup is not a blocker, a default is taken.
|
||||
#
|
||||
# The GroupSet is accepted only as literals linked with whitespaces, plus-signs or kommas
|
||||
# The GroupSet is accepted in three ways: As single number like 65536
|
||||
# As added numbers like 65536 + 6 +8
|
||||
# As literals linked with whitespaces, plus-signs or kommas
|
||||
#
|
||||
my $GroupSet = "";
|
||||
my %GroupArr = ();
|
||||
$GroupSet = $Control{'groupset'} if( defined( $Control{ 'groupset' }));
|
||||
#
|
||||
# Fetch the default value for groupsetting
|
||||
my $DefaultGroup = 'ReadInternal';
|
||||
SendSQL("select id from groups where name=" . SqlQuote( $DefaultGroup ));
|
||||
SendSQL("select bit from groups where name=" . SqlQuote( "ReadInternal" ));
|
||||
my $default_group = FetchOneColumn();
|
||||
|
||||
if( $GroupSet eq "" ) {
|
||||
# Too bad: Groupset does not contain anything -> set to default
|
||||
$GroupArr{$DefaultGroup} = $default_group if(defined( $default_group ));
|
||||
# To bad: Groupset does not contain anything -> set to default
|
||||
$GroupSet = $default_group;
|
||||
#
|
||||
# Give the user a hint
|
||||
my $Text = "You did not send a value for optional key \@groupset, which controls\n";
|
||||
$Text .= "the Permissions of the bug. It will be set to a default value 'Internal Bug'\n";
|
||||
$Text .= "if the group '$DefaultGroup' exists. The QA may change that.\n";
|
||||
$Text .= "the Permissions of the bug. It was set to a default value 'Internal Bug'\n";
|
||||
$Text .= "Probably the QA will change that if desired.\n";
|
||||
|
||||
BugMailError( 0, $Text );
|
||||
} elsif( $GroupSet =~ /^\d+$/ ) {
|
||||
# Numerical Groups (no +-signs), the GroupSet must be the sum of the bits
|
||||
#
|
||||
my $grp_num = $GroupSet;
|
||||
# print "Numeric: $GroupSet\n";
|
||||
SendSQL("select bit from groups where isbuggroup=1 order by bit");
|
||||
my @Groups = FetchAllSQLData();
|
||||
|
||||
# DANGEROUS: This code implies, that perl *CAN* cope with large numbers
|
||||
# Its probably better to allow only one default-group when mailing !
|
||||
my $Val = "0";
|
||||
foreach ( @Groups ) {
|
||||
# print 0+$grp_num & 0+$_ , "\n";
|
||||
if ( ( (0+$grp_num) & (0+$_) ) == 0+$_ ) {
|
||||
$Val .= sprintf( "+%d", $_ );
|
||||
}
|
||||
}
|
||||
if( $Val eq "0" ) {
|
||||
# No valid group found
|
||||
my $Text = "The number you sent for the groupset of the bug was wrong.\n" .
|
||||
"It was not the sum of valid bits, which are:\n\t";
|
||||
$Text .= join( "\n\t", @Groups ) . "\n";
|
||||
$Text .= "The groupset for your bug is set to default $default_group, which\n" .
|
||||
"means 'ReadInternal'";
|
||||
|
||||
BugMailError( 0, $Text );
|
||||
$GroupSet = $default_group;
|
||||
} else {
|
||||
$GroupSet = $Val;
|
||||
}
|
||||
|
||||
} elsif( $GroupSet =~ /^(\s*\d+\s*\+\s*)+/ ) {
|
||||
#
|
||||
# Groupset given as String with added numbers like 65536+131072
|
||||
# The strings are splitted and checked if the numbers are in the DB
|
||||
my @bits = split( /\s*\+\s*/, $GroupSet );
|
||||
my $new_groupset = "0";
|
||||
# Get all bits for groupsetting
|
||||
SendSQL("select bit from groups where isbuggroup=1" );
|
||||
my @db_bits = FetchAllSQLData();
|
||||
|
||||
# ... and check, if the given bits and the one in the DB fit together
|
||||
foreach my $bit ( @bits ) {
|
||||
# print "The Bit is: $bit \n";
|
||||
if( lsearch( \@db_bits, $bit ) == -1 ) {
|
||||
# Bit not found !
|
||||
my $Text = "Checking the Group-Settings: You sent the Groupset-Bit $bit\n" .
|
||||
"which is not a valid Groupset-Bit. It will be skipped !\n\n";
|
||||
BugMailError( 0, $Text );
|
||||
} else {
|
||||
# Cool bit, add to the result-String
|
||||
$new_groupset .= "+" . $bit;
|
||||
}
|
||||
}
|
||||
|
||||
# Is the new-String larger than 0
|
||||
if( $new_groupset eq "0" ) {
|
||||
$new_groupset = $default_group;
|
||||
|
||||
my $Text = "All given Groupsetting-Bits are invalid. Setting Groupsetting to\n" .
|
||||
"default-Value $new_groupset, what means 'ReadInternal'\n\n";
|
||||
|
||||
BugMailError( 0, $Text );
|
||||
}
|
||||
# Restore to Groupset-Variable
|
||||
$GroupSet = $new_groupset;
|
||||
|
||||
} else {
|
||||
# literal e.g. 'ReadInternal'
|
||||
my $Value = "0";
|
||||
my $gserr = 0;
|
||||
my $Text = "";
|
||||
|
||||
@@ -1027,29 +1106,37 @@ if( $GroupSet eq "" ) {
|
||||
# Split literal Groupsettings either on Whitespaces, +-Signs or ,
|
||||
# Then search for every Literal in the DB - col name
|
||||
foreach ( split /\s+|\s*\+\s*|\s*,\s*/, $GroupSet ) {
|
||||
SendSQL("select id, Name from groups where name=" . SqlQuote($_));
|
||||
SendSQL("select bit, Name from groups where name=" . SqlQuote($_));
|
||||
my( $bval, $bname ) = FetchSQLData();
|
||||
|
||||
if( defined( $bname ) && $_ eq $bname ) {
|
||||
$GroupArr{$bname} = $bval;
|
||||
$Value .= sprintf( "+%d", $bval );
|
||||
} else {
|
||||
$Text .= "You sent the wrong GroupSet-String $_\n";
|
||||
$gserr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Give help if wrong GroupSet-String came
|
||||
if( $gserr > 0 ) {
|
||||
# There happend errors
|
||||
$Text .= "Here are all valid literal Groupsetting-strings:\n\t";
|
||||
SendSQL( "select g.name from groups g, user_group_map u where u.user_id=".$Control{'reporter'}.
|
||||
" and g.isbuggroup=1 and g.id = u.group_id group by g.name;" );
|
||||
SendSQL( "select name from groups where isbuggroup=1" );
|
||||
$Text .= join( "\n\t", FetchAllSQLData()) . "\n";
|
||||
BugMailError( 0, $Text );
|
||||
}
|
||||
|
||||
#
|
||||
# Check if anything was right, if not -> set default
|
||||
if( $Value eq "0" ) {
|
||||
$Value = $default_group;
|
||||
$Text .= "\nThe group will be set to $default_group, what means 'ReadInternal'\n\n";
|
||||
}
|
||||
|
||||
$GroupSet = $Value;
|
||||
} # End of checking groupsets
|
||||
delete $Control{'groupset'};
|
||||
|
||||
$Control{'groupset'} = $GroupSet;
|
||||
|
||||
# ###################################################################################
|
||||
# Checking is finished
|
||||
@@ -1087,20 +1174,11 @@ END
|
||||
my $query = "insert into bugs (\n" . join(",\n", @used_fields ) .
|
||||
", bug_status, creation_ts, everconfirmed) values ( ";
|
||||
|
||||
# 'Yuck'. Then again, this whole file should be rewritten anyway...
|
||||
$query =~ s/product/product_id/;
|
||||
$query =~ s/component/component_id/;
|
||||
|
||||
my $tmp_reply = "These values were stored by bugzilla:\n";
|
||||
my $val;
|
||||
foreach my $field (@used_fields) {
|
||||
if( $field eq "groupset" ) {
|
||||
$query .= $Control{$field} . ",\n";
|
||||
} elsif ( $field eq 'product' ) {
|
||||
$query .= get_product_id($Control{$field}) . ",\n";
|
||||
} elsif ( $field eq 'component' ) {
|
||||
$query .= get_component_id(get_product_id($Control{'product'}),
|
||||
$Control{$field}) . ",\n";
|
||||
} else {
|
||||
$query .= SqlQuote($Control{$field}) . ",\n";
|
||||
}
|
||||
@@ -1108,6 +1186,7 @@ END
|
||||
$val = $Control{ $field };
|
||||
|
||||
$val = DBID_to_name( $val ) if( $field =~ /reporter|assigned_to|qa_contact/ );
|
||||
$val = groupBitToString( $val ) if( $field =~ /groupset/ );
|
||||
|
||||
$tmp_reply .= sprintf( " \@%-15s = %-15s\n", $field, $val );
|
||||
|
||||
@@ -1115,10 +1194,6 @@ END
|
||||
$reporter = $val;
|
||||
}
|
||||
}
|
||||
#
|
||||
# Display GroupArr
|
||||
#
|
||||
$tmp_reply .= sprintf( " \@%-15s = %-15s\n", 'groupset', join(',', keys %GroupArr) );
|
||||
|
||||
$tmp_reply .= " ... and your error-description !\n";
|
||||
|
||||
@@ -1133,13 +1208,14 @@ END
|
||||
my $ever_confirmed = 0;
|
||||
my $state = SqlQuote("UNCONFIRMED");
|
||||
|
||||
SendSQL("SELECT votestoconfirm FROM products WHERE name = " .
|
||||
SqlQuote($Control{'product'}));
|
||||
SendSQL("SELECT votestoconfirm FROM products WHERE product = " .
|
||||
SqlQuote($Control{'product'}) . ";");
|
||||
if (!FetchOneColumn()) {
|
||||
$ever_confirmed = 1;
|
||||
$state = SqlQuote("NEW");
|
||||
}
|
||||
|
||||
|
||||
$query .= $state . ", \'$bug_when\', $ever_confirmed)\n";
|
||||
# $query .= SqlQuote( "NEW" ) . ", now(), " . SqlQuote($comment) . " )\n";
|
||||
|
||||
@@ -1157,25 +1233,14 @@ END
|
||||
my $long_desc_query = "INSERT INTO longdescs SET bug_id=$id, who=$userid, bug_when=\'$bug_when\', thetext=" . SqlQuote($comment);
|
||||
SendSQL($long_desc_query);
|
||||
|
||||
# Cool, the mail was successful
|
||||
# system("./processmail", $id, $SenderShort);
|
||||
# Cool, the mail was successfull
|
||||
system("cd .. ; ./processmail $id '$Sender'");
|
||||
} else {
|
||||
$id = 0xFFFFFFFF; # TEST !
|
||||
$id = 0xFFFF; # TEST !
|
||||
print "\n-------------------------------------------------------------------------\n";
|
||||
print "$query\n";
|
||||
}
|
||||
|
||||
#
|
||||
# Handle GroupArr
|
||||
#
|
||||
foreach my $grp (keys %GroupArr) {
|
||||
if( ! $test) {
|
||||
SendSQL("INSERT INTO bug_group_map SET bug_id=$id, group_id=$GroupArr{$grp}");
|
||||
} else {
|
||||
print "INSERT INTO bug_group_map SET bug_id=$id, group_id=$GroupArr{$grp}\n";
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# handle Attachments
|
||||
#
|
||||
@@ -1189,8 +1254,6 @@ END
|
||||
#
|
||||
# Send the 'you did it'-reply
|
||||
Reply( $SenderShort, $Message_ID,"Bugzilla success (ID $id)", $reply );
|
||||
|
||||
Bugzilla::BugMail::Send($id) if( ! $test);
|
||||
|
||||
} else {
|
||||
# There were critical errors in the mail - the bug couldnt be inserted. !
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
bugzilla-submit
|
||||
===============
|
||||
|
||||
Authors: Christian Reis <kiko@async.com.br>
|
||||
Eric Raymond <esr@thyrsus.com>
|
||||
|
||||
bugzilla-submit is a simple Python program that creates bugs in a Bugzilla
|
||||
instance. It takes as input text resembling message headers (RFC-822
|
||||
formatted) via standard input, or optionally a number of commandline
|
||||
parameters. It communicates using HTTP, which allows it to work over a
|
||||
network.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Its only requirement is Python 2.3 or higher; you should have the
|
||||
"python" executable in your path.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
* Please constrain testing to your own installation of Bugzilla, or use
|
||||
* http://landfill.bugzilla.org/ for testing purposes -- opening test
|
||||
* bugs on production instances of Bugzilla is definitely not a good idea
|
||||
|
||||
Run "bugzilla-submit --help" for a description of the possible options.
|
||||
|
||||
An example input file, named bugdata.txt, is provided. You can pipe it
|
||||
in as standard input to bugzilla-submit, providing a Bugzilla URI through
|
||||
the command-line.
|
||||
|
||||
Note that you must create a ~/.netrc entry to authenticate against the
|
||||
Bugzilla instance. The entry's machine field is a *quoted* Bugzilla URI,
|
||||
the login field is your ID on that host, and the password field is the
|
||||
your password password. An example entry follows:
|
||||
|
||||
machine "http://bugzilla.mozilla.org/"
|
||||
login foo@bar.loo
|
||||
password snarf
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Documentation for bugzilla-submit is provided in Docbook format; see
|
||||
bugzilla-submit.xml.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Product: FoodReplicator
|
||||
Component: Salt
|
||||
Version: 1.0
|
||||
Priority: P2
|
||||
Hardware: PC
|
||||
OS: Linux
|
||||
Severity: critical
|
||||
Summary: Impending electron shortage
|
||||
Depends-on: 8
|
||||
URL: http://www.google.com/
|
||||
|
||||
We need an emergency supply of electrons.
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# bugzilla-submit: a command-line script to post bugs to a Bugzilla instance
|
||||
#
|
||||
# Authors: Christian Reis <kiko@async.com.br>
|
||||
# Eric S. Raymond <esr@thyrsus.com>
|
||||
#
|
||||
# This is version 0.5.
|
||||
#
|
||||
# For a usage hint run bugzilla-submit --help
|
||||
#
|
||||
# TODO: use RDF output to pick up valid options, as in
|
||||
# http://www.async.com.br/~kiko/mybugzilla/config.cgi?ctype=rdf
|
||||
|
||||
import sys, string
|
||||
|
||||
def error(m):
|
||||
sys.stderr.write("bugzilla-submit: %s\n" % m)
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
version = string.split(string.split(sys.version)[0], ".")[:2]
|
||||
if map(int, version) < [2, 3]:
|
||||
error("you must upgrade to Python 2.3 or higher to use this script.")
|
||||
|
||||
import urllib, re, os, netrc, email.Parser, optparse
|
||||
|
||||
class ErrorURLopener(urllib.URLopener):
|
||||
"""URLopener that handles HTTP 404s"""
|
||||
def http_error_404(self, url, fp, errcode, errmsg, headers, *extra):
|
||||
raise ValueError, errmsg # 'File Not Found'
|
||||
|
||||
# Set up some aliases -- partly to hide the less friendly fieldnames
|
||||
# behind the names actually used for them in the stock web page presentation,
|
||||
# and partly to provide a place for mappings if the Bugzilla fieldnames
|
||||
# ever change.
|
||||
field_aliases = (('hardware', 'rep_platform'),
|
||||
('os', 'op_sys'),
|
||||
('summary', 'short_desc'),
|
||||
('description', 'comment'),
|
||||
('depends_on', 'dependson'),
|
||||
('status', 'bug_status'),
|
||||
('severity', 'bug_severity'),
|
||||
('url', 'bug_file_loc'),)
|
||||
|
||||
def header_to_field(hdr):
|
||||
hdr = hdr.lower().replace("-", "_")
|
||||
for (alias, name) in field_aliases:
|
||||
if hdr == alias:
|
||||
hdr = name
|
||||
break
|
||||
return hdr
|
||||
|
||||
def field_to_header(hdr):
|
||||
hdr = "-".join(map(lambda x: x.capitalize(), hdr.split("_")))
|
||||
for (alias, name) in field_aliases:
|
||||
if hdr == name:
|
||||
hdr = alias
|
||||
break
|
||||
return hdr
|
||||
|
||||
def setup_parser():
|
||||
# Take override values from the command line
|
||||
parser = optparse.OptionParser(usage="usage: %prog [options] bugzilla-url")
|
||||
parser.add_option('-b', '--status', dest='bug_status',
|
||||
help='Set the Status field.')
|
||||
parser.add_option('-u', '--url', dest='bug_file_loc',
|
||||
help='Set the URL field.')
|
||||
parser.add_option('-p', '--product', dest='product',
|
||||
help='Set the Product field.')
|
||||
parser.add_option('-v', '--version', dest='version',
|
||||
help='Set the Version field.')
|
||||
parser.add_option('-c', '--component', dest='component',
|
||||
help='Set the Component field.')
|
||||
parser.add_option('-s', '--summary', dest='short_desc',
|
||||
help='Set the Summary field.')
|
||||
parser.add_option('-H', '--hardware', dest='rep_platform',
|
||||
help='Set the Hardware field.')
|
||||
parser.add_option('-o', '--os', dest='op_sys',
|
||||
help='Set the Operating System field.')
|
||||
parser.add_option('-r', '--priority', dest='priority',
|
||||
help='Set the Priority field.')
|
||||
parser.add_option('-x', '--severity', dest='bug_severity',
|
||||
help='Set the Severity field.')
|
||||
parser.add_option('-d', '--description', dest='comment',
|
||||
help='Set the Description field.')
|
||||
parser.add_option('-a', '--assigned-to', dest='assigned_to',
|
||||
help='Set the Assigned-To field.')
|
||||
parser.add_option('-C', '--cc', dest='cc',
|
||||
help='Set the Cc field.')
|
||||
parser.add_option('-k', '--keywords', dest='keywords',
|
||||
help='Set the Keywords field.')
|
||||
parser.add_option('-D', '--depends-on', dest='dependson',
|
||||
help='Set the Depends-On field.')
|
||||
parser.add_option('-B', '--blocked', dest='blocked',
|
||||
help='Set the Blocked field.')
|
||||
parser.add_option('-n', '--no-stdin', dest='read',
|
||||
default=True, action='store_false',
|
||||
help='Suppress reading fields from stdin.')
|
||||
return parser
|
||||
|
||||
# Fetch user's credential for access to this Bugzilla instance.
|
||||
def get_credentials(bugzilla):
|
||||
# Work around a quirk in the Python implementation.
|
||||
# The URL has to be quoted, otherwise the parser barfs on the colon.
|
||||
# But the parser doesn't strip the quotes.
|
||||
authenticate_on = '"' + bugzilla + '"'
|
||||
try:
|
||||
credentials = netrc.netrc()
|
||||
except netrc.NetrcParseError, e:
|
||||
error("ill-formed .netrc: %s:%s %s" % (e.filename, e.lineno, e.msg))
|
||||
except IOError, e:
|
||||
error("missing .netrc file %s" % str(e).split()[-1])
|
||||
ret = credentials.authenticators(authenticate_on)
|
||||
if not ret:
|
||||
# Okay, the literal string passed in failed. Just to make sure,
|
||||
# try adding/removing a slash after the address and looking
|
||||
# again. We don't know what format was used in .netrc, which is
|
||||
# why this rather hackish approach is necessary.
|
||||
if bugzilla[-1] == "/":
|
||||
authenticate_on = '"' + bugzilla[:-1] + '"'
|
||||
else:
|
||||
authenticate_on = '"' + bugzilla + '/"'
|
||||
ret = credentials.authenticators(authenticate_on)
|
||||
if not ret:
|
||||
# Apparently, an invalid machine URL will cause credentials == None
|
||||
error("no credentials for Bugzilla instance at %s" % bugzilla)
|
||||
return ret
|
||||
|
||||
def process_options(options):
|
||||
data = {}
|
||||
# Initialize bug report fields from message on standard input
|
||||
if options.read:
|
||||
message_parser = email.Parser.Parser()
|
||||
message = message_parser.parse(sys.stdin)
|
||||
for (key, value) in message.items():
|
||||
data.update({header_to_field(key) : value})
|
||||
if not 'comment' in data:
|
||||
data['comment'] = message.get_payload()
|
||||
|
||||
# Merge in options from the command line; they override what's on stdin.
|
||||
for (key, value) in options.__dict__.items():
|
||||
if key != 'read' and value != None:
|
||||
data[key] = value
|
||||
return data
|
||||
|
||||
def ensure_defaults(data):
|
||||
# Provide some defaults if the user did not supply them.
|
||||
if 'op_sys' not in data:
|
||||
if sys.platform.startswith('linux'):
|
||||
data['op_sys'] = 'Linux'
|
||||
if 'rep_platform' not in data:
|
||||
data['rep_platform'] = 'PC'
|
||||
if 'bug_status' not in data:
|
||||
data['bug_status'] = 'NEW'
|
||||
if 'bug_severity' not in data:
|
||||
data['bug_severity'] = 'normal'
|
||||
if 'bug_file_loc' not in data:
|
||||
data['bug_file_loc'] = 'http://' # Yes, Bugzilla needs this
|
||||
if 'priority' not in data:
|
||||
data['priority'] = 'P2'
|
||||
|
||||
def validate_fields(data):
|
||||
# Fields for validation
|
||||
required_fields = (
|
||||
"bug_status", "bug_file_loc", "product", "version", "component",
|
||||
"short_desc", "rep_platform", "op_sys", "priority", "bug_severity",
|
||||
"comment",
|
||||
)
|
||||
legal_fields = required_fields + (
|
||||
"assigned_to", "cc", "keywords", "dependson", "blocked",
|
||||
)
|
||||
my_fields = data.keys()
|
||||
for field in my_fields:
|
||||
if field not in legal_fields:
|
||||
error("invalid field: %s" % field_to_header(field))
|
||||
for field in required_fields:
|
||||
if field not in my_fields:
|
||||
error("required field missing: %s" % field_to_header(field))
|
||||
|
||||
if not data['short_desc']:
|
||||
error("summary for bug submission must not be empty")
|
||||
|
||||
if not data['comment']:
|
||||
error("comment for bug submission must not be empty")
|
||||
|
||||
#
|
||||
# POST-specific functions
|
||||
#
|
||||
|
||||
def submit_bug_POST(bugzilla, data):
|
||||
# Move the request over the wire
|
||||
postdata = urllib.urlencode(data)
|
||||
try:
|
||||
url = ErrorURLopener().open("%s/post_bug.cgi" % bugzilla, postdata)
|
||||
except ValueError:
|
||||
error("Bugzilla site at %s not found (HTTP returned 404)" % bugzilla)
|
||||
ret = url.read()
|
||||
check_result_POST(ret, data)
|
||||
|
||||
def check_result_POST(ret, data):
|
||||
# XXX We can move pre-validation out of here as soon as we pick up
|
||||
# the valid options from config.cgi -- it will become a simple
|
||||
# assertion and ID-grabbing step.
|
||||
#
|
||||
# XXX: We use get() here which may return None, but since the user
|
||||
# might not have provided these options, we don't want to die on
|
||||
# them.
|
||||
version = data.get('version')
|
||||
product = data.get('product')
|
||||
component = data.get('component')
|
||||
priority = data.get('priority')
|
||||
severity = data.get('bug_severity')
|
||||
status = data.get('bug_status')
|
||||
assignee = data.get('assigned_to')
|
||||
platform = data.get('rep_platform')
|
||||
opsys = data.get('op_sys')
|
||||
keywords = data.get('keywords')
|
||||
deps = data.get('dependson', '') + " " + data.get('blocked', '')
|
||||
deps = deps.replace(" ", ", ")
|
||||
# XXX: we should really not be using plain find() here, as it can
|
||||
# match the bug content inadvertedly
|
||||
if ret.find("A legal Version was not") != -1:
|
||||
error("version %r does not exist for component %s:%s" %
|
||||
(version, product, component))
|
||||
if ret.find("A legal Priority was not") != -1:
|
||||
error("priority %r does not exist in "
|
||||
"this Bugzilla instance" % priority)
|
||||
if ret.find("A legal Severity was not") != -1:
|
||||
error("severity %r does not exist in "
|
||||
"this Bugzilla instance" % severity)
|
||||
if ret.find("A legal Status was not") != -1:
|
||||
error("status %r is not a valid creation status in "
|
||||
"this Bugzilla instance" % status)
|
||||
if ret.find("A legal Platform was not") != -1:
|
||||
error("platform %r is not a valid platform in "
|
||||
"this Bugzilla instance" % platform)
|
||||
if ret.find("A legal OS/Version was not") != -1:
|
||||
error("%r is not a valid OS in "
|
||||
"this Bugzilla instance" % opsys)
|
||||
if ret.find("Invalid Username") != -1:
|
||||
error("invalid credentials submitted")
|
||||
if ret.find("Component Needed") != -1:
|
||||
error("the component %r does not exist in "
|
||||
"this Bugzilla instance" % component)
|
||||
if ret.find("Unknown Keyword") != -1:
|
||||
error("keyword(s) %r not registered in "
|
||||
"this Bugzilla instance" % keywords)
|
||||
if ret.find("The product name") != -1:
|
||||
error("product %r does not exist in this "
|
||||
"Bugzilla instance" % product)
|
||||
# XXX: this should be smarter
|
||||
if ret.find("does not exist") != -1:
|
||||
error("could not mark dependencies for bugs %s: one or "
|
||||
"more bugs didn't exist in this Bugzilla instance" % deps)
|
||||
if ret.find("Match Failed") != -1:
|
||||
# XXX: invalid CC hits on this error too
|
||||
error("the bug assignee %r isn't registered in "
|
||||
"this Bugzilla instance" % assignee)
|
||||
# If all is well, return bug number posted
|
||||
if ret.find("process_bug.cgi") == -1:
|
||||
error("could not post bug to %s: are you sure this "
|
||||
"is Bugzilla instance's top-level directory?" % bugzilla)
|
||||
m = re.search("Bug ([0-9]+) Submitted", ret)
|
||||
if not m:
|
||||
print ret
|
||||
error("Internal error: bug id not found; please report a bug")
|
||||
id = m.group(1)
|
||||
print "Bug %s posted." % id
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = setup_parser()
|
||||
|
||||
# Parser will print help and exit here if we specified --help
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error("missing Bugzilla host URL")
|
||||
|
||||
bugzilla = args[0]
|
||||
data = process_options(options)
|
||||
|
||||
login, account, password = get_credentials(bugzilla)
|
||||
if "@" not in login: # no use even trying to submit
|
||||
error("login %r is invalid (it should be an email address)" % login)
|
||||
|
||||
ensure_defaults(data)
|
||||
validate_fields(data)
|
||||
|
||||
# Attach authentication information
|
||||
data.update({'Bugzilla_login' : login,
|
||||
'Bugzilla_password' : password,
|
||||
'GoAheadAndLogIn' : 1,
|
||||
'form_name' : 'enter_bug'})
|
||||
|
||||
submit_bug_POST(bugzilla, data)
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC
|
||||
"-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||
"docbook/docbookx.dtd">
|
||||
<refentry id='bugzilla-submit.1'>
|
||||
<refmeta>
|
||||
<refentrytitle>bugzilla-submit</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo class='date'>Oct 30, 2003</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv id='name'>
|
||||
<refname>bugzilla-submit</refname>
|
||||
<refpurpose>post bugs to a Bugzilla instance</refpurpose>
|
||||
</refnamediv>
|
||||
<refsynopsisdiv id='synopsis'>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>bugzilla-submit</command>
|
||||
<arg choice='opt'>--status <replaceable>bug_status</replaceable></arg>
|
||||
<arg choice='opt'>--url <replaceable>bug_file_loc</replaceable></arg>
|
||||
<arg choice='opt'>--product <replaceable>product</replaceable></arg>
|
||||
<arg choice='opt'>--version <replaceable>version</replaceable></arg>
|
||||
<arg choice='opt'>--component <replaceable>component</replaceable></arg>
|
||||
<arg choice='opt'>--summary <replaceable>short_desc</replaceable></arg>
|
||||
<arg choice='opt'>--hardware <replaceable>rep_platform</replaceable></arg>
|
||||
<arg choice='opt'>--os <replaceable>op_sys</replaceable></arg>
|
||||
<arg choice='opt'>--priority <replaceable>priority</replaceable></arg>
|
||||
<arg choice='opt'>--severity <replaceable>bug_severity</replaceable></arg>
|
||||
<arg choice='opt'>--assigned-to <replaceable>assigned-to</replaceable></arg>
|
||||
<arg choice='opt'>--cc <replaceable>cc</replaceable></arg>
|
||||
<arg choice='opt'>--keywords <replaceable>keywords</replaceable></arg>
|
||||
<arg choice='opt'>--depends-on <replaceable>dependson</replaceable></arg>
|
||||
<arg choice='opt'>--blocked <replaceable>blocked</replaceable></arg>
|
||||
<arg choice='opt'>--description <replaceable>comment</replaceable></arg>
|
||||
<arg choice='opt'>--no-read </arg>
|
||||
<arg choice='plain'><replaceable>bugzilla-url</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1 id='description'><title>DESCRIPTION</title>
|
||||
|
||||
<para><application>bugzilla-submit</application> is a command-line tool
|
||||
for posting bug reports to any instance of Bugzilla. It accepts on
|
||||
standard input text resembling an RFC-822 message. The headers of
|
||||
that message, and its body, are used to set error-report field values.
|
||||
More field values are merged in from command-line options. If required
|
||||
fields have not been set, <application>bugzilla-submit</application>
|
||||
tries to compute them. Finally, the resulting error report is
|
||||
validated. If all required fields are present, and there are no
|
||||
illegal fields or values, the report is shipped off to the Mozilla
|
||||
instance specified by the single positional argument. Login/password
|
||||
credentials are read from the calling user's <filename>~/.netrc</filename>
|
||||
file.</para>
|
||||
|
||||
<para>bugzilla-submit accepts a single argument:
|
||||
<replaceable>bugzilla-url</replaceable>. Its value must match the
|
||||
relevant Bugzilla instance's base URL (technically, its
|
||||
<replaceable>urlbase</replaceable> param). The program also accepts the
|
||||
following options to set or override fields:</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-b. --bug-status</term>
|
||||
<listitem>
|
||||
<para>Set the bug_status field, overriding the Status header from
|
||||
standard input if present. (The stock Bugzilla web presentation
|
||||
identifies this field as <quote>Status</quote>.)</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-u, --url</term>
|
||||
<listitem>
|
||||
<para>Set the bug_file_loc field, overriding the URL header from
|
||||
standard input if present. (The stock Bugzilla web presentation
|
||||
identifies this field as <quote>URL</quote>.)</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-p, --product</term>
|
||||
<listitem>
|
||||
<para>Set the product field, overriding the Product header from
|
||||
standard input if necessary.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-v, --version</term>
|
||||
<listitem><para>Set the version field, overriding the Version header
|
||||
from standard input if necessary.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-c, --component</term>
|
||||
<listitem><para>Set the component field, overriding the Component header
|
||||
from standard input if necessary.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-s, --summary</term>
|
||||
<listitem><para>Set the short_desc field, overriding the Summary header
|
||||
from standard input if necessary. (The stock Bugzilla web presentation
|
||||
identifies this field as <quote>Summary</quote>.)</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-H, --hardware</term>
|
||||
<listitem><para>Set the rep_platform field, overriding the Hardware header
|
||||
from standard input if necessary. (The stock Bugzilla web presentation
|
||||
identifies this field as <quote>Hardware</quote>.)</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-o, --os</term>
|
||||
<listitem><para>Set the op_sys field, overriding the OS (Operating
|
||||
System) header from standard input if necessary. (The stock Bugzilla web
|
||||
presentation also identifies this field as
|
||||
<quote>OS</quote>.)</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-r, --priority</term>
|
||||
<listitem><para>Set the priority field, overriding the Priority header
|
||||
from standard input if necessary.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-x, --severity</term>
|
||||
<listitem><para>Set the severity field, overriding the Severity header
|
||||
from standard input if necessary.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-d, --description</term>
|
||||
<listitem><para>Set the comment field, overriding the Description header
|
||||
from standard input if necessary. (The stock Bugzilla web presentation
|
||||
identifies this field as <quote>Description</quote>.) If there is a
|
||||
message body and no Description field and this option is not
|
||||
specified, the message body is used as a description.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-a, --assigned-to</term>
|
||||
<listitem>
|
||||
<para>Set the optional assigned_to field, overriding the Assigned-To
|
||||
header from standard input if necessary.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-C, --cc</term>
|
||||
<listitem>
|
||||
<para>Set the optional cc field, overriding the Cc
|
||||
header from standard input if necessary.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-k, --keywords</term>
|
||||
<listitem>
|
||||
<para>Set the optional keywords field, overriding the Keywords
|
||||
header from standard input if necessary.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-D, --depends-on</term>
|
||||
<listitem>
|
||||
<para>Set the optional dependson field, overriding the Depends-On
|
||||
header from standard input if necessary.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-B, --assigned-to</term>
|
||||
<listitem>
|
||||
<para>Set the optional blocked field, overriding the Blocked
|
||||
header from standard input if necessary.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-n, --no-stdin</term>
|
||||
<listitem><para>Suppress reading fields from standard input.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>-h, --help</term>
|
||||
<listitem><para>Print usage help and exit.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>This program will try to deduce OS and Hardware if those are not
|
||||
specified. If it fails, validation will fail before shipping the
|
||||
report.</para>
|
||||
|
||||
<para>There is expected to be a single positional argument following
|
||||
any options. It should be the URL of the Bugzilla instance to which
|
||||
the bug is to be submitted.</para>
|
||||
|
||||
</refsect1>
|
||||
<refsect1 id='files'><title>FILES</title>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><filename>~/.netrc</filename></term>
|
||||
<listitem><para>Must contain an entry in which the machine field is
|
||||
the Bugzilla instance URL, the login field is your ID on that host, and the
|
||||
password field is the right password. The URL in the machine field
|
||||
must be enclosed in double quotes.</para>
|
||||
|
||||
<para>For example, if your Bugzilla instance is at
|
||||
"http://landfill.bugzilla.org/bztest/", and your login and password
|
||||
there are "john@doe.com" and "foo", respectively, your
|
||||
<filename>.netrc</filename> entry should look something like:</para>
|
||||
|
||||
<screen>
|
||||
machine "http://landfill.bugzilla.org/bztest/"
|
||||
login john@doe.com
|
||||
password foo
|
||||
|
||||
</screen>
|
||||
|
||||
<para>Note that the machine entry should match exactly the instance URL
|
||||
specified to <application>bugzilla-submit</application>.</para>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
<refsect1 id='author'><title>AUTHORS</title>
|
||||
<para>Christian Reis <kiko@async.com.br>, Eric S. Raymond
|
||||
<esr@thyrsus.com>.</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
@@ -28,19 +28,13 @@
|
||||
# 1. better way to get the body text (I don't know what dump_entity() is
|
||||
# actually doing
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
use MIME::Parser;
|
||||
|
||||
BEGIN {
|
||||
chdir ".."; # this script lives in contrib, change to main
|
||||
push @INC, "contrib";
|
||||
push @INC, "."; # this script lives in contrib
|
||||
}
|
||||
|
||||
push @INC, "../."; # this script lives in contrib
|
||||
require "globals.pl";
|
||||
use BugzillaEmail;
|
||||
use Bugzilla::Config qw(:DEFAULT $datadir);
|
||||
use Bugzilla::BugMail;
|
||||
require "BugzillaEmail.pm";
|
||||
|
||||
# Create a new MIME parser:
|
||||
my $parser = new MIME::Parser;
|
||||
@@ -49,10 +43,10 @@ my $Comment = "";
|
||||
|
||||
# Create and set the output directory:
|
||||
# FIXME: There should be a $BUGZILLA_HOME variable (SML)
|
||||
(-d "$datadir/mimedump-tmp") or mkdir "$datadir/mimedump-tmp",0755 or die "mkdir: $!";
|
||||
(-w "$datadir/mimedump-tmp") or die "can't write to directory";
|
||||
(-d "../data/mimedump-tmp") or mkdir "../data/mimedump-tmp",0755 or die "mkdir: $!";
|
||||
(-w "../data/mimedump-tmp") or die "can't write to directory";
|
||||
|
||||
$parser->output_dir("$datadir/mimedump-tmp");
|
||||
$parser->output_dir("../data/mimedump-tmp");
|
||||
|
||||
# Read the MIME message:
|
||||
my $entity = $parser->read(\*STDIN) or die "couldn't parse MIME stream";
|
||||
@@ -69,6 +63,8 @@ chomp( $Message_ID );
|
||||
|
||||
print "Dealing with the sender $Sender\n";
|
||||
|
||||
ConnectToDatabase();
|
||||
|
||||
my $SenderShort = $Sender;
|
||||
$SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/;
|
||||
|
||||
@@ -119,12 +115,11 @@ my $Body = "Subject: " . $Subject . "\n" . $Comment;
|
||||
my $long_desc_query = "INSERT INTO longdescs SET bug_id=$found_id, who=$userid, bug_when=NOW(), thetext=" . SqlQuote($Body) . ";";
|
||||
SendSQL($long_desc_query);
|
||||
|
||||
Bugzilla::BugMail::Send( $found_id, { changer => $SenderShort } );
|
||||
system("cd .. ; ./processmail $found_id '$SenderShort'");
|
||||
|
||||
sub DealWithError {
|
||||
my ($reason) = @_;
|
||||
print $reason . "\n";
|
||||
exit 100;
|
||||
}
|
||||
|
||||
# Yanking this wholesale from bug_email, 'cause I know this works. I'll
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
#!/usr/local/bin/ruby
|
||||
|
||||
# Queries an LDAP server for all email addresses (tested against Exchange 5.5),
|
||||
# and makes nice bugzilla user entries out of them. Also disables Bugzilla users
|
||||
# that are not found in LDAP.
|
||||
|
||||
# $Id: bugzilla_ldapsync.rb,v 1.2 2003-04-26 16:35:04 jake%bugzilla.org Exp $
|
||||
|
||||
require 'ldap'
|
||||
require 'dbi'
|
||||
require 'getoptlong'
|
||||
|
||||
opts = GetoptLong.new(
|
||||
['--dbname', '-d', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--dbpassword', '-p', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--dbuser', '-u', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--dbpassfile', '-P', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--ldaphost', '-h', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--ldapbase', '-b', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--ldapquery', '-q', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--maildomain', '-m', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--noremove', '-n', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--defaultpass', '-D', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--checkmode', '-c', GetoptLong::OPTIONAL_ARGUMENT]
|
||||
)
|
||||
|
||||
|
||||
# in hash to make it easy
|
||||
optHash = Hash.new
|
||||
opts.each do |opt, arg|
|
||||
optHash[opt]=arg
|
||||
end
|
||||
|
||||
# grab password from file if it's an option
|
||||
if optHash['--dbpassfile']
|
||||
dbPassword=File.open(optHash['--dbpassfile'], 'r').readlines[0].chomp!
|
||||
else
|
||||
dbPassword=optHash['--dbpassword'] || nil
|
||||
end
|
||||
|
||||
# make bad assumptions.
|
||||
dbName = optHash['--dbname'] || 'bugzilla'
|
||||
dbUser = optHash['--dbuser'] || 'bugzilla'
|
||||
ldapHost = optHash['--ldaphost'] || 'ldap'
|
||||
ldapBase = optHash['--ldapbase'] || ''
|
||||
mailDomain = optHash['--maildomain'] || `domainname`.chomp!
|
||||
ldapQuery = optHash['--ldapquery'] || "(&(objectclass=person)(rfc822Mailbox=*@#{mailDomain}))"
|
||||
checkMode = optHash['--checkmode'] || nil
|
||||
noRemove = optHash['--noremove'] || nil
|
||||
defaultPass = optHash['--defaultpass'] || 'bugzilla'
|
||||
|
||||
if (! dbPassword)
|
||||
puts "bugzilla_ldapsync v1.3 (c) 2003 Thomas Stromberg <thomas+bugzilla@stromberg.org>"
|
||||
puts ""
|
||||
puts " -d | --dbname name of MySQL database [#{dbName}]"
|
||||
puts " -u | --dbuser username for MySQL database [#{dbUser}]"
|
||||
puts " -p | --dbpassword password for MySQL user [#{dbPassword}]"
|
||||
puts " -P | --dbpassfile filename containing password for MySQL user"
|
||||
puts " -h | --ldaphost hostname for LDAP server [#{ldapHost}]"
|
||||
puts " -b | --ldapbase Base of LDAP query, for instance, o=Bugzilla.com"
|
||||
puts " -q | --ldapquery LDAP query, uses maildomain [#{ldapQuery}]"
|
||||
puts " -m | --maildomain e-mail domain to use records from"
|
||||
puts " -n | --noremove do not remove Bugzilla users that are not in LDAP"
|
||||
puts " -c | --checkmode checkmode, does not perform any SQL changes"
|
||||
puts " -D | --defaultpass default password for new users [#{defaultPass}]"
|
||||
puts
|
||||
puts "example:"
|
||||
puts
|
||||
puts " bugzilla_ldapsync.rb -c -u taskzilla -P /tmp/test -d taskzilla -h bhncmail -m \"bowebellhowell.com\""
|
||||
exit
|
||||
end
|
||||
|
||||
|
||||
if (checkMode)
|
||||
puts '(checkmode enabled, no SQL writes will actually happen)'
|
||||
puts "ldapquery is #{ldapQuery}"
|
||||
puts
|
||||
end
|
||||
|
||||
|
||||
bugzillaUsers = Hash.new
|
||||
ldapUsers = Hash.new
|
||||
encPassword = defaultPass.crypt('xx')
|
||||
sqlNewUser = "INSERT INTO profiles VALUES ('', ?, '#{encPassword}', ?, '', 1, NULL, '0000-00-00 00:00:00');"
|
||||
|
||||
# presumes that the MySQL database is local.
|
||||
dbh = DBI.connect("DBI:Mysql:#{dbName}", dbUser, dbPassword)
|
||||
|
||||
# select all e-mail addresses where there is no disabledtext defined. Only valid users, please!
|
||||
dbh.select_all('select login_name, realname, disabledtext from profiles') { |row|
|
||||
login = row[0].downcase
|
||||
bugzillaUsers[login] = Hash.new
|
||||
bugzillaUsers[login]['desc'] = row[1]
|
||||
bugzillaUsers[login]['disabled'] = row[2]
|
||||
#puts "bugzilla has #{login} - \"#{bugzillaUsers[login]['desc']}\" (#{bugzillaUsers[login]['disabled']})"
|
||||
}
|
||||
|
||||
|
||||
LDAP::Conn.new(ldapHost, 389).bind{|conn|
|
||||
sub = nil
|
||||
# perform the query, but only get the e-mail address, location, and name returned to us.
|
||||
conn.search(ldapBase, LDAP::LDAP_SCOPE_SUBTREE, ldapQuery,
|
||||
['rfc822Mailbox', 'physicalDeliveryOfficeName', 'cn']) { |entry|
|
||||
|
||||
# Get the users first (primary) e-mail address, but I only want what's before the @ sign.
|
||||
entry.vals("rfc822Mailbox")[0] =~ /([\w\.-]+)\@/
|
||||
email = $1
|
||||
|
||||
# We put the officename in the users description, and nothing otherwise.
|
||||
if entry.vals("physicalDeliveryOfficeName")
|
||||
location = entry.vals("physicalDeliveryOfficeName")[0]
|
||||
else
|
||||
location = ''
|
||||
end
|
||||
|
||||
# for whatever reason, we get blank entries. Do some double checking here.
|
||||
if (email && (email.length > 4) && (location !~ /Generic/) && (entry.vals("cn")))
|
||||
if (location.length > 2)
|
||||
desc = entry.vals("cn")[0] + " (" + location + ")"
|
||||
else
|
||||
desc = entry.vals("cn")[0]
|
||||
end
|
||||
|
||||
# take care of the whitespace.
|
||||
desc.sub!("\s+$", "")
|
||||
desc.sub!("^\s+", "")
|
||||
|
||||
# dumb hack. should be properly escaped, and apostrophes should never ever ever be in email.
|
||||
email.sub!("\'", "%")
|
||||
email.sub!('%', "\'")
|
||||
email=email.downcase
|
||||
ldapUsers[email.downcase] = Hash.new
|
||||
ldapUsers[email.downcase]['desc'] = desc
|
||||
ldapUsers[email.downcase]['disabled'] = nil
|
||||
#puts "ldap has #{email} - #{ldapUsers[email.downcase]['desc']}"
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
# This is the loop that takes the users that we found in Bugzilla originally, and
|
||||
# checks to see if they are still in the LDAP server. If they are not, away they go!
|
||||
|
||||
ldapUsers.each_key { |user|
|
||||
# user does not exist at all.
|
||||
#puts "checking ldap user #{user}"
|
||||
if (! bugzillaUsers[user])
|
||||
puts "+ Adding #{user} - #{ldapUsers[user]['desc']}"
|
||||
|
||||
if (! checkMode)
|
||||
dbh.do(sqlNewUser, user, ldapUsers[user]['desc'])
|
||||
end
|
||||
|
||||
# short-circuit now.
|
||||
next
|
||||
end
|
||||
|
||||
if (bugzillaUsers[user]['desc'] != ldapUsers[user]['desc'])
|
||||
puts "* Changing #{user} from \"#{bugzillaUsers[user]['desc']}\" to \"#{ldapUsers[user]['desc']}\""
|
||||
if (! checkMode)
|
||||
# not efficient.
|
||||
dbh.do("UPDATE profiles SET realname = ? WHERE login_name = ?", ldapUsers[user]['desc'], user)
|
||||
end
|
||||
end
|
||||
|
||||
if (bugzillaUsers[user]['disabled'].length > 0)
|
||||
puts "+ Enabling #{user} (was \"#{bugzillaUsers[user]['disabled']}\")"
|
||||
if (! checkMode)
|
||||
dbh.do("UPDATE profiles SET disabledtext = NULL WHERE login_name=\"#{user}\"")
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
if (! noRemove)
|
||||
bugzillaUsers.each_key { |user|
|
||||
if ((bugzillaUsers[user]['disabled'].length < 1) && (! ldapUsers[user]))
|
||||
puts "- Disabling #{user} (#{bugzillaUsers[user]['disabled']})"
|
||||
|
||||
if (! checkMode)
|
||||
dbh.do("UPDATE profiles SET disabledtext = \'auto-disabled by ldap sync\' WHERE login_name=\"#{user}\"")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
dbh.disconnect
|
||||
|
||||
23
mozilla/webtools/bugzilla/contrib/cvs-update.pl → mozilla/webtools/bugzilla/contrib/cvs-update.sh
Executable file → Normal file
23
mozilla/webtools/bugzilla/contrib/cvs-update.pl → mozilla/webtools/bugzilla/contrib/cvs-update.sh
Executable file → Normal file
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#!/bin/sh
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
@@ -19,8 +18,7 @@
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Dawn Endico <endico@mozilla.org>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# Jouni Heikniemi <jouni@heikniemi.net>
|
||||
|
||||
|
||||
# Keep a record of all cvs updates made from a given directory.
|
||||
#
|
||||
@@ -28,21 +26,12 @@
|
||||
# and run the cvs command with the date that you want to back
|
||||
# out to. (Probably the second to last entry).
|
||||
|
||||
# Because this script lives in contrib, you may want to
|
||||
# ln -s contrib/cvs-update.pl cvs-update.pl
|
||||
# from your bugzilla install directory so you can run
|
||||
# the script easily from there (./cvs-update.pl)
|
||||
|
||||
#DATE=`date +%e/%m/%Y\ %k:%M:%S\ %Z`
|
||||
DATE=`date`
|
||||
COMMAND="cvs update -d -P -D"
|
||||
echo $COMMAND \"$DATE\" >> cvs-update.log
|
||||
$COMMAND "$DATE"
|
||||
|
||||
my ($second, $minute, $hour, $day, $month, $year) = gmtime;
|
||||
my $date = sprintf("%04d-%02d-%02d %d:%02d:%02dZ",
|
||||
$year+1900, $month+1, $day, $hour, $minute, $second);
|
||||
my $cmd = "cvs -q update -dP";
|
||||
open LOG, ">>cvs-update.log" or die("Couldn't open cvs update log!");
|
||||
print LOG "$cmd -D \"$date\"\n";
|
||||
close LOG;
|
||||
system("$cmd -A");
|
||||
|
||||
# sample log file
|
||||
#cvs update -P -D "11/04/2000 20:22:08 PDT"
|
||||
@@ -1,62 +0,0 @@
|
||||
gnatsparse
|
||||
==========
|
||||
|
||||
Author: Daniel Berlin <dan@dberlin.org>
|
||||
|
||||
gnatsparse is a simple Python program that imports a GNATS database
|
||||
into a Bugzilla system. It is based on the gnats2bz.pl Perl script
|
||||
but it's a rewrite at the same time. Its parser is based on gnatsweb,
|
||||
which gives a 10 times speed improvement compared to the previous code.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Chunks audit trail into separate comments, with the right From's, times, etc.
|
||||
|
||||
* Handles followup emails that are in the report, with the right From's, times,
|
||||
etc.
|
||||
|
||||
* Properly handles duplicates, adding the standard bugzilla duplicate message.
|
||||
|
||||
* Extracts and handles gnatsweb attachments, as well as uuencoded attachments
|
||||
appearing in either followup emails, the how-to-repeat field, etc. Replaces
|
||||
them with a message to look at the attachments list, and adds the standard
|
||||
"Created an attachment" message that bugzilla uses. Handling them includes
|
||||
giving them the right name and mime-type. "attachments" means multiple
|
||||
uuencoded things/gnatsweb attachments are handled properly.
|
||||
|
||||
* Handles reopened bug reports.
|
||||
|
||||
* Builds the cc list from the people who have commented on the report,
|
||||
and the reporter.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
It requires python 2.2+, it won't work with 1.5.2 (Linux distributions
|
||||
ship with 2.2+ these days, so that shouldn't be an issue).
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Documentation can be found inside the scripts. The source code is self
|
||||
documenting.
|
||||
|
||||
Issues for someone trying to use it to convert a gnats install
|
||||
-----------------------------------
|
||||
|
||||
1. We have three custom fields bugzilla doesn't ship with,
|
||||
gcchost, gcctarget, and gccbuild.
|
||||
We removed two bugzilla fields, rep_platform and op_sys.
|
||||
If you use the latter instead of the former, you'll need to
|
||||
update the script to account for this.
|
||||
2. Because gcc attachments consist of preprocessed source, all attachments
|
||||
inserted into the attachment database are compressed with zlib.compress.
|
||||
This requires associated bugzilla changes to decompress before sending to
|
||||
the browser.
|
||||
Unless you want to make those changes (it's roughly 3 lines), you'll
|
||||
need to remove the zlib.compress call.
|
||||
3. You will need to come up with your own release to version mapping and
|
||||
install it.
|
||||
4. Obviously, any extra gnats fields you have added will have to
|
||||
be handled in some manner.
|
||||
@@ -1,804 +0,0 @@
|
||||
try:
|
||||
# Using Psyco makes it about 25% faster, but there's a bug in psyco in
|
||||
# handling of eval causing it to use unlimited memory with the magic
|
||||
# file enabled.
|
||||
# import psyco
|
||||
# psyco.full()
|
||||
# from psyco.classes import *
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
import re
|
||||
import base64
|
||||
import cStringIO
|
||||
import specialuu
|
||||
import array
|
||||
import email.Utils
|
||||
import zlib
|
||||
import magic
|
||||
|
||||
# Comment out if you don't want magic detection
|
||||
magicf = magic.MagicFile()
|
||||
|
||||
# Open our output file
|
||||
outfile = open("gnats2bz_data.sql", "w")
|
||||
|
||||
# List of GNATS fields
|
||||
fieldnames = ("Number", "Category", "Synopsis", "Confidential", "Severity",
|
||||
"Priority", "Responsible", "State", "Quarter", "Keywords",
|
||||
"Date-Required", "Class", "Submitter-Id", "Arrival-Date",
|
||||
"Closed-Date", "Last-Modified", "Originator", "Release",
|
||||
"Organization", "Environment", "Description", "How-To-Repeat",
|
||||
"Fix", "Release-Note", "Audit-Trail", "Unformatted")
|
||||
|
||||
# Dictionary telling us which GNATS fields are multiline
|
||||
multilinefields = {"Organization":1, "Environment":1, "Description":1,
|
||||
"How-To-Repeat":1, "Fix":1, "Release-Note":1,
|
||||
"Audit-Trail":1, "Unformatted":1}
|
||||
|
||||
# Mapping of GCC release to version. Our version string is updated every
|
||||
# so we need to funnel all release's with 3.4 in the string to be version
|
||||
# 3.4 for bug tracking purposes
|
||||
# The key is a regex to match, the value is the version it corresponds
|
||||
# with
|
||||
releasetovermap = {r"3\.4":"3.4", r"3\.3":"3.3", r"3\.2\.2":"3.2.2",
|
||||
r"3\.2\.1":"3.2.1", r"3\.2":"3.2", r"3\.1\.2":"3.1.2",
|
||||
r"3\.1\.1":"3.1.1", r"3\.1":"3.1", r"3\.0\.4":"3.0.4",
|
||||
r"3\.0\.3":"3.0.3", r"3\.0\.2":"3.0.2", r"3\.0\.1":"3.0.1",
|
||||
r"3\.0":"3.0", r"2\.95\.4":"2.95.4", r"2\.95\.3":"2.95.3",
|
||||
r"2\.95\.2":"2.95.2", r"2\.95\.1":"2.95.1",
|
||||
r"2\.95":"2.95", r"2\.97":"2.97",
|
||||
r"2\.96.*[rR][eE][dD].*[hH][aA][tT]":"2.96 (redhat)",
|
||||
r"2\.96":"2.96"}
|
||||
|
||||
# These map the field name to the field id bugzilla assigns. We need
|
||||
# the id when doing bug activity.
|
||||
fieldids = {"State":8, "Responsible":15}
|
||||
|
||||
# These are the keywords we use in gcc bug tracking. They are transformed
|
||||
# into bugzilla keywords. The format here is <keyword>-><bugzilla keyword id>
|
||||
keywordids = {"wrong-code":1, "ice-on-legal-code":2, "ice-on-illegal-code":3,
|
||||
"rejects-legal":4, "accepts-illegal":5, "pessimizes-code":6}
|
||||
|
||||
# Map from GNATS states to Bugzilla states. Duplicates and reopened bugs
|
||||
# are handled when parsing the audit trail, so no need for them here.
|
||||
state_lookup = {"":"NEW", "open":"ASSIGNED", "analyzed":"ASSIGNED",
|
||||
"feedback":"WAITING", "closed":"CLOSED",
|
||||
"suspended":"SUSPENDED"}
|
||||
|
||||
# Table of versions that exist in the bugs, built up as we go along
|
||||
versions_table = {}
|
||||
|
||||
# Delimiter gnatsweb uses for attachments
|
||||
attachment_delimiter = "----gnatsweb-attachment----\n"
|
||||
|
||||
# Here starts the various regular expressions we use
|
||||
# Matches an entire GNATS single line field
|
||||
gnatfieldre = re.compile(r"""^([>\w\-]+)\s*:\s*(.*)\s*$""")
|
||||
|
||||
# Matches the name of a GNATS field
|
||||
fieldnamere = re.compile(r"""^>(.*)$""")
|
||||
|
||||
# Matches the useless part of an envelope
|
||||
uselessre = re.compile(r"""^(\S*?):\s*""", re.MULTILINE)
|
||||
|
||||
# Matches the filename in a content disposition
|
||||
dispositionre = re.compile("(\\S+);\\s*filename=\"([^\"]+)\"")
|
||||
|
||||
# Matches the last changed date in the entire text of a bug
|
||||
# If you have other editable fields that get audit trail entries, modify this
|
||||
# The field names are explicitly listed in order to speed up matching
|
||||
lastdatere = re.compile(r"""^(?:(?:State|Responsible|Priority|Severity)-Changed-When: )(.+?)$""", re.MULTILINE)
|
||||
|
||||
# Matches the From line of an email or the first line of an audit trail entry
|
||||
# We use this re to find the begin lines of all the audit trail entries
|
||||
# The field names are explicitly listed in order to speed up matching
|
||||
fromtore=re.compile(r"""^(?:(?:State|Responsible|Priority|Severity)-Changed-From-To: |From: )""", re.MULTILINE)
|
||||
|
||||
# These re's match the various parts of an audit trail entry
|
||||
changedfromtore=re.compile(r"""^(\w+?)-Changed-From-To: (.+?)$""", re.MULTILINE)
|
||||
changedbyre=re.compile(r"""^\w+?-Changed-By: (.+?)$""", re.MULTILINE)
|
||||
changedwhenre=re.compile(r"""^\w+?-Changed-When: (.+?)$""", re.MULTILINE)
|
||||
changedwhyre=re.compile(r"""^\w+?-Changed-Why:\s*(.*?)$""", re.MULTILINE)
|
||||
|
||||
# This re matches audit trail text saying that the current bug is a duplicate of another
|
||||
duplicatere=re.compile(r"""(?:")?Dup(?:licate)?(?:d)?(?:")? of .*?(\d+)""", re.IGNORECASE | re.MULTILINE)
|
||||
|
||||
# Get the text of a From: line
|
||||
fromre=re.compile(r"""^From: (.*?)$""", re.MULTILINE)
|
||||
|
||||
# Get the text of a Date: Line
|
||||
datere=re.compile(r"""^Date: (.*?)$""", re.MULTILINE)
|
||||
|
||||
# Map of the responsible file to email addresses
|
||||
responsible_map = {}
|
||||
# List of records in the responsible file
|
||||
responsible_list = []
|
||||
# List of records in the categories file
|
||||
categories_list = []
|
||||
# List of pr's in the index
|
||||
pr_list = []
|
||||
# Map usernames to user ids
|
||||
usermapping = {}
|
||||
# Start with this user id
|
||||
userid_base = 2
|
||||
|
||||
# Name of gnats user
|
||||
gnats_username = "gnats@gcc.gnu.org"
|
||||
# Name of unassigned user
|
||||
unassigned_username = "unassigned@gcc.gnu.org"
|
||||
|
||||
gnats_db_dir = "."
|
||||
product = "gcc"
|
||||
productdesc = "GNU Compiler Connection"
|
||||
milestoneurl = "http://gcc/gnu.org"
|
||||
defaultmilestone = "3.4"
|
||||
|
||||
def write_non_bug_tables():
|
||||
""" Write out the non-bug related tables, such as products, profiles, etc."""
|
||||
# Set all non-unconfirmed bugs's everconfirmed flag
|
||||
print >>outfile, "update bugs set everconfirmed=1 where bug_status != 'UNCONFIRMED';"
|
||||
|
||||
# Set all bugs assigned to the unassigned user to NEW
|
||||
print >>outfile, "update bugs set bug_status='NEW',assigned_to='NULL' where bug_status='ASSIGNED' AND assigned_to=3;"
|
||||
|
||||
# Insert the products
|
||||
print >>outfile, "\ninsert into products ("
|
||||
print >>outfile, " product, description, milestoneurl, disallownew,"
|
||||
print >>outfile, " defaultmilestone, votestoconfirm) values ("
|
||||
print >>outfile, " '%s', '%s', '%s', 0, '%s', 1);" % (product,
|
||||
productdesc,
|
||||
milestoneurl,
|
||||
defaultmilestone)
|
||||
|
||||
# Insert the components
|
||||
for category in categories_list:
|
||||
component = SqlQuote(category[0])
|
||||
productstr = SqlQuote(product)
|
||||
description = SqlQuote(category[1])
|
||||
initialowner = SqlQuote("3")
|
||||
print >>outfile, "\ninsert into components (";
|
||||
print >>outfile, " value, program, initialowner, initialqacontact,"
|
||||
print >>outfile, " description) values ("
|
||||
print >>outfile, " %s, %s, %s, '', %s);" % (component, productstr,
|
||||
initialowner, description)
|
||||
|
||||
# Insert the versions
|
||||
for productstr, version_list in versions_table.items():
|
||||
productstr = SqlQuote(productstr)
|
||||
for version in version_list:
|
||||
version = SqlQuote(version)
|
||||
print >>outfile, "\ninsert into versions (value, program) "
|
||||
print >>outfile, " values (%s, %s);" % (version, productstr)
|
||||
|
||||
# Insert the users
|
||||
for username, userid in usermapping.items():
|
||||
realname = map_username_to_realname(username)
|
||||
username = SqlQuote(username)
|
||||
realname = SqlQuote(realname)
|
||||
print >>outfile, "\ninsert into profiles ("
|
||||
print >>outfile, " userid, login_name, password, cryptpassword, realname, groupset"
|
||||
print >>outfile, ") values ("
|
||||
print >>outfile, "%s,%s,'password',encrypt('password'), %s, 0);" % (userid, username, realname)
|
||||
print >>outfile, "update profiles set groupset=1 << 32 where login_name like '%\@gcc.gnu.org';"
|
||||
|
||||
def unixdate2datetime(unixdate):
|
||||
""" Convert a unix date to a datetime value """
|
||||
year, month, day, hour, min, sec, x, x, x, x = email.Utils.parsedate_tz(unixdate)
|
||||
return "%d-%02d-%02d %02d:%02d:%02d" % (year,month,day,hour,min,sec)
|
||||
|
||||
def unixdate2timestamp(unixdate):
|
||||
""" Convert a unix date to a timestamp value """
|
||||
year, month, day, hour, min, sec, x, x, x, x = email.Utils.parsedate_tz(unixdate)
|
||||
return "%d%02d%02d%02d%02d%02d" % (year,month,day,hour,min,sec)
|
||||
|
||||
def SqlQuote(str):
|
||||
""" Perform SQL quoting on a string """
|
||||
return "'%s'" % str.replace("'", """''""").replace("\\", "\\\\").replace("\0","\\0")
|
||||
|
||||
def convert_gccver_to_ver(gccver):
|
||||
""" Given a gcc version, convert it to a Bugzilla version. """
|
||||
for k in releasetovermap.keys():
|
||||
if re.search(".*%s.*" % k, gccver) is not None:
|
||||
return releasetovermap[k]
|
||||
result = re.search(r""".*(\d\.\d) \d+ \(experimental\).*""", gccver)
|
||||
if result is not None:
|
||||
return result.group(1)
|
||||
return "unknown"
|
||||
|
||||
def load_index(fname):
|
||||
""" Load in the GNATS index file """
|
||||
global pr_list
|
||||
ifp = open(fname)
|
||||
for record in ifp.xreadlines():
|
||||
fields = record.split("|")
|
||||
pr_list.append(fields[0])
|
||||
ifp.close()
|
||||
|
||||
def load_categories(fname):
|
||||
""" Load in the GNATS categories file """
|
||||
global categories_list
|
||||
cfp = open(fname)
|
||||
for record in cfp.xreadlines():
|
||||
if re.search("^#", record) is not None:
|
||||
continue
|
||||
categories_list.append(record.split(":"))
|
||||
cfp.close()
|
||||
|
||||
def map_username_to_realname(username):
|
||||
""" Given a username, find the real name """
|
||||
name = username
|
||||
name = re.sub("@.*", "", name)
|
||||
for responsible_record in responsible_list:
|
||||
if responsible_record[0] == name:
|
||||
return responsible_record[1]
|
||||
if len(responsible_record) > 2:
|
||||
if responsible_record[2] == username:
|
||||
return responsible_record[1]
|
||||
return ""
|
||||
|
||||
|
||||
def get_userid(responsible):
|
||||
""" Given an email address, get the user id """
|
||||
global responsible_map
|
||||
global usermapping
|
||||
global userid_base
|
||||
if responsible is None:
|
||||
return -1
|
||||
responsible = responsible.lower()
|
||||
responsible = re.sub("sources.redhat.com", "gcc.gnu.org", responsible)
|
||||
if responsible_map.has_key(responsible):
|
||||
responsible = responsible_map[responsible]
|
||||
if usermapping.has_key(responsible):
|
||||
return usermapping[responsible]
|
||||
else:
|
||||
usermapping[responsible] = userid_base
|
||||
userid_base += 1
|
||||
return usermapping[responsible]
|
||||
|
||||
def load_responsible(fname):
|
||||
""" Load in the GNATS responsible file """
|
||||
global responsible_map
|
||||
global responsible_list
|
||||
rfp = open(fname)
|
||||
for record in rfp.xreadlines():
|
||||
if re.search("^#", record) is not None:
|
||||
continue
|
||||
split_record = record.split(":")
|
||||
responsible_map[split_record[0]] = split_record[2].rstrip()
|
||||
responsible_list.append(record.split(":"))
|
||||
rfp.close()
|
||||
|
||||
def split_csl(list):
|
||||
""" Split a comma seperated list """
|
||||
newlist = re.split(r"""\s*,\s*""", list)
|
||||
return newlist
|
||||
|
||||
def fix_email_addrs(addrs):
|
||||
""" Perform various fixups and cleaning on an e-mail address """
|
||||
addrs = split_csl(addrs)
|
||||
trimmed_addrs = []
|
||||
for addr in addrs:
|
||||
addr = re.sub(r"""\(.*\)""","",addr)
|
||||
addr = re.sub(r""".*<(.*)>.*""","\\1",addr)
|
||||
addr = addr.rstrip()
|
||||
addr = addr.lstrip()
|
||||
trimmed_addrs.append(addr)
|
||||
addrs = ", ".join(trimmed_addrs)
|
||||
return addrs
|
||||
|
||||
class Bugzillabug(object):
|
||||
""" Class representing a bugzilla bug """
|
||||
def __init__(self, gbug):
|
||||
""" Initialize a bugzilla bug from a GNATS bug. """
|
||||
self.bug_id = gbug.bug_id
|
||||
self.long_descs = []
|
||||
self.bug_ccs = [get_userid("gcc-bugs@gcc.gnu.org")]
|
||||
self.bug_activity = []
|
||||
self.attachments = gbug.attachments
|
||||
self.gnatsfields = gbug.fields
|
||||
self.need_unformatted = gbug.has_unformatted_attach == 0
|
||||
self.need_unformatted &= gbug.fields.has_key("Unformatted")
|
||||
self.translate_pr()
|
||||
self.update_versions()
|
||||
if self.fields.has_key("Audit-Trail"):
|
||||
self.parse_audit_trail()
|
||||
self.write_bug()
|
||||
|
||||
def parse_fromto(type, string):
|
||||
""" Parses the from and to parts of a changed-from-to line """
|
||||
fromstr = ""
|
||||
tostr = ""
|
||||
|
||||
# Some slightly messed up changed lines have unassigned-new,
|
||||
# instead of unassigned->new. So we make the > optional.
|
||||
result = re.search(r"""(.*)-(?:>?)(.*)""", string)
|
||||
|
||||
# Only know how to handle parsing of State and Responsible
|
||||
# changed-from-to right now
|
||||
if type == "State":
|
||||
fromstr = state_lookup[result.group(1)]
|
||||
tostr = state_lookup[result.group(2)]
|
||||
elif type == "Responsible":
|
||||
if result.group(1) != "":
|
||||
fromstr = result.group(1)
|
||||
if result.group(2) != "":
|
||||
tostr = result.group(2)
|
||||
if responsible_map.has_key(fromstr):
|
||||
fromstr = responsible_map[fromstr]
|
||||
if responsible_map.has_key(tostr):
|
||||
tostr = responsible_map[tostr]
|
||||
return (fromstr, tostr)
|
||||
parse_fromto = staticmethod(parse_fromto)
|
||||
|
||||
def parse_audit_trail(self):
|
||||
""" Parse a GNATS audit trail """
|
||||
trail = self.fields["Audit-Trail"]
|
||||
# Begin to split the audit trail into pieces
|
||||
result = fromtore.finditer(trail)
|
||||
starts = []
|
||||
ends = []
|
||||
pieces = []
|
||||
# Make a list of the pieces
|
||||
for x in result:
|
||||
pieces.append (x)
|
||||
# Find the start and end of each piece
|
||||
if len(pieces) > 0:
|
||||
for x in xrange(len(pieces)-1):
|
||||
starts.append(pieces[x].start())
|
||||
ends.append(pieces[x+1].start())
|
||||
starts.append(pieces[-1].start())
|
||||
ends.append(len(trail))
|
||||
pieces = []
|
||||
# Now make the list of actual text of the pieces
|
||||
for x in xrange(len(starts)):
|
||||
pieces.append(trail[starts[x]:ends[x]])
|
||||
# And parse the actual pieces
|
||||
for piece in pieces:
|
||||
result = changedfromtore.search(piece)
|
||||
# See what things we actually have inside this entry, and
|
||||
# handle them approriately
|
||||
if result is not None:
|
||||
type = result.group(1)
|
||||
changedfromto = result.group(2)
|
||||
# If the bug was reopened, mark it as such
|
||||
if changedfromto.find("closed->analyzed") != -1:
|
||||
if self.fields["bug_status"] == "'NEW'":
|
||||
self.fields["bug_status"] = "'REOPENED'"
|
||||
if type == "State" or type == "Responsible":
|
||||
oldstate, newstate = self.parse_fromto (type, changedfromto)
|
||||
result = changedbyre.search(piece)
|
||||
if result is not None:
|
||||
changedby = result.group(1)
|
||||
result = changedwhenre.search(piece)
|
||||
if result is not None:
|
||||
changedwhen = result.group(1)
|
||||
changedwhen = unixdate2datetime(changedwhen)
|
||||
changedwhen = SqlQuote(changedwhen)
|
||||
result = changedwhyre.search(piece)
|
||||
changedwhy = piece[result.start(1):]
|
||||
#changedwhy = changedwhy.lstrip()
|
||||
changedwhy = changedwhy.rstrip()
|
||||
changedby = get_userid(changedby)
|
||||
# Put us on the cc list if we aren't there already
|
||||
if changedby != self.fields["userid"] \
|
||||
and changedby not in self.bug_ccs:
|
||||
self.bug_ccs.append(changedby)
|
||||
# If it's a duplicate, mark it as such
|
||||
result = duplicatere.search(changedwhy)
|
||||
if result is not None:
|
||||
newtext = "*** This bug has been marked as a duplicate of %s ***" % result.group(1)
|
||||
newtext = SqlQuote(newtext)
|
||||
self.long_descs.append((self.bug_id, changedby,
|
||||
changedwhen, newtext))
|
||||
self.fields["bug_status"] = "'RESOLVED'"
|
||||
self.fields["resolution"] = "'DUPLICATE'"
|
||||
self.fields["userid"] = changedby
|
||||
else:
|
||||
newtext = "%s-Changed-From-To: %s\n%s-Changed-Why: %s\n" % (type, changedfromto, type, changedwhy)
|
||||
newtext = SqlQuote(newtext)
|
||||
self.long_descs.append((self.bug_id, changedby,
|
||||
changedwhen, newtext))
|
||||
if type == "State" or type == "Responsible":
|
||||
newstate = SqlQuote("%s" % newstate)
|
||||
oldstate = SqlQuote("%s" % oldstate)
|
||||
fieldid = fieldids[type]
|
||||
self.bug_activity.append((newstate, oldstate, fieldid, changedby, changedwhen))
|
||||
|
||||
else:
|
||||
# It's an email
|
||||
result = fromre.search(piece)
|
||||
if result is None:
|
||||
continue
|
||||
fromstr = result.group(1)
|
||||
fromstr = fix_email_addrs(fromstr)
|
||||
fromstr = get_userid(fromstr)
|
||||
result = datere.search(piece)
|
||||
if result is None:
|
||||
continue
|
||||
datestr = result.group(1)
|
||||
datestr = SqlQuote(unixdate2timestamp(datestr))
|
||||
if fromstr != self.fields["userid"] \
|
||||
and fromstr not in self.bug_ccs:
|
||||
self.bug_ccs.append(fromstr)
|
||||
self.long_descs.append((self.bug_id, fromstr, datestr,
|
||||
SqlQuote(piece)))
|
||||
|
||||
|
||||
|
||||
def write_bug(self):
|
||||
""" Output a bug to the data file """
|
||||
fields = self.fields
|
||||
print >>outfile, "\ninsert into bugs("
|
||||
print >>outfile, " bug_id, assigned_to, bug_severity, priority, bug_status, creation_ts, delta_ts,"
|
||||
print >>outfile, " short_desc,"
|
||||
print >>outfile, " reporter, version,"
|
||||
print >>outfile, " product, component, resolution, target_milestone, qa_contact,"
|
||||
print >>outfile, " gccbuild, gcctarget, gcchost, keywords"
|
||||
print >>outfile, " ) values ("
|
||||
print >>outfile, "%s, %s, %s, %s, %s, %s, %s," % (self.bug_id, fields["userid"], fields["bug_severity"], fields["priority"], fields["bug_status"], fields["creation_ts"], fields["delta_ts"])
|
||||
print >>outfile, "%s," % (fields["short_desc"])
|
||||
print >>outfile, "%s, %s," % (fields["reporter"], fields["version"])
|
||||
print >>outfile, "%s, %s, %s, %s, 0," %(fields["product"], fields["component"], fields["resolution"], fields["target_milestone"])
|
||||
print >>outfile, "%s, %s, %s, %s" % (fields["gccbuild"], fields["gcctarget"], fields["gcchost"], fields["keywords"])
|
||||
print >>outfile, ");"
|
||||
if self.fields["keywords"] != 0:
|
||||
print >>outfile, "\ninsert into keywords (bug_id, keywordid) values ("
|
||||
print >>outfile, " %s, %s);" % (self.bug_id, fields["keywordid"])
|
||||
for id, who, when, text in self.long_descs:
|
||||
print >>outfile, "\ninsert into longdescs ("
|
||||
print >>outfile, " bug_id, who, bug_when, thetext) values("
|
||||
print >>outfile, " %s, %s, %s, %s);" % (id, who, when, text)
|
||||
for name, data, who in self.attachments:
|
||||
print >>outfile, "\ninsert into attachments ("
|
||||
print >>outfile, " bug_id, filename, description, mimetype, ispatch, submitter_id, thedata) values ("
|
||||
ftype = None
|
||||
# It's *magic*!
|
||||
if name.endswith(".ii") == 1:
|
||||
ftype = "text/x-c++"
|
||||
elif name.endswith(".i") == 1:
|
||||
ftype = "text/x-c"
|
||||
else:
|
||||
ftype = magicf.detect(cStringIO.StringIO(data))
|
||||
if ftype is None:
|
||||
ftype = "application/octet-stream"
|
||||
|
||||
print >>outfile, "%s,%s,%s, %s,0, %s,%s);" %(self.bug_id, SqlQuote(name), SqlQuote(name), SqlQuote (ftype), who, SqlQuote(zlib.compress(data)))
|
||||
for newstate, oldstate, fieldid, changedby, changedwhen in self.bug_activity:
|
||||
print >>outfile, "\ninsert into bugs_activity ("
|
||||
print >>outfile, " bug_id, who, bug_when, fieldid, added, removed) values ("
|
||||
print >>outfile, " %s, %s, %s, %s, %s, %s);" % (self.bug_id,
|
||||
changedby,
|
||||
changedwhen,
|
||||
fieldid,
|
||||
newstate,
|
||||
oldstate)
|
||||
for cc in self.bug_ccs:
|
||||
print >>outfile, "\ninsert into cc(bug_id, who) values (%s, %s);" %(self.bug_id, cc)
|
||||
def update_versions(self):
|
||||
""" Update the versions table to account for the version on this bug """
|
||||
global versions_table
|
||||
if self.fields.has_key("Release") == 0 \
|
||||
or self.fields.has_key("Category") == 0:
|
||||
return
|
||||
curr_product = "gcc"
|
||||
curr_version = self.fields["Release"]
|
||||
if curr_version == "":
|
||||
return
|
||||
curr_version = convert_gccver_to_ver (curr_version)
|
||||
if versions_table.has_key(curr_product) == 0:
|
||||
versions_table[curr_product] = []
|
||||
for version in versions_table[curr_product]:
|
||||
if version == curr_version:
|
||||
return
|
||||
versions_table[curr_product].append(curr_version)
|
||||
def translate_pr(self):
|
||||
""" Transform a GNATS PR into a Bugzilla bug """
|
||||
self.fields = self.gnatsfields
|
||||
if (self.fields.has_key("Organization") == 0) \
|
||||
or self.fields["Organization"].find("GCC"):
|
||||
self.fields["Originator"] = ""
|
||||
self.fields["Organization"] = ""
|
||||
self.fields["Organization"].lstrip()
|
||||
if (self.fields.has_key("Release") == 0) \
|
||||
or self.fields["Release"] == "" \
|
||||
or self.fields["Release"].find("unknown-1.0") != -1:
|
||||
self.fields["Release"]="unknown"
|
||||
if self.fields.has_key("Responsible"):
|
||||
result = re.search(r"""\w+""", self.fields["Responsible"])
|
||||
self.fields["Responsible"] = "%s%s" % (result.group(0), "@gcc.gnu.org")
|
||||
self.fields["gcchost"] = ""
|
||||
self.fields["gcctarget"] = ""
|
||||
self.fields["gccbuild"] = ""
|
||||
if self.fields.has_key("Environment"):
|
||||
result = re.search("^host: (.+?)$", self.fields["Environment"],
|
||||
re.MULTILINE)
|
||||
if result is not None:
|
||||
self.fields["gcchost"] = result.group(1)
|
||||
result = re.search("^target: (.+?)$", self.fields["Environment"],
|
||||
re.MULTILINE)
|
||||
if result is not None:
|
||||
self.fields["gcctarget"] = result.group(1)
|
||||
result = re.search("^build: (.+?)$", self.fields["Environment"],
|
||||
re.MULTILINE)
|
||||
if result is not None:
|
||||
self.fields["gccbuild"] = result.group(1)
|
||||
self.fields["userid"] = get_userid(self.fields["Responsible"])
|
||||
self.fields["bug_severity"] = "normal"
|
||||
if self.fields["Class"] == "change-request":
|
||||
self.fields["bug_severity"] = "enhancement"
|
||||
elif self.fields.has_key("Severity"):
|
||||
if self.fields["Severity"] == "critical":
|
||||
self.fields["bug_severity"] = "critical"
|
||||
elif self.fields["Severity"] == "serious":
|
||||
self.fields["bug_severity"] = "major"
|
||||
elif self.fields.has_key("Synopsis"):
|
||||
if re.search("crash|assert", self.fields["Synopsis"]):
|
||||
self.fields["bug_severity"] = "critical"
|
||||
elif re.search("wrong|error", self.fields["Synopsis"]):
|
||||
self.fields["bug_severity"] = "major"
|
||||
self.fields["bug_severity"] = SqlQuote(self.fields["bug_severity"])
|
||||
self.fields["keywords"] = 0
|
||||
if keywordids.has_key(self.fields["Class"]):
|
||||
self.fields["keywords"] = self.fields["Class"]
|
||||
self.fields["keywordid"] = keywordids[self.fields["Class"]]
|
||||
self.fields["keywords"] = SqlQuote(self.fields["keywords"])
|
||||
self.fields["priority"] = "P1"
|
||||
if self.fields.has_key("Severity") and self.fields.has_key("Priority"):
|
||||
severity = self.fields["Severity"]
|
||||
priority = self.fields["Priority"]
|
||||
if severity == "critical":
|
||||
if priority == "high":
|
||||
self.fields["priority"] = "P1"
|
||||
else:
|
||||
self.fields["priority"] = "P2"
|
||||
elif severity == "serious":
|
||||
if priority == "low":
|
||||
self.fields["priority"] = "P4"
|
||||
else:
|
||||
self.fields["priority"] = "P3"
|
||||
else:
|
||||
if priority == "high":
|
||||
self.fields["priority"] = "P4"
|
||||
else:
|
||||
self.fields["priority"] = "P5"
|
||||
self.fields["priority"] = SqlQuote(self.fields["priority"])
|
||||
state = self.fields["State"]
|
||||
if (state == "open" or state == "analyzed") and self.fields["userid"] != 3:
|
||||
self.fields["bug_status"] = "ASSIGNED"
|
||||
self.fields["resolution"] = ""
|
||||
elif state == "feedback":
|
||||
self.fields["bug_status"] = "WAITING"
|
||||
self.fields["resolution"] = ""
|
||||
elif state == "closed":
|
||||
self.fields["bug_status"] = "CLOSED"
|
||||
if self.fields.has_key("Class"):
|
||||
theclass = self.fields["Class"]
|
||||
if theclass.find("duplicate") != -1:
|
||||
self.fields["resolution"]="DUPLICATE"
|
||||
elif theclass.find("mistaken") != -1:
|
||||
self.fields["resolution"]="INVALID"
|
||||
else:
|
||||
self.fields["resolution"]="FIXED"
|
||||
else:
|
||||
self.fields["resolution"]="FIXED"
|
||||
elif state == "suspended":
|
||||
self.fields["bug_status"] = "SUSPENDED"
|
||||
self.fields["resolution"] = ""
|
||||
elif state == "analyzed" and self.fields["userid"] == 3:
|
||||
self.fields["bug_status"] = "NEW"
|
||||
self.fields["resolution"] = ""
|
||||
else:
|
||||
self.fields["bug_status"] = "UNCONFIRMED"
|
||||
self.fields["resolution"] = ""
|
||||
self.fields["bug_status"] = SqlQuote(self.fields["bug_status"])
|
||||
self.fields["resolution"] = SqlQuote(self.fields["resolution"])
|
||||
self.fields["creation_ts"] = ""
|
||||
if self.fields.has_key("Arrival-Date") and self.fields["Arrival-Date"] != "":
|
||||
self.fields["creation_ts"] = unixdate2datetime(self.fields["Arrival-Date"])
|
||||
self.fields["creation_ts"] = SqlQuote(self.fields["creation_ts"])
|
||||
self.fields["delta_ts"] = ""
|
||||
if self.fields.has_key("Audit-Trail"):
|
||||
result = lastdatere.findall(self.fields["Audit-Trail"])
|
||||
result.reverse()
|
||||
if len(result) > 0:
|
||||
self.fields["delta_ts"] = unixdate2timestamp(result[0])
|
||||
if self.fields["delta_ts"] == "":
|
||||
if self.fields.has_key("Arrival-Date") and self.fields["Arrival-Date"] != "":
|
||||
self.fields["delta_ts"] = unixdate2timestamp(self.fields["Arrival-Date"])
|
||||
self.fields["delta_ts"] = SqlQuote(self.fields["delta_ts"])
|
||||
self.fields["short_desc"] = SqlQuote(self.fields["Synopsis"])
|
||||
if self.fields.has_key("Reply-To") and self.fields["Reply-To"] != "":
|
||||
self.fields["reporter"] = get_userid(self.fields["Reply-To"])
|
||||
elif self.fields.has_key("Mail-Header"):
|
||||
result = re.search(r"""From .*?([\w.]+@[\w.]+)""", self.fields["Mail-Header"])
|
||||
if result:
|
||||
self.fields["reporter"] = get_userid(result.group(1))
|
||||
else:
|
||||
self.fields["reporter"] = get_userid(gnats_username)
|
||||
else:
|
||||
self.fields["reporter"] = get_userid(gnats_username)
|
||||
long_desc = self.fields["Description"]
|
||||
long_desc2 = ""
|
||||
for field in ["Release", "Environment", "How-To-Repeat"]:
|
||||
if self.fields.has_key(field) and self.fields[field] != "":
|
||||
long_desc += ("\n\n%s:\n" % field) + self.fields[field]
|
||||
if self.fields.has_key("Fix") and self.fields["Fix"] != "":
|
||||
long_desc2 = "Fix:\n" + self.fields["Fix"]
|
||||
if self.need_unformatted == 1 and self.fields["Unformatted"] != "":
|
||||
long_desc += "\n\nUnformatted:\n" + self.fields["Unformatted"]
|
||||
if long_desc != "":
|
||||
self.long_descs.append((self.bug_id, self.fields["reporter"],
|
||||
self.fields["creation_ts"],
|
||||
SqlQuote(long_desc)))
|
||||
if long_desc2 != "":
|
||||
self.long_descs.append((self.bug_id, self.fields["reporter"],
|
||||
self.fields["creation_ts"],
|
||||
SqlQuote(long_desc2)))
|
||||
for field in ["gcchost", "gccbuild", "gcctarget"]:
|
||||
self.fields[field] = SqlQuote(self.fields[field])
|
||||
self.fields["version"] = ""
|
||||
if self.fields["Release"] != "":
|
||||
self.fields["version"] = convert_gccver_to_ver (self.fields["Release"])
|
||||
self.fields["version"] = SqlQuote(self.fields["version"])
|
||||
self.fields["product"] = SqlQuote("gcc")
|
||||
self.fields["component"] = "invalid"
|
||||
if self.fields.has_key("Category"):
|
||||
self.fields["component"] = self.fields["Category"]
|
||||
self.fields["component"] = SqlQuote(self.fields["component"])
|
||||
self.fields["target_milestone"] = "---"
|
||||
if self.fields["version"].find("3.4") != -1:
|
||||
self.fields["target_milestone"] = "3.4"
|
||||
self.fields["target_milestone"] = SqlQuote(self.fields["target_milestone"])
|
||||
if self.fields["userid"] == 2:
|
||||
self.fields["userid"] = "\'NULL\'"
|
||||
|
||||
class GNATSbug(object):
|
||||
""" Represents a single GNATS PR """
|
||||
def __init__(self, filename):
|
||||
self.attachments = []
|
||||
self.has_unformatted_attach = 0
|
||||
fp = open (filename)
|
||||
self.fields = self.parse_pr(fp.xreadlines())
|
||||
self.bug_id = int(self.fields["Number"])
|
||||
if self.fields.has_key("Unformatted"):
|
||||
self.find_gnatsweb_attachments()
|
||||
if self.fields.has_key("How-To-Repeat"):
|
||||
self.find_regular_attachments("How-To-Repeat")
|
||||
if self.fields.has_key("Fix"):
|
||||
self.find_regular_attachments("Fix")
|
||||
|
||||
def get_attacher(fields):
|
||||
if fields.has_key("Reply-To") and fields["Reply-To"] != "":
|
||||
return get_userid(fields["Reply-To"])
|
||||
else:
|
||||
result = None
|
||||
if fields.has_key("Mail-Header"):
|
||||
result = re.search(r"""From .*?([\w.]+\@[\w.]+)""",
|
||||
fields["Mail-Header"])
|
||||
if result is not None:
|
||||
reporter = get_userid(result.group(1))
|
||||
else:
|
||||
reporter = get_userid(gnats_username)
|
||||
get_attacher = staticmethod(get_attacher)
|
||||
def find_regular_attachments(self, which):
|
||||
fields = self.fields
|
||||
while re.search("^begin [0-7]{3}", fields[which],
|
||||
re.DOTALL | re.MULTILINE):
|
||||
outfp = cStringIO.StringIO()
|
||||
infp = cStringIO.StringIO(fields[which])
|
||||
filename, start, end = specialuu.decode(infp, outfp, quiet=0)
|
||||
fields[which]=fields[which].replace(fields[which][start:end],
|
||||
"See attachments for %s\n" % filename)
|
||||
self.attachments.append((filename, outfp.getvalue(),
|
||||
self.get_attacher(fields)))
|
||||
|
||||
def decode_gnatsweb_attachment(self, attachment):
|
||||
result = re.split(r"""\n\n""", attachment, 1)
|
||||
if len(result) == 1:
|
||||
return -1
|
||||
envelope, body = result
|
||||
envelope = uselessre.split(envelope)
|
||||
envelope.pop(0)
|
||||
# Turn the list of key, value into a dict of key => value
|
||||
attachinfo = dict([(envelope[i], envelope[i+1]) for i in xrange(0,len(envelope),2)])
|
||||
for x in attachinfo.keys():
|
||||
attachinfo[x] = attachinfo[x].rstrip()
|
||||
if (attachinfo.has_key("Content-Type") == 0) or \
|
||||
(attachinfo.has_key("Content-Disposition") == 0):
|
||||
raise ValueError, "Unable to parse file attachment"
|
||||
result = dispositionre.search(attachinfo["Content-Disposition"])
|
||||
filename = result.group(2)
|
||||
filename = re.sub(".*/","", filename)
|
||||
filename = re.sub(".*\\\\","", filename)
|
||||
attachinfo["filename"]=filename
|
||||
result = re.search("""(\S+);.*""", attachinfo["Content-Type"])
|
||||
if result is not None:
|
||||
attachinfo["Content-Type"] = result.group(1)
|
||||
if attachinfo.has_key("Content-Transfer-Encoding"):
|
||||
if attachinfo["Content-Transfer-Encoding"] == "base64":
|
||||
attachinfo["data"] = base64.decodestring(body)
|
||||
else:
|
||||
attachinfo["data"]=body
|
||||
|
||||
return (attachinfo["filename"], attachinfo["data"],
|
||||
self.get_attacher(self.fields))
|
||||
|
||||
def find_gnatsweb_attachments(self):
|
||||
fields = self.fields
|
||||
attachments = re.split(attachment_delimiter, fields["Unformatted"])
|
||||
fields["Unformatted"] = attachments.pop(0)
|
||||
for attachment in attachments:
|
||||
result = self.decode_gnatsweb_attachment (attachment)
|
||||
if result != -1:
|
||||
self.attachments.append(result)
|
||||
self.has_unformatted_attach = 1
|
||||
def parse_pr(lines):
|
||||
#fields = {"envelope":[]}
|
||||
fields = {"envelope":array.array("c")}
|
||||
hdrmulti = "envelope"
|
||||
for line in lines:
|
||||
line = line.rstrip('\n')
|
||||
line += '\n'
|
||||
result = gnatfieldre.search(line)
|
||||
if result is None:
|
||||
if hdrmulti != "":
|
||||
if fields.has_key(hdrmulti):
|
||||
#fields[hdrmulti].append(line)
|
||||
fields[hdrmulti].fromstring(line)
|
||||
else:
|
||||
#fields[hdrmulti] = [line]
|
||||
fields[hdrmulti] = array.array("c", line)
|
||||
continue
|
||||
hdr, arg = result.groups()
|
||||
ghdr = "*not valid*"
|
||||
result = fieldnamere.search(hdr)
|
||||
if result != None:
|
||||
ghdr = result.groups()[0]
|
||||
if ghdr in fieldnames:
|
||||
if multilinefields.has_key(ghdr):
|
||||
hdrmulti = ghdr
|
||||
#fields[ghdr] = [""]
|
||||
fields[ghdr] = array.array("c")
|
||||
else:
|
||||
hdrmulti = ""
|
||||
#fields[ghdr] = [arg]
|
||||
fields[ghdr] = array.array("c", arg)
|
||||
elif hdrmulti != "":
|
||||
#fields[hdrmulti].append(line)
|
||||
fields[hdrmulti].fromstring(line)
|
||||
if hdrmulti == "envelope" and \
|
||||
(hdr == "Reply-To" or hdr == "From" \
|
||||
or hdr == "X-GNATS-Notify"):
|
||||
arg = fix_email_addrs(arg)
|
||||
#fields[hdr] = [arg]
|
||||
fields[hdr] = array.array("c", arg)
|
||||
if fields.has_key("Reply-To") and len(fields["Reply-To"]) > 0:
|
||||
fields["Reply-To"] = fields["Reply-To"]
|
||||
else:
|
||||
fields["Reply-To"] = fields["From"]
|
||||
if fields.has_key("From"):
|
||||
del fields["From"]
|
||||
if fields.has_key("X-GNATS-Notify") == 0:
|
||||
fields["X-GNATS-Notify"] = array.array("c")
|
||||
#fields["X-GNATS-Notify"] = ""
|
||||
for x in fields.keys():
|
||||
fields[x] = fields[x].tostring()
|
||||
#fields[x] = "".join(fields[x])
|
||||
for x in fields.keys():
|
||||
if multilinefields.has_key(x):
|
||||
fields[x] = fields[x].rstrip()
|
||||
|
||||
return fields
|
||||
parse_pr = staticmethod(parse_pr)
|
||||
load_index("%s/gnats-adm/index" % gnats_db_dir)
|
||||
load_categories("%s/gnats-adm/categories" % gnats_db_dir)
|
||||
load_responsible("%s/gnats-adm/responsible" % gnats_db_dir)
|
||||
get_userid(gnats_username)
|
||||
get_userid(unassigned_username)
|
||||
for x in pr_list:
|
||||
print "Processing %s..." % x
|
||||
a = GNATSbug ("%s/%s" % (gnats_db_dir, x))
|
||||
b = Bugzillabug(a)
|
||||
write_non_bug_tables()
|
||||
outfile.close()
|
||||
@@ -1,712 +0,0 @@
|
||||
# Found on a russian zope mailing list, and modified to fix bugs in parsing
|
||||
# the magic file and string making
|
||||
# -- Daniel Berlin <dberlin@dberlin.org>
|
||||
import sys, struct, time, re, exceptions, pprint, stat, os, pwd, grp
|
||||
|
||||
_mew = 0
|
||||
|
||||
# _magic='/tmp/magic'
|
||||
# _magic='/usr/share/magic.mime'
|
||||
_magic='/usr/share/magic.mime'
|
||||
mime = 1
|
||||
|
||||
_ldate_adjust = lambda x: time.mktime( time.gmtime(x) )
|
||||
|
||||
BUFFER_SIZE = 1024 * 128 # 128K should be enough...
|
||||
|
||||
class MagicError(exceptions.Exception): pass
|
||||
|
||||
def _handle(fmt='@x',adj=None): return fmt, struct.calcsize(fmt), adj
|
||||
|
||||
KnownTypes = {
|
||||
# 'byte':_handle('@b'),
|
||||
'byte':_handle('@B'),
|
||||
'ubyte':_handle('@B'),
|
||||
|
||||
'string':('s',0,None),
|
||||
'pstring':_handle('p'),
|
||||
|
||||
# 'short':_handle('@h'),
|
||||
# 'beshort':_handle('>h'),
|
||||
# 'leshort':_handle('<h'),
|
||||
'short':_handle('@H'),
|
||||
'beshort':_handle('>H'),
|
||||
'leshort':_handle('<H'),
|
||||
'ushort':_handle('@H'),
|
||||
'ubeshort':_handle('>H'),
|
||||
'uleshort':_handle('<H'),
|
||||
|
||||
'long':_handle('@l'),
|
||||
'belong':_handle('>l'),
|
||||
'lelong':_handle('<l'),
|
||||
'ulong':_handle('@L'),
|
||||
'ubelong':_handle('>L'),
|
||||
'ulelong':_handle('<L'),
|
||||
|
||||
'date':_handle('=l'),
|
||||
'bedate':_handle('>l'),
|
||||
'ledate':_handle('<l'),
|
||||
'ldate':_handle('=l',_ldate_adjust),
|
||||
'beldate':_handle('>l',_ldate_adjust),
|
||||
'leldate':_handle('<l',_ldate_adjust),
|
||||
}
|
||||
|
||||
_mew_cnt = 0
|
||||
def mew(x):
|
||||
global _mew_cnt
|
||||
if _mew :
|
||||
if x=='.' :
|
||||
_mew_cnt += 1
|
||||
if _mew_cnt % 64 == 0 : sys.stderr.write( '\n' )
|
||||
sys.stderr.write( '.' )
|
||||
else:
|
||||
sys.stderr.write( '\b'+x )
|
||||
|
||||
def has_format(s):
|
||||
n = 0
|
||||
l = None
|
||||
for c in s :
|
||||
if c == '%' :
|
||||
if l == '%' : n -= 1
|
||||
else : n += 1
|
||||
l = c
|
||||
return n
|
||||
|
||||
def read_asciiz(file,size=None,pos=None):
|
||||
s = []
|
||||
if pos :
|
||||
mew('s')
|
||||
file.seek( pos, 0 )
|
||||
mew('z')
|
||||
if size is not None :
|
||||
s = [file.read( size ).split('\0')[0]]
|
||||
else:
|
||||
while 1 :
|
||||
c = file.read(1)
|
||||
if (not c) or (ord(c)==0) or (c=='\n') : break
|
||||
s.append (c)
|
||||
mew('Z')
|
||||
return ''.join(s)
|
||||
|
||||
def a2i(v,base=0):
|
||||
if v[-1:] in 'lL' : v = v[:-1]
|
||||
return int( v, base )
|
||||
|
||||
_cmap = {
|
||||
'\\' : '\\',
|
||||
'0' : '\0',
|
||||
}
|
||||
for c in range(ord('a'),ord('z')+1) :
|
||||
try : e = eval('"\\%c"' % chr(c))
|
||||
except ValueError : pass
|
||||
else : _cmap[chr(c)] = e
|
||||
else:
|
||||
del c
|
||||
del e
|
||||
|
||||
def make_string(s):
|
||||
return eval( '"'+s.replace('"','\\"')+'"')
|
||||
|
||||
class MagicTestError(MagicError): pass
|
||||
|
||||
class MagicTest:
|
||||
def __init__(self,offset,mtype,test,message,line=None,level=None):
|
||||
self.line, self.level = line, level
|
||||
self.mtype = mtype
|
||||
self.mtest = test
|
||||
self.subtests = []
|
||||
self.mask = None
|
||||
self.smod = None
|
||||
self.nmod = None
|
||||
self.offset, self.type, self.test, self.message = \
|
||||
offset,mtype,test,message
|
||||
if self.mtype == 'true' : return # XXX hack to enable level skips
|
||||
if test[-1:]=='\\' and test[-2:]!='\\\\' :
|
||||
self.test += 'n' # looks like someone wanted EOL to match?
|
||||
if mtype[:6]=='string' :
|
||||
if '/' in mtype : # for strings
|
||||
self.type, self.smod = \
|
||||
mtype[:mtype.find('/')], mtype[mtype.find('/')+1:]
|
||||
else:
|
||||
for nm in '&+-' :
|
||||
if nm in mtype : # for integer-based
|
||||
self.nmod, self.type, self.mask = (
|
||||
nm,
|
||||
mtype[:mtype.find(nm)],
|
||||
# convert mask to int, autodetect base
|
||||
int( mtype[mtype.find(nm)+1:], 0 )
|
||||
)
|
||||
break
|
||||
self.struct, self.size, self.cast = KnownTypes[ self.type ]
|
||||
def __str__(self):
|
||||
return '%s %s %s %s' % (
|
||||
self.offset, self.mtype, self.mtest, self.message
|
||||
)
|
||||
def __repr__(self):
|
||||
return 'MagicTest(%s,%s,%s,%s,line=%s,level=%s,subtests=\n%s%s)' % (
|
||||
`self.offset`, `self.mtype`, `self.mtest`, `self.message`,
|
||||
`self.line`, `self.level`,
|
||||
'\t'*self.level, pprint.pformat(self.subtests)
|
||||
)
|
||||
def run(self,file):
|
||||
result = ''
|
||||
do_close = 0
|
||||
try:
|
||||
if type(file) == type('x') :
|
||||
file = open( file, 'r', BUFFER_SIZE )
|
||||
do_close = 1
|
||||
# else:
|
||||
# saved_pos = file.tell()
|
||||
if self.mtype != 'true' :
|
||||
data = self.read(file)
|
||||
last = file.tell()
|
||||
else:
|
||||
data = last = None
|
||||
if self.check( data ) :
|
||||
result = self.message+' '
|
||||
if has_format( result ) : result %= data
|
||||
for test in self.subtests :
|
||||
m = test.run(file)
|
||||
if m is not None : result += m
|
||||
return make_string( result )
|
||||
finally:
|
||||
if do_close :
|
||||
file.close()
|
||||
# else:
|
||||
# file.seek( saved_pos, 0 )
|
||||
def get_mod_and_value(self):
|
||||
if self.type[-6:] == 'string' :
|
||||
# "something like\tthis\n"
|
||||
if self.test[0] in '=<>' :
|
||||
mod, value = self.test[0], make_string( self.test[1:] )
|
||||
else:
|
||||
mod, value = '=', make_string( self.test )
|
||||
else:
|
||||
if self.test[0] in '=<>&^' :
|
||||
mod, value = self.test[0], a2i(self.test[1:])
|
||||
elif self.test[0] == 'x':
|
||||
mod = self.test[0]
|
||||
value = 0
|
||||
else:
|
||||
mod, value = '=', a2i(self.test)
|
||||
return mod, value
|
||||
def read(self,file):
|
||||
mew( 's' )
|
||||
file.seek( self.offset(file), 0 ) # SEEK_SET
|
||||
mew( 'r' )
|
||||
try:
|
||||
data = rdata = None
|
||||
# XXX self.size might be 0 here...
|
||||
if self.size == 0 :
|
||||
# this is an ASCIIZ string...
|
||||
size = None
|
||||
if self.test != '>\\0' : # magic's hack for string read...
|
||||
value = self.get_mod_and_value()[1]
|
||||
size = (value=='\0') and None or len(value)
|
||||
rdata = data = read_asciiz( file, size=size )
|
||||
else:
|
||||
rdata = file.read( self.size )
|
||||
if not rdata or (len(rdata)!=self.size) : return None
|
||||
data = struct.unpack( self.struct, rdata )[0] # XXX hack??
|
||||
except:
|
||||
print >>sys.stderr, self
|
||||
print >>sys.stderr, '@%s struct=%s size=%d rdata=%s' % (
|
||||
self.offset, `self.struct`, self.size,`rdata`)
|
||||
raise
|
||||
mew( 'R' )
|
||||
if self.cast : data = self.cast( data )
|
||||
if self.mask :
|
||||
try:
|
||||
if self.nmod == '&' : data &= self.mask
|
||||
elif self.nmod == '+' : data += self.mask
|
||||
elif self.nmod == '-' : data -= self.mask
|
||||
else: raise MagicTestError(self.nmod)
|
||||
except:
|
||||
print >>sys.stderr,'data=%s nmod=%s mask=%s' % (
|
||||
`data`, `self.nmod`, `self.mask`
|
||||
)
|
||||
raise
|
||||
return data
|
||||
def check(self,data):
|
||||
mew('.')
|
||||
if self.mtype == 'true' :
|
||||
return '' # not None !
|
||||
mod, value = self.get_mod_and_value()
|
||||
if self.type[-6:] == 'string' :
|
||||
# "something like\tthis\n"
|
||||
if self.smod :
|
||||
xdata = data
|
||||
if 'b' in self.smod : # all blanks are optional
|
||||
xdata = ''.join( data.split() )
|
||||
value = ''.join( value.split() )
|
||||
if 'c' in self.smod : # all blanks are optional
|
||||
xdata = xdata.upper()
|
||||
value = value.upper()
|
||||
# if 'B' in self.smod : # compact blanks
|
||||
### XXX sorry, i don't understand this :-(
|
||||
# data = ' '.join( data.split() )
|
||||
# if ' ' not in data : return None
|
||||
else:
|
||||
xdata = data
|
||||
try:
|
||||
if mod == '=' : result = data == value
|
||||
elif mod == '<' : result = data < value
|
||||
elif mod == '>' : result = data > value
|
||||
elif mod == '&' : result = data & value
|
||||
elif mod == '^' : result = (data & (~value)) == 0
|
||||
elif mod == 'x' : result = 1
|
||||
else : raise MagicTestError(self.test)
|
||||
if result :
|
||||
zdata, zval = `data`, `value`
|
||||
if self.mtype[-6:]!='string' :
|
||||
try: zdata, zval = hex(data), hex(value)
|
||||
except: zdata, zval = `data`, `value`
|
||||
if 0 : print >>sys.stderr, '%s @%s %s:%s %s %s => %s (%s)' % (
|
||||
'>'*self.level, self.offset,
|
||||
zdata, self.mtype, `mod`, zval, `result`,
|
||||
self.message
|
||||
)
|
||||
return result
|
||||
except:
|
||||
print >>sys.stderr,'mtype=%s data=%s mod=%s value=%s' % (
|
||||
`self.mtype`, `data`, `mod`, `value`
|
||||
)
|
||||
raise
|
||||
def add(self,mt):
|
||||
if not isinstance(mt,MagicTest) :
|
||||
raise MagicTestError((mt,'incorrect subtest type %s'%(type(mt),)))
|
||||
if mt.level == self.level+1 :
|
||||
self.subtests.append( mt )
|
||||
elif self.subtests :
|
||||
self.subtests[-1].add( mt )
|
||||
elif mt.level > self.level+1 :
|
||||
# it's possible to get level 3 just after level 1 !!! :-(
|
||||
level = self.level + 1
|
||||
while level < mt.level :
|
||||
xmt = MagicTest(None,'true','x','',line=self.line,level=level)
|
||||
self.add( xmt )
|
||||
level += 1
|
||||
else:
|
||||
self.add( mt ) # retry...
|
||||
else:
|
||||
raise MagicTestError((mt,'incorrect subtest level %s'%(`mt.level`,)))
|
||||
def last_test(self):
|
||||
return self.subtests[-1]
|
||||
#end class MagicTest
|
||||
|
||||
class OffsetError(MagicError): pass
|
||||
|
||||
class Offset:
|
||||
pos_format = {'b':'<B','B':'>B','s':'<H','S':'>H','l':'<I','L':'>I',}
|
||||
pattern0 = re.compile(r''' # mere offset
|
||||
^
|
||||
&? # possible ampersand
|
||||
( 0 # just zero
|
||||
| [1-9]{1,1}[0-9]* # decimal
|
||||
| 0[0-7]+ # octal
|
||||
| 0x[0-9a-f]+ # hex
|
||||
)
|
||||
$
|
||||
''', re.X|re.I
|
||||
)
|
||||
pattern1 = re.compile(r''' # indirect offset
|
||||
^\(
|
||||
(?P<base>&?0 # just zero
|
||||
|&?[1-9]{1,1}[0-9]* # decimal
|
||||
|&?0[0-7]* # octal
|
||||
|&?0x[0-9A-F]+ # hex
|
||||
)
|
||||
(?P<type>
|
||||
\. # this dot might be alone
|
||||
[BSL]? # one of this chars in either case
|
||||
)?
|
||||
(?P<sign>
|
||||
[-+]{0,1}
|
||||
)?
|
||||
(?P<off>0 # just zero
|
||||
|[1-9]{1,1}[0-9]* # decimal
|
||||
|0[0-7]* # octal
|
||||
|0x[0-9a-f]+ # hex
|
||||
)?
|
||||
\)$''', re.X|re.I
|
||||
)
|
||||
def __init__(self,s):
|
||||
self.source = s
|
||||
self.value = None
|
||||
self.relative = 0
|
||||
self.base = self.type = self.sign = self.offs = None
|
||||
m = Offset.pattern0.match( s )
|
||||
if m : # just a number
|
||||
if s[0] == '&' :
|
||||
self.relative, self.value = 1, int( s[1:], 0 )
|
||||
else:
|
||||
self.value = int( s, 0 )
|
||||
return
|
||||
m = Offset.pattern1.match( s )
|
||||
if m : # real indirect offset
|
||||
try:
|
||||
self.base = m.group('base')
|
||||
if self.base[0] == '&' :
|
||||
self.relative, self.base = 1, int( self.base[1:], 0 )
|
||||
else:
|
||||
self.base = int( self.base, 0 )
|
||||
if m.group('type') : self.type = m.group('type')[1:]
|
||||
self.sign = m.group('sign')
|
||||
if m.group('off') : self.offs = int( m.group('off'), 0 )
|
||||
if self.sign == '-' : self.offs = 0 - self.offs
|
||||
except:
|
||||
print >>sys.stderr, '$$', m.groupdict()
|
||||
raise
|
||||
return
|
||||
raise OffsetError(`s`)
|
||||
def __call__(self,file=None):
|
||||
if self.value is not None : return self.value
|
||||
pos = file.tell()
|
||||
try:
|
||||
if not self.relative : file.seek( self.offset, 0 )
|
||||
frmt = Offset.pos_format.get( self.type, 'I' )
|
||||
size = struct.calcsize( frmt )
|
||||
data = struct.unpack( frmt, file.read( size ) )
|
||||
if self.offs : data += self.offs
|
||||
return data
|
||||
finally:
|
||||
file.seek( pos, 0 )
|
||||
def __str__(self): return self.source
|
||||
def __repr__(self): return 'Offset(%s)' % `self.source`
|
||||
#end class Offset
|
||||
|
||||
class MagicFileError(MagicError): pass
|
||||
|
||||
class MagicFile:
|
||||
def __init__(self,filename=_magic):
|
||||
self.file = None
|
||||
self.tests = []
|
||||
self.total_tests = 0
|
||||
self.load( filename )
|
||||
self.ack_tests = None
|
||||
self.nak_tests = None
|
||||
def __del__(self):
|
||||
self.close()
|
||||
def load(self,filename=None):
|
||||
self.open( filename )
|
||||
self.parse()
|
||||
self.close()
|
||||
def open(self,filename=None):
|
||||
self.close()
|
||||
if filename is not None :
|
||||
self.filename = filename
|
||||
self.file = open( self.filename, 'r', BUFFER_SIZE )
|
||||
def close(self):
|
||||
if self.file :
|
||||
self.file.close()
|
||||
self.file = None
|
||||
def parse(self):
|
||||
line_no = 0
|
||||
for line in self.file.xreadlines() :
|
||||
line_no += 1
|
||||
if not line or line[0]=='#' : continue
|
||||
line = line.lstrip().rstrip('\r\n')
|
||||
if not line or line[0]=='#' : continue
|
||||
try:
|
||||
x = self.parse_line( line )
|
||||
if x is None :
|
||||
print >>sys.stderr, '#[%04d]#'%line_no, line
|
||||
continue
|
||||
except:
|
||||
print >>sys.stderr, '###[%04d]###'%line_no, line
|
||||
raise
|
||||
self.total_tests += 1
|
||||
level, offset, mtype, test, message = x
|
||||
new_test = MagicTest(offset,mtype,test,message,
|
||||
line=line_no,level=level)
|
||||
try:
|
||||
if level == 0 :
|
||||
self.tests.append( new_test )
|
||||
else:
|
||||
self.tests[-1].add( new_test )
|
||||
except:
|
||||
if 1 :
|
||||
print >>sys.stderr, 'total tests=%s' % (
|
||||
`self.total_tests`,
|
||||
)
|
||||
print >>sys.stderr, 'level=%s' % (
|
||||
`level`,
|
||||
)
|
||||
print >>sys.stderr, 'tests=%s' % (
|
||||
pprint.pformat(self.tests),
|
||||
)
|
||||
raise
|
||||
else:
|
||||
while self.tests[-1].level > 0 :
|
||||
self.tests.pop()
|
||||
def parse_line(self,line):
|
||||
# print >>sys.stderr, 'line=[%s]' % line
|
||||
if (not line) or line[0]=='#' : return None
|
||||
level = 0
|
||||
offset = mtype = test = message = ''
|
||||
mask = None
|
||||
# get optional level (count leading '>')
|
||||
while line and line[0]=='>' :
|
||||
line, level = line[1:], level+1
|
||||
# get offset
|
||||
while line and not line[0].isspace() :
|
||||
offset, line = offset+line[0], line[1:]
|
||||
try:
|
||||
offset = Offset(offset)
|
||||
except:
|
||||
print >>sys.stderr, 'line=[%s]' % line
|
||||
raise
|
||||
# skip spaces
|
||||
line = line.lstrip()
|
||||
# get type
|
||||
c = None
|
||||
while line :
|
||||
last_c, c, line = c, line[0], line[1:]
|
||||
if last_c!='\\' and c.isspace() :
|
||||
break # unescaped space - end of field
|
||||
else:
|
||||
mtype += c
|
||||
if last_c == '\\' :
|
||||
c = None # don't fuck my brain with sequential backslashes
|
||||
# skip spaces
|
||||
line = line.lstrip()
|
||||
# get test
|
||||
c = None
|
||||
while line :
|
||||
last_c, c, line = c, line[0], line[1:]
|
||||
if last_c!='\\' and c.isspace() :
|
||||
break # unescaped space - end of field
|
||||
else:
|
||||
test += c
|
||||
if last_c == '\\' :
|
||||
c = None # don't fuck my brain with sequential backslashes
|
||||
# skip spaces
|
||||
line = line.lstrip()
|
||||
# get message
|
||||
message = line
|
||||
if mime and line.find("\t") != -1:
|
||||
message=line[0:line.find("\t")]
|
||||
#
|
||||
# print '>>', level, offset, mtype, test, message
|
||||
return level, offset, mtype, test, message
|
||||
def detect(self,file):
|
||||
self.ack_tests = 0
|
||||
self.nak_tests = 0
|
||||
answers = []
|
||||
for test in self.tests :
|
||||
message = test.run( file )
|
||||
if message :
|
||||
self.ack_tests += 1
|
||||
answers.append( message )
|
||||
else:
|
||||
self.nak_tests += 1
|
||||
if answers :
|
||||
return '; '.join( answers )
|
||||
#end class MagicFile
|
||||
|
||||
def username(uid):
|
||||
try:
|
||||
return pwd.getpwuid( uid )[0]
|
||||
except:
|
||||
return '#%s'%uid
|
||||
|
||||
def groupname(gid):
|
||||
try:
|
||||
return grp.getgrgid( gid )[0]
|
||||
except:
|
||||
return '#%s'%gid
|
||||
|
||||
def get_file_type(fname,follow):
|
||||
t = None
|
||||
if not follow :
|
||||
try:
|
||||
st = os.lstat( fname ) # stat that entry, don't follow links!
|
||||
except os.error, why :
|
||||
pass
|
||||
else:
|
||||
if stat.S_ISLNK(st[stat.ST_MODE]) :
|
||||
t = 'symbolic link'
|
||||
try:
|
||||
lnk = os.readlink( fname )
|
||||
except:
|
||||
t += ' (unreadable)'
|
||||
else:
|
||||
t += ' to '+lnk
|
||||
if t is None :
|
||||
try:
|
||||
st = os.stat( fname )
|
||||
except os.error, why :
|
||||
return "can't stat `%s' (%s)." % (why.filename,why.strerror)
|
||||
|
||||
dmaj, dmin = (st.st_rdev>>8)&0x0FF, st.st_rdev&0x0FF
|
||||
|
||||
if 0 : pass
|
||||
elif stat.S_ISSOCK(st.st_mode) : t = 'socket'
|
||||
elif stat.S_ISLNK (st.st_mode) : t = follow and 'symbolic link' or t
|
||||
elif stat.S_ISREG (st.st_mode) : t = 'file'
|
||||
elif stat.S_ISBLK (st.st_mode) : t = 'block special (%d/%d)'%(dmaj,dmin)
|
||||
elif stat.S_ISDIR (st.st_mode) : t = 'directory'
|
||||
elif stat.S_ISCHR (st.st_mode) : t = 'character special (%d/%d)'%(dmaj,dmin)
|
||||
elif stat.S_ISFIFO(st.st_mode) : t = 'pipe'
|
||||
else: t = '<unknown>'
|
||||
|
||||
if st.st_mode & stat.S_ISUID :
|
||||
t = 'setuid(%d=%s) %s'%(st.st_uid,username(st.st_uid),t)
|
||||
if st.st_mode & stat.S_ISGID :
|
||||
t = 'setgid(%d=%s) %s'%(st.st_gid,groupname(st.st_gid),t)
|
||||
if st.st_mode & stat.S_ISVTX :
|
||||
t = 'sticky '+t
|
||||
|
||||
return t
|
||||
|
||||
HELP = '''%s [options] [files...]
|
||||
|
||||
Options:
|
||||
|
||||
-?, --help -- this help
|
||||
-m, --magic=<file> -- use this magic <file> instead of %s
|
||||
-f, --files=<namefile> -- read filenames for <namefile>
|
||||
* -C, --compile -- write "compiled" magic file
|
||||
-b, --brief -- don't prepend filenames to output lines
|
||||
+ -c, --check -- check the magic file
|
||||
-i, --mime -- output MIME types
|
||||
* -k, --keep-going -- don't stop st the first match
|
||||
-n, --flush -- flush stdout after each line
|
||||
-v, --verson -- print version and exit
|
||||
* -z, --compressed -- try to look inside compressed files
|
||||
-L, --follow -- follow symlinks
|
||||
-s, --special -- don't skip special files
|
||||
|
||||
* -- not implemented so far ;-)
|
||||
+ -- implemented, but in another way...
|
||||
'''
|
||||
|
||||
def main():
|
||||
import getopt
|
||||
global _magic
|
||||
try:
|
||||
brief = 0
|
||||
flush = 0
|
||||
follow= 0
|
||||
mime = 0
|
||||
check = 0
|
||||
special=0
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
'?m:f:CbciknvzLs',
|
||||
( 'help',
|
||||
'magic=',
|
||||
'names=',
|
||||
'compile',
|
||||
'brief',
|
||||
'check',
|
||||
'mime',
|
||||
'keep-going',
|
||||
'flush',
|
||||
'version',
|
||||
'compressed',
|
||||
'follow',
|
||||
'special',
|
||||
)
|
||||
)
|
||||
except getopt.error, why:
|
||||
print >>sys.stderr, sys.argv[0], why
|
||||
return 1
|
||||
else:
|
||||
files = None
|
||||
for o,v in opts :
|
||||
if o in ('-?','--help'):
|
||||
print HELP % (
|
||||
sys.argv[0],
|
||||
_magic,
|
||||
)
|
||||
return 0
|
||||
elif o in ('-f','--files='):
|
||||
files = v
|
||||
elif o in ('-m','--magic='):
|
||||
_magic = v[:]
|
||||
elif o in ('-C','--compile'):
|
||||
pass
|
||||
elif o in ('-b','--brief'):
|
||||
brief = 1
|
||||
elif o in ('-c','--check'):
|
||||
check = 1
|
||||
elif o in ('-i','--mime'):
|
||||
mime = 1
|
||||
if os.path.exists( _magic+'.mime' ) :
|
||||
_magic += '.mime'
|
||||
print >>sys.stderr,sys.argv[0]+':',\
|
||||
"Using regular magic file `%s'" % _magic
|
||||
elif o in ('-k','--keep-going'):
|
||||
pass
|
||||
elif o in ('-n','--flush'):
|
||||
flush = 1
|
||||
elif o in ('-v','--version'):
|
||||
print 'VERSION'
|
||||
return 0
|
||||
elif o in ('-z','--compressed'):
|
||||
pass
|
||||
elif o in ('-L','--follow'):
|
||||
follow = 1
|
||||
elif o in ('-s','--special'):
|
||||
special = 1
|
||||
else:
|
||||
if files :
|
||||
files = map(lambda x: x.strip(), v.split(','))
|
||||
if '-' in files and '-' in args :
|
||||
error( 1, 'cannot use STDIN simultaneously for file list and data' )
|
||||
for file in files :
|
||||
for name in (
|
||||
(file=='-')
|
||||
and sys.stdin
|
||||
or open(file,'r',BUFFER_SIZE)
|
||||
).xreadlines():
|
||||
name = name.strip()
|
||||
if name not in args :
|
||||
args.append( name )
|
||||
try:
|
||||
if check : print >>sys.stderr, 'Loading magic database...'
|
||||
t0 = time.time()
|
||||
m = MagicFile(_magic)
|
||||
t1 = time.time()
|
||||
if check :
|
||||
print >>sys.stderr, \
|
||||
m.total_tests, 'tests loaded', \
|
||||
'for', '%.2f' % (t1-t0), 'seconds'
|
||||
print >>sys.stderr, len(m.tests), 'tests at top level'
|
||||
return 0 # XXX "shortened" form ;-)
|
||||
|
||||
mlen = max( map(len, args) )+1
|
||||
for arg in args :
|
||||
if not brief : print (arg + ':').ljust(mlen),
|
||||
ftype = get_file_type( arg, follow )
|
||||
if (special and ftype.find('special')>=0) \
|
||||
or ftype[-4:] == 'file' :
|
||||
t0 = time.time()
|
||||
try:
|
||||
t = m.detect( arg )
|
||||
except (IOError,os.error), why:
|
||||
t = "can't read `%s' (%s)" % (why.filename,why.strerror)
|
||||
if ftype[-4:] == 'file' : t = ftype[:-4] + t
|
||||
t1 = time.time()
|
||||
print t and t or 'data'
|
||||
if 0 : print \
|
||||
'#\t%d tests ok, %d tests failed for %.2f seconds'%\
|
||||
(m.ack_tests, m.nak_tests, t1-t0)
|
||||
else:
|
||||
print mime and 'application/x-not-regular-file' or ftype
|
||||
if flush : sys.stdout.flush()
|
||||
# print >>sys.stderr, 'DONE'
|
||||
except:
|
||||
if check : return 1
|
||||
raise
|
||||
else:
|
||||
return 0
|
||||
finally:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__' :
|
||||
sys.exit( main() )
|
||||
# vim:ai
|
||||
# EOF #
|
||||
@@ -1,104 +0,0 @@
|
||||
#! /usr/bin/env python2.2
|
||||
|
||||
# Copyright 1994 by Lance Ellinghouse
|
||||
# Cathedral City, California Republic, United States of America.
|
||||
# All Rights Reserved
|
||||
# Permission to use, copy, modify, and distribute this software and its
|
||||
# documentation for any purpose and without fee is hereby granted,
|
||||
# provided that the above copyright notice appear in all copies and that
|
||||
# both that copyright notice and this permission notice appear in
|
||||
# supporting documentation, and that the name of Lance Ellinghouse
|
||||
# not be used in advertising or publicity pertaining to distribution
|
||||
# of the software without specific, written prior permission.
|
||||
# LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
# FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE CENTRUM BE LIABLE
|
||||
# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
# Modified by Jack Jansen, CWI, July 1995:
|
||||
# - Use binascii module to do the actual line-by-line conversion
|
||||
# between ascii and binary. This results in a 1000-fold speedup. The C
|
||||
# version is still 5 times faster, though.
|
||||
# - Arguments more compliant with python standard
|
||||
|
||||
"""Implementation of the UUencode and UUdecode functions.
|
||||
|
||||
encode(in_file, out_file [,name, mode])
|
||||
decode(in_file [, out_file, mode])
|
||||
"""
|
||||
|
||||
import binascii
|
||||
import os
|
||||
import sys
|
||||
from types import StringType
|
||||
|
||||
__all__ = ["Error", "decode"]
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
def decode(in_file, out_file=None, mode=None, quiet=0):
|
||||
"""Decode uuencoded file"""
|
||||
#
|
||||
# Open the input file, if needed.
|
||||
#
|
||||
if in_file == '-':
|
||||
in_file = sys.stdin
|
||||
elif isinstance(in_file, StringType):
|
||||
in_file = open(in_file)
|
||||
#
|
||||
# Read until a begin is encountered or we've exhausted the file
|
||||
#
|
||||
while 1:
|
||||
hdr = in_file.readline()
|
||||
if not hdr:
|
||||
raise Error, 'No valid begin line found in input file'
|
||||
if hdr[:5] != 'begin':
|
||||
continue
|
||||
hdrfields = hdr.split(" ", 2)
|
||||
if len(hdrfields) == 3 and hdrfields[0] == 'begin':
|
||||
try:
|
||||
int(hdrfields[1], 8)
|
||||
start_pos = in_file.tell() - len (hdr)
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
if out_file is None:
|
||||
out_file = hdrfields[2].rstrip()
|
||||
if os.path.exists(out_file):
|
||||
raise Error, 'Cannot overwrite existing file: %s' % out_file
|
||||
if mode is None:
|
||||
mode = int(hdrfields[1], 8)
|
||||
#
|
||||
# Open the output file
|
||||
#
|
||||
if out_file == '-':
|
||||
out_file = sys.stdout
|
||||
elif isinstance(out_file, StringType):
|
||||
fp = open(out_file, 'wb')
|
||||
try:
|
||||
os.path.chmod(out_file, mode)
|
||||
except AttributeError:
|
||||
pass
|
||||
out_file = fp
|
||||
#
|
||||
# Main decoding loop
|
||||
#
|
||||
s = in_file.readline()
|
||||
while s and s.strip() != 'end':
|
||||
try:
|
||||
data = binascii.a2b_uu(s)
|
||||
except binascii.Error, v:
|
||||
# Workaround for broken uuencoders by /Fredrik Lundh
|
||||
nbytes = (((ord(s[0])-32) & 63) * 4 + 5) / 3
|
||||
data = binascii.a2b_uu(s[:nbytes])
|
||||
if not quiet:
|
||||
sys.stderr.write("Warning: %s\n" % str(v))
|
||||
out_file.write(data)
|
||||
s = in_file.readline()
|
||||
# if not s:
|
||||
# raise Error, 'Truncated input file'
|
||||
return (hdrfields[2].rstrip(), start_pos, in_file.tell())
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/perl -w
|
||||
#!/usr/bonsaitools/bin/perl -w
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
@@ -23,61 +23,48 @@
|
||||
# mysqld-watcher.pl - a script that watches the running instance of
|
||||
# mysqld and kills off any long-running SELECTs against the shadow_db
|
||||
#
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
require "globals.pl";
|
||||
|
||||
# some configurables:
|
||||
|
||||
# length of time before a thread is eligible to be killed, in seconds
|
||||
#
|
||||
my $long_query_time = 180;
|
||||
my $long_query_time = 600;
|
||||
#
|
||||
# the From header for any messages sent out
|
||||
#
|
||||
my $mail_from = "root\@mothra.mozilla.org";
|
||||
#
|
||||
# the To header for any messages sent out
|
||||
#
|
||||
my $mail_to = "root";
|
||||
my $mail_from = "root\@lounge.mozilla.org";
|
||||
#
|
||||
# mail transfer agent. this should probably really be converted to a Param().
|
||||
#
|
||||
my $mta_program = "/usr/lib/sendmail -t -ODeliveryMode=deferred";
|
||||
|
||||
# The array of long-running queries
|
||||
# and STDIN is where we get the info about running threads
|
||||
#
|
||||
my $long = {};
|
||||
close(STDIN);
|
||||
open(STDIN, "/usr/bonsaitools/bin/mysqladmin processlist |");
|
||||
|
||||
# Run mysqladmin processlist twice, the first time getting complete queries
|
||||
# and the second time getting just abbreviated queries. We want complete
|
||||
# queries so we know which queries are taking too long to run, but complete
|
||||
# queries with line breaks get missed by this script, so we get abbreviated
|
||||
# queries as well to make sure we don't miss any.
|
||||
foreach my $command ("/opt/mysql/bin/mysqladmin --verbose processlist",
|
||||
"/opt/mysql/bin/mysqladmin processlist")
|
||||
{
|
||||
close(STDIN);
|
||||
open(STDIN, "$command |");
|
||||
# iterate through the running threads
|
||||
#
|
||||
my @LONGEST = (0,0,0,0,0,0,0,0,0);
|
||||
while ( <STDIN> ) {
|
||||
my @F = split(/\|/);
|
||||
|
||||
# iterate through the running threads
|
||||
# if this line is not the correct number of fields, or if the thread-id
|
||||
# field contains Id, skip this line. both these cases indicate that this
|
||||
# line contains pretty-printing gunk and not thread info.
|
||||
#
|
||||
while ( <STDIN> ) {
|
||||
my @F = split(/\|/);
|
||||
next if ( $#F != 9 || $F[1] =~ /Id/);
|
||||
|
||||
# if this line is not the correct number of fields, or if the thread-id
|
||||
# field contains Id, skip this line. both these cases indicate that this
|
||||
# line contains pretty-printing gunk and not thread info.
|
||||
#
|
||||
next if ( $#F != 9 || $F[1] =~ /Id/);
|
||||
|
||||
if ( $F[4] =~ /shadow_bugs/ # shadowbugs database in use
|
||||
&& $F[5] =~ /Query/ # this is actually a query
|
||||
&& $F[6] > $long_query_time # this query has taken too long
|
||||
&& $F[8] =~ /(select|SELECT)/ # only kill a select
|
||||
&& !defined($long->{$F[1]}) ) # haven't seen this one already
|
||||
{
|
||||
$long->{$F[1]} = \@F;
|
||||
system("/opt/mysql/bin/mysqladmin", "kill", $F[1]);
|
||||
}
|
||||
if ( $F[4] =~ /shadow_bugs/ # shadowbugs database in use
|
||||
&& $F[5] =~ /Query/ # this is actually a query
|
||||
&& $F[6] > $long_query_time # this query has taken too long
|
||||
&& $F[8] =~ /(select|SELECT)/ # only kill a select
|
||||
&& $F[6] > $LONGEST[6] ) { # the longest running query seen
|
||||
@LONGEST = @F;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,15 +90,13 @@ sub sendEmail($$$$) {
|
||||
|
||||
# if we found anything, kill the database thread and send mail about it
|
||||
#
|
||||
if (scalar(keys(%$long))) {
|
||||
my $message = "";
|
||||
foreach my $process_id (keys(%$long)) {
|
||||
my $qry = $long->{$process_id};
|
||||
$message .= join(" ", @$qry) . "\n\n";
|
||||
}
|
||||
if ($LONGEST[6] != 0) {
|
||||
|
||||
# fire off an email telling the maintainer that we had to kill some threads
|
||||
system ("/usr/bonsaitools/bin/mysqladmin", "kill", $LONGEST[1]);
|
||||
|
||||
# fire off an email telling the maintainer that we had to kill a thread
|
||||
#
|
||||
sendEmail($mail_from, $mail_to, "long running MySQL thread(s) killed", $message);
|
||||
sendEmail($mail_from, Param("maintainer"),
|
||||
"long running MySQL thread killed",
|
||||
join(" ", @LONGEST) . "\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# sendbugmail.pl
|
||||
#
|
||||
# Nick Barnes, Ravenbrook Limited, 2004-04-01.
|
||||
#
|
||||
# $Id: sendbugmail.pl,v 1.1 2004-07-05 21:54:01 justdave%bugzilla.org Exp $
|
||||
#
|
||||
# Bugzilla email script for Bugzilla 2.17.4 and later. Invoke this to send
|
||||
# bugmail for a bug which has been changed directly in the database.
|
||||
# This uses Bugzilla's own BugMail facility, and will email the
|
||||
# users associated with the bug. Replaces the old "processmail"
|
||||
# script.
|
||||
#
|
||||
# Usage: bugmail.pl bug_id user_email
|
||||
|
||||
use lib qw(..);
|
||||
|
||||
require "globals.pl";
|
||||
use Bugzilla::BugMail;
|
||||
|
||||
sub usage {
|
||||
print STDERR "Usage: $0 bug_id user_email\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
if (($#ARGV < 1) || ($#ARGV > 2)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
# Get the arguments.
|
||||
my $bugnum = $ARGV[0];
|
||||
my $changer = $ARGV[1];
|
||||
|
||||
# Validate the bug number.
|
||||
if (!($bugnum =~ /^(\d+)$/)) {
|
||||
print STDERR "Bug number \"$bugnum\" not numeric.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
SendSQL("SELECT bug_id FROM bugs WHERE bug_id = $bugnum");
|
||||
|
||||
if (!FetchOneColumn()) {
|
||||
print STDERR "Bug number $bugnum does not exist.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
# Validate the changer address.
|
||||
my $match = Param('emailregexp');
|
||||
if ($changer !~ /$match/) {
|
||||
print STDERR "Changer \"$changer\" doesn't match email regular expression.\n";
|
||||
usage();
|
||||
}
|
||||
if(!DBname_to_id($changer)) {
|
||||
print STDERR "\"$changer\" is not a login ID.\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
# Send the email.
|
||||
my $outputref = Bugzilla::BugMail::Send($bugnum, {'changer' => $changer });
|
||||
|
||||
# Report the results.
|
||||
my $sent = scalar(@{$outputref->{sent}});
|
||||
my $excluded = scalar(@{$outputref->{excluded}});
|
||||
|
||||
if ($sent) {
|
||||
print "email sent to $sent recipients:\n";
|
||||
} else {
|
||||
print "No email sent.\n";
|
||||
}
|
||||
|
||||
foreach my $sent (@{$outputref->{sent}}) {
|
||||
print " $sent\n";
|
||||
}
|
||||
|
||||
if ($excluded) {
|
||||
print "$excluded recipients excluded:\n";
|
||||
} else {
|
||||
print "No recipients excluded.\n";
|
||||
}
|
||||
|
||||
foreach my $excluded (@{$outputref->{excluded}}) {
|
||||
print " $excluded\n";
|
||||
}
|
||||
|
||||
# This document is copyright (C) 2004 Perforce Software, Inc. All rights
|
||||
# reserved.
|
||||
#
|
||||
# Redistribution and use of this document in any form, with or without
|
||||
# modification, is permitted provided that redistributions of this
|
||||
# document retain the above copyright notice, this condition and the
|
||||
# following disclaimer.
|
||||
#
|
||||
# THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -1,285 +0,0 @@
|
||||
#!/usr/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the LDAP to Bugzilla User Sync Tool.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Andreas Höfler.
|
||||
# Portions created by Andreas Höfler are Copyright (C) 2003
|
||||
# Andreas Höfler. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Andreas Höfler <andreas.hoefler@bearingpoint.com>
|
||||
#
|
||||
|
||||
use strict;
|
||||
|
||||
require "CGI.pl";
|
||||
|
||||
use lib qw(.);
|
||||
|
||||
use Net::LDAP;
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
my $readonly = 0;
|
||||
my $nodisable = 0;
|
||||
my $noupdate = 0;
|
||||
my $nocreate = 0;
|
||||
my $quiet = 0;
|
||||
|
||||
###
|
||||
# Do some preparations
|
||||
###
|
||||
foreach my $arg (@ARGV)
|
||||
{
|
||||
if($arg eq '-r') {
|
||||
$readonly = 1;
|
||||
}
|
||||
elsif($arg eq '-d') {
|
||||
$nodisable = 1;
|
||||
}
|
||||
elsif($arg eq '-u') {
|
||||
$noupdate = 1;
|
||||
}
|
||||
elsif($arg eq '-c') {
|
||||
$nocreate = 1;
|
||||
}
|
||||
elsif($arg eq '-q') {
|
||||
$quiet = 1;
|
||||
}
|
||||
else {
|
||||
print "LDAP Sync Script\n";
|
||||
print "Syncronizes the users table from the LDAP server with the Bugzilla users.\n";
|
||||
print "Takes mail-attribute from preferences and description from 'cn' or,\n";
|
||||
print "if not available, from the uid-attribute.\n\n";
|
||||
print "usage:\n syncLDAP.pl [options]\n\n";
|
||||
print "options:\n";
|
||||
print " -r Readonly, do not make changes to Bugzilla tables\n";
|
||||
print " -d No disable, don't disable users, which are not in LDAP\n";
|
||||
print " -u No update, don't update users, which have different description in LDAP\n";
|
||||
print " -c No create, don't create users, which are in LDAP but not in Bugzilla\n";
|
||||
print " -q Quiet mode, give less output\n";
|
||||
print "\n";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
my %bugzilla_users;
|
||||
my %ldap_users;
|
||||
|
||||
###
|
||||
# Get current bugzilla users
|
||||
###
|
||||
SendSQL("SELECT login_name, realname, disabledtext " .
|
||||
"FROM profiles" );
|
||||
while (MoreSQLData()) {
|
||||
my ($login_name, $realname, $disabledtext)
|
||||
= FetchSQLData();
|
||||
|
||||
# remove whitespaces
|
||||
$realname =~ s/^\s+|\s+$//g;
|
||||
|
||||
$bugzilla_users{$login_name} = { realname => $realname,
|
||||
new_login_name => $login_name,
|
||||
disabledtext => $disabledtext };
|
||||
}
|
||||
|
||||
###
|
||||
# Get current LDAP users
|
||||
###
|
||||
my $LDAPserver = Param("LDAPserver");
|
||||
if ($LDAPserver eq "") {
|
||||
print "No LDAP server defined in bugzilla preferences.\n";
|
||||
exit;
|
||||
}
|
||||
my $LDAPport = "389"; # default LDAP port
|
||||
if($LDAPserver =~ /:/) {
|
||||
($LDAPserver, $LDAPport) = split(":",$LDAPserver);
|
||||
}
|
||||
|
||||
my $LDAPconn = Net::LDAP->new($LDAPserver, port => $LDAPport, version => 3);
|
||||
if(!$LDAPconn) {
|
||||
print "Connecting to LDAP server failed. Check LDAPserver setting.\n";
|
||||
exit;
|
||||
}
|
||||
my $mesg;
|
||||
if (Param("LDAPbinddn")) {
|
||||
my ($LDAPbinddn,$LDAPbindpass) = split(":",Param("LDAPbinddn"));
|
||||
$mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass);
|
||||
}
|
||||
else {
|
||||
$mesg = $LDAPconn->bind();
|
||||
}
|
||||
if($mesg->code) {
|
||||
print "Binding to LDAP server failed: " . $mesg->error . "\nCheck LDAPbinddn setting.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
# We've got our anonymous bind; let's look up the users.
|
||||
$mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
|
||||
scope => "sub",
|
||||
filter => '(&(' . Param("LDAPuidattribute") . "=*)" . Param("LDAPfilter") . ')',
|
||||
);
|
||||
|
||||
|
||||
if(! $mesg->count) {
|
||||
print "LDAP lookup failure. Check LDAPBaseDN setting.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
my $val = $mesg->as_struct;
|
||||
|
||||
while( my ($key, $value) = each(%$val) ) {
|
||||
|
||||
my $login_name = @$value{Param("LDAPmailattribute")};
|
||||
my $realname = @$value{"cn"};
|
||||
|
||||
# no mail entered? go to next
|
||||
if(! defined $login_name) {
|
||||
print "$key has no valid mail address\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# no cn entered? use uid instead
|
||||
if(! defined $realname) {
|
||||
$realname = @$value{Param("LDAPuidattribute")};
|
||||
}
|
||||
|
||||
my $login = shift @$login_name;
|
||||
my $real = shift @$realname;
|
||||
$ldap_users{$login} = { realname => $real };
|
||||
}
|
||||
|
||||
print "\n" unless $quiet;
|
||||
|
||||
###
|
||||
# Sort the users into disable/update/create-Lists and display everything
|
||||
###
|
||||
my %disable_users;
|
||||
my %update_users;
|
||||
my %create_users;
|
||||
|
||||
print "Bugzilla-Users: \n" unless $quiet;
|
||||
while( my ($key, $value) = each(%bugzilla_users) ) {
|
||||
print " " . $key . " '" . @$value{'realname'} . "' " . @$value{'disabledtext'} ."\n" unless $quiet==1;
|
||||
if(!exists $ldap_users{$key}){
|
||||
if(@$value{'disabledtext'} eq '') {
|
||||
$disable_users{$key} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "\nLDAP-Users: \n" unless $quiet;
|
||||
while( my ($key, $value) = each(%ldap_users) ) {
|
||||
print " " . $key . " '" . @$value{'realname'} . "'\n" unless $quiet==1;
|
||||
if(!exists $bugzilla_users{$key}){
|
||||
$create_users{$key} = $value;
|
||||
}
|
||||
else {
|
||||
my $bugzilla_user_value = $bugzilla_users{$key};
|
||||
if(@$bugzilla_user_value{'realname'} ne @$value{'realname'}) {
|
||||
$update_users{$key} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "\nDetecting email changes: \n" unless $quiet;
|
||||
while( my ($create_key, $create_value) = each(%create_users) ) {
|
||||
while( my ($disable_key, $disable_value) = each(%disable_users) ) {
|
||||
if(@$create_value{'realname'} eq @$disable_value{'realname'}) {
|
||||
print " " . $disable_key . " => " . $create_key ."'\n" unless $quiet==1;
|
||||
$update_users{$disable_key} = { realname => @$create_value{'realname'},
|
||||
new_login_name => $create_key };
|
||||
delete $create_users{$create_key};
|
||||
delete $disable_users{$disable_key};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($quiet == 0) {
|
||||
print "\nUsers to disable: \n";
|
||||
while( my ($key, $value) = each(%disable_users) ) {
|
||||
print " " . $key . " '" . @$value{'realname'} . "'\n";
|
||||
}
|
||||
|
||||
print "\nUsers to update: \n";
|
||||
while( my ($key, $value) = each(%update_users) ) {
|
||||
print " " . $key . " '" . @$value{'realname'} . "' ";
|
||||
if(defined @$value{'new_login_name'}) {
|
||||
print "has changed email to " . @$value{'new_login_name'};
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
print "\nUsers to create: \n";
|
||||
while( my ($key, $value) = each(%create_users) ) {
|
||||
print " " . $key . " '" . @$value{'realname'} . "'\n";
|
||||
}
|
||||
|
||||
print "\n\n";
|
||||
}
|
||||
|
||||
|
||||
###
|
||||
# now do the DB-Update
|
||||
###
|
||||
if($readonly == 0) {
|
||||
print "Performing DB update:\nPhase 1: disabling not-existing users... " unless $quiet;
|
||||
if($nodisable == 0) {
|
||||
while( my ($key, $value) = each(%disable_users) ) {
|
||||
SendSQL("UPDATE profiles SET disabledtext = 'auto-disabled by ldap sync' WHERE login_name='$key'" );
|
||||
}
|
||||
print "done!\n" unless $quiet;
|
||||
}
|
||||
else {
|
||||
print "disabled!\n" unless $quiet;
|
||||
}
|
||||
|
||||
print "Phase 2: updating existing users... " unless $quiet;
|
||||
if($noupdate == 0) {
|
||||
while( my ($key, $value) = each(%update_users) ) {
|
||||
if(defined @$value{'new_login_name'}) {
|
||||
SendSQL("UPDATE profiles SET login_name = '" . @$value{'new_login_name'} . "' WHERE login_name='$key'" );
|
||||
} else {
|
||||
SendSQL("UPDATE profiles SET realname = '" . @$value{'realname'} . "' WHERE login_name='$key'" );
|
||||
}
|
||||
}
|
||||
print "done!\n" unless $quiet;
|
||||
}
|
||||
else {
|
||||
print "disabled!\n" unless $quiet;
|
||||
}
|
||||
|
||||
print "Phase 3: creating new users... " unless $quiet;
|
||||
if($nocreate == 0) {
|
||||
while( my ($key, $value) = each(%create_users) ) {
|
||||
SendSQL("INSERT INTO profiles VALUES ('',
|
||||
'$key',
|
||||
'xxKFIy4WR66mA',
|
||||
'" . @$value{'realname'} . "',
|
||||
'',
|
||||
1,
|
||||
'ExcludeSelf~on~emailOwnerRemoveme~on~emailOwnerComments~on~emailOwnerAttachments~on~emailOwnerStatus~on~emailOwnerResolved~on~emailOwnerKeywords~on~emailOwnerCC~on~emailOwnerOther~on~emailOwnerUnconfirmed~on~emailReporterRemoveme~on~emailReporterComments~on~emailReporterAttachments~on~emailReporterStatus~on~emailReporterResolved~on~emailReporterKeywords~on~emailReporterCC~on~emailReporterOther~on~emailReporterUnconfirmed~on~emailQAcontactRemoveme~on~emailQAcontactComments~on~emailQAcontactAttachments~on~emailQAcontactStatus~on~emailQAcontactResolved~on~emailQAcontactKeywords~on~emailQAcontactCC~on~emailQAcontactOther~on~emailQAcontactUnconfirmed~on~emailCClistRemoveme~on~emailCClistComments~on~emailCClistAttachments~on~emailCClistStatus~on~emailCClistResolved~on~emailCClistKeywords~on~emailCClistCC~on~emailCClistOther~on~emailCClistUnconfirmed~on~emailVoterRemoveme~on~emailVoterComments~on~emailVoterAttachments~on~emailVoterStatus~on~emailVoterResolved~on~emailVoterKeywords~on~emailVoterCC~on~emailVoterOther~on~emailVoterUnconfirmed~on',
|
||||
sysdate())");
|
||||
}
|
||||
print "done!\n" unless $quiet;
|
||||
}
|
||||
else {
|
||||
print "disabled!\n" unless $quiet;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print "No changes to DB because readonly mode\n" unless $quiet;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/perl -wT
|
||||
#!/usr/bonsaitools/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
@@ -24,39 +24,47 @@
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
use lib qw(.);
|
||||
|
||||
require "CGI.pl";
|
||||
require "globals.pl";
|
||||
|
||||
# Shut up misguided -w warnings about "used only once":
|
||||
use vars qw(
|
||||
$template
|
||||
$vars
|
||||
%FORM
|
||||
$template
|
||||
$vars
|
||||
);
|
||||
|
||||
ConnectToDatabase();
|
||||
|
||||
# If we're using LDAP for login, then we can't create a new account here.
|
||||
unless (Bugzilla::Auth->can_edit('new')) {
|
||||
# Just in case someone already has an account, let them get the correct
|
||||
# footer on the error message
|
||||
Bugzilla->login();
|
||||
ThrowUserError("auth_cant_create_account");
|
||||
if(Param('useLDAP')) {
|
||||
DisplayError("This site is using LDAP for authentication. Please contact
|
||||
an LDAP administrator to get a new account created.",
|
||||
"Can't create LDAP accounts");
|
||||
PutFooter();
|
||||
exit;
|
||||
}
|
||||
|
||||
# Clear out the login cookies. Make people log in again if they create an
|
||||
# account; otherwise, they'll probably get confused.
|
||||
Bugzilla->logout();
|
||||
my $cookiepath = Param("cookiepath");
|
||||
print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT
|
||||
Set-Cookie: Bugzilla_logincookie= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n";
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
print $cgi->header();
|
||||
print "Content-Type: text/html\n\n";
|
||||
|
||||
my $login = $cgi->param('login');
|
||||
my $login = $::FORM{'login'};
|
||||
my $realname = trim($::FORM{'realname'});
|
||||
|
||||
if (defined($login)) {
|
||||
# We've been asked to create an account.
|
||||
my $realname = trim($cgi->param('realname'));
|
||||
CheckEmailSyntax($login);
|
||||
trick_taint($login);
|
||||
$vars->{'login'} = $login;
|
||||
|
||||
if (!ValidateNewUser($login)) {
|
||||
@@ -65,13 +73,6 @@ if (defined($login)) {
|
||||
|| ThrowTemplateError($template->error());
|
||||
exit;
|
||||
}
|
||||
|
||||
my $createexp = Param('createemailregexp');
|
||||
if (!($createexp)
|
||||
|| ($login !~ /$createexp/)) {
|
||||
ThrowUserError("account_creation_disabled");
|
||||
exit;
|
||||
}
|
||||
|
||||
# Create account
|
||||
my $password = InsertNewUser($login, $realname);
|
||||
|
||||
@@ -24,29 +24,15 @@
|
||||
/* Style bug rows according to severity. */
|
||||
.bz_blocker { color: red; font-weight: bold; }
|
||||
.bz_critical { color: red; }
|
||||
.bz_enhancement { color: #888; background-color: white; }
|
||||
.bz_enhancement { font-style: italic; }
|
||||
|
||||
/* Style secure bugs if the installation is not using bug groups.
|
||||
* Installations that *are* using bug groups are likely to be using
|
||||
* them for almost all bugs, in which case special styling is not
|
||||
* informative and generally a nuisance.
|
||||
*/
|
||||
.bz_secure { color: black; background-color: lightgrey; }
|
||||
|
||||
/* Align columns in the "change multiple bugs" form to the right. */
|
||||
table#form tr th { text-align: right; }
|
||||
|
||||
table.bz_buglist td, table.bz_buglist th {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
/* For styling rows; by default striped white and gray */
|
||||
tr.bz_odd {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
tr.bz_even {
|
||||
}
|
||||
|
||||
/* we use a first-child class and not the pseudo-class because IE
|
||||
* doesn't support it :-( */
|
||||
tr.bz_secure td.first-child {
|
||||
background-image: url("../images/padlock.png");
|
||||
background-position: center left;
|
||||
background-repeat: no-repeat;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,89 +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): Christian Reis <kiko@async.com.br>
|
||||
*/
|
||||
|
||||
ul.tree {
|
||||
padding-left: 0em;
|
||||
margin-left: 1em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul.tree ul {
|
||||
padding-top: 3px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul.tree li {
|
||||
/* see http://www.kryogenix.org/code/browser/aqlists/ for idea */
|
||||
padding-top: 3px;
|
||||
text-indent: -1.2em;
|
||||
padding-left: 0.5em;
|
||||
list-style-type: none;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
ul.tree li a.b {
|
||||
padding-left: 12px;
|
||||
margin-right: 2px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul.tree li a.b_open {
|
||||
background: url("../images/tree-open.png") center no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul.tree li a.b_closed {
|
||||
background: url("../images/tree-closed.png") center no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.summ_info {
|
||||
/* change to inline if you would like to see the full bug details
|
||||
* displayed in the list */
|
||||
display: none;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 90%;
|
||||
margin: 0.2em;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.hint h3, .hint ul {
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
/* uncomment this if you would like summary text of closed classes to be
|
||||
* striked through */
|
||||
/*
|
||||
.bz_closed + .summ_text {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
*/
|
||||
|
||||
/* for styling summaries that are leaves or not */
|
||||
.summ, .summ_deep {
|
||||
}
|
||||
|
||||
.summ_deep {
|
||||
}
|
||||
@@ -1,34 +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>
|
||||
*/
|
||||
|
||||
tree#results-tree {
|
||||
margin-right: 0px;
|
||||
border-right-width: 0px;
|
||||
margin-left: 0px;
|
||||
border-left-width: 0px;
|
||||
}
|
||||
|
||||
treechildren:-moz-tree-cell-text(resolution-FIXED) {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
treecol#id_column { width: 6em; }
|
||||
treecol#duplicate_count_column { width: 5em; }
|
||||
treecol#duplicate_delta_column { width: 5em; }
|
||||
@@ -1,175 +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): Byron Jones <bugzilla@glob.com.au>
|
||||
* Christian Reis <kiko@async.com.br>
|
||||
* Vitaly Harisov <vitaly@rathedg.com>
|
||||
* Svetlana Harisova <light@rathedg.com>
|
||||
*/
|
||||
|
||||
/* banner (begin) */
|
||||
#banner
|
||||
{
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
background: #000;
|
||||
|
||||
/* for Netscape 4 only */
|
||||
border: 1px solid #fff;
|
||||
border-bottom-width: 0.65em;
|
||||
}
|
||||
|
||||
/* hide from NN4 */
|
||||
div#banner
|
||||
{
|
||||
border-bottom-width: 0.2em;
|
||||
}
|
||||
|
||||
#banner p
|
||||
{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#banner-name
|
||||
{
|
||||
font-family: serif;
|
||||
font-size: 255%;
|
||||
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* hide from NN4 */
|
||||
p#banner-name
|
||||
{
|
||||
font-size: 300%;
|
||||
}
|
||||
|
||||
#banner-version
|
||||
{
|
||||
font-size: 75%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* hide from NN4 */
|
||||
p#banner-version
|
||||
{
|
||||
font-size: 85%;
|
||||
}
|
||||
/* banner (end) */
|
||||
|
||||
/* footer (begin) */
|
||||
#footer
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
|
||||
#footer form
|
||||
{
|
||||
background: #FFFFE0;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
#footer span
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#footer .btn, #footer .txt
|
||||
{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#footer #useful-links
|
||||
{
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
/* hide from MSIE and NN4 */
|
||||
[id]#footer #useful-links
|
||||
{
|
||||
margin: 0.3em;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#footer #links-actions,
|
||||
#footer #links-edit,
|
||||
#footer #links-saved
|
||||
{
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
#footer .label
|
||||
{
|
||||
width: 7.2em;
|
||||
display: block;
|
||||
float: left;
|
||||
|
||||
vertical-align: baseline;
|
||||
|
||||
padding: 0.1em 0.2em;
|
||||
}
|
||||
|
||||
#footer #links-actions .label
|
||||
{
|
||||
padding-top: 0.45em;
|
||||
}
|
||||
|
||||
/* hide from MSIE and NN4 */
|
||||
[id]#footer .label
|
||||
{
|
||||
display: table-cell;
|
||||
float: none;
|
||||
width: auto;
|
||||
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#footer .links
|
||||
{
|
||||
display: block;
|
||||
|
||||
padding: 0.1em 0.2em;
|
||||
}
|
||||
|
||||
/* hide from MSIE and NN4 */
|
||||
[id]#footer .links
|
||||
{
|
||||
display: table-cell;
|
||||
|
||||
padding-top: 0;
|
||||
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* hide from MSIE and NN4 */
|
||||
#footer+*
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
/* footer (end) */
|
||||
|
||||
.bz_obsolete { text-decoration: line-through; }
|
||||
.bz_inactive { text-decoration: line-through; }
|
||||
.bz_closed { text-decoration: line-through; }
|
||||
.bz_private { color: darkred ; background : #f3eeee ; }
|
||||
.bz_disabled { color: #a0a0a0 ; }
|
||||
|
||||
.bz_comment { background-color: #e0e0e0; }
|
||||
|
||||
table#flags th, table#flags td { vertical-align: baseline; text-align: left; }
|
||||
@@ -1 +0,0 @@
|
||||
.bz_private { color:darkred }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/perl -wT
|
||||
#!/usr/bonsaitools/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
@@ -22,84 +22,79 @@
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
|
||||
use vars qw(
|
||||
%legal_product
|
||||
%FORM
|
||||
%proddesc
|
||||
$userid
|
||||
);
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
|
||||
use lib qw(.);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
|
||||
require "CGI.pl";
|
||||
|
||||
Bugzilla->login();
|
||||
|
||||
ConnectToDatabase();
|
||||
GetVersionTable();
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $product = $cgi->param('product');
|
||||
my $userid = quietly_check_login();
|
||||
|
||||
if (!defined $product) {
|
||||
if (!defined $::FORM{'product'}) {
|
||||
# Reference to a subset of %::proddesc, which the user is allowed to see
|
||||
my %products;
|
||||
|
||||
if (AnyEntryGroups()) {
|
||||
# OK, now only add products the user can see
|
||||
Bugzilla->login(LOGIN_REQUIRED);
|
||||
foreach my $p (@::legal_product) {
|
||||
if (CanEnterProduct($p)) {
|
||||
$products{$p} = $::proddesc{$p};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
%products = %::proddesc;
|
||||
foreach my $p (@::legal_product) {
|
||||
next if !CanSeeProduct($userid, $p);
|
||||
$products{$p} = $::proddesc{$p};
|
||||
}
|
||||
|
||||
my $prodsize = scalar(keys %products);
|
||||
if ($prodsize == 0) {
|
||||
ThrowUserError("no_products");
|
||||
DisplayError("Either no products have been defined ".
|
||||
"or you have not been given access to any.\n");
|
||||
exit;
|
||||
}
|
||||
elsif ($prodsize > 1) {
|
||||
$::vars->{'proddesc'} = \%products;
|
||||
$::vars->{'target'} = "describecomponents.cgi";
|
||||
$::vars->{'title'} = "Bugzilla component description";
|
||||
$::vars->{'h2'} =
|
||||
"Please specify the product whose components you want described.";
|
||||
|
||||
print $cgi->header();
|
||||
print "Content-type: text/html\n\n";
|
||||
$::template->process("global/choose-product.html.tmpl", $::vars)
|
||||
|| ThrowTemplateError($::template->error());
|
||||
exit;
|
||||
}
|
||||
|
||||
$product = (keys %products)[0];
|
||||
$::FORM{'product'} = (keys %products)[0];
|
||||
}
|
||||
|
||||
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.
|
||||
my $product_id = get_product_id($product);
|
||||
|
||||
if (!$product_id) {
|
||||
ThrowUserError("invalid_product_name",
|
||||
{ product => $product });
|
||||
}
|
||||
grep($product eq $_ , @::legal_product)
|
||||
|| DisplayError("The product name is invalid.")
|
||||
&& exit;
|
||||
|
||||
# Make sure the user is authorized to access this product.
|
||||
CanEnterProduct($product)
|
||||
|| ThrowUserError("product_access_denied");
|
||||
!CanSeeProduct($userid, $product)
|
||||
&& DisplayError("You are not authorized to access that product.")
|
||||
&& exit;
|
||||
|
||||
######################################################################
|
||||
# End Data/Security Validation
|
||||
######################################################################
|
||||
|
||||
my @components;
|
||||
SendSQL("SELECT name, initialowner, initialqacontact, description FROM " .
|
||||
"components WHERE product_id = $product_id ORDER BY " .
|
||||
"name");
|
||||
SendSQL("SELECT value, initialowner, initialqacontact, description FROM " .
|
||||
"components WHERE program = " . SqlQuote($product) . " ORDER BY " .
|
||||
"value");
|
||||
while (MoreSQLData()) {
|
||||
my ($name, $initialowner, $initialqacontact, $description) =
|
||||
FetchSQLData();
|
||||
@@ -119,7 +114,7 @@ while (MoreSQLData()) {
|
||||
$::vars->{'product'} = $product;
|
||||
$::vars->{'components'} = \@components;
|
||||
|
||||
print $cgi->header();
|
||||
print "Content-type: text/html\n\n";
|
||||
$::template->process("reports/components.html.tmpl", $::vars)
|
||||
|| ThrowTemplateError($::template->error());
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/perl -wT
|
||||
#!/usr/bonsaitools/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
@@ -21,24 +21,23 @@
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Contributor(s): Gervase Markham <gerv@gerv.net>
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
use lib ".";
|
||||
|
||||
use Bugzilla;
|
||||
|
||||
require "CGI.pl";
|
||||
|
||||
# Use the global template variables.
|
||||
use vars qw($vars $template);
|
||||
|
||||
Bugzilla->login();
|
||||
ConnectToDatabase();
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $userid = 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;
|
||||
@@ -52,8 +51,8 @@ while (MoreSQLData()) {
|
||||
}
|
||||
|
||||
$vars->{'keywords'} = \@keywords;
|
||||
$vars->{'caneditkeywords'} = UserInGroup("editkeywords");
|
||||
$vars->{'caneditkeywords'} = UserInGroup($userid, "editkeywords");
|
||||
|
||||
print Bugzilla->cgi->header();
|
||||
print "Content-type: text/html\n\n";
|
||||
$template->process("reports/keywords.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
@@ -2,33 +2,35 @@ Welcome to the Bugzilla documentation project!
|
||||
You'll find these directories and files here:
|
||||
|
||||
README.docs # This README file
|
||||
html/ # The compiled HTML docs from XML sources (do not edit)
|
||||
txt/ # The compiled text docs from XML sources (do not edit)
|
||||
xml/ # The original XML doc sources (edit these)
|
||||
html/ # The compiled HTML docs from SGML sources (do not edit)
|
||||
sgml/ # The original SGML doc sources (edit these)
|
||||
txt/ # The compiled text docs from SGML sources
|
||||
ps/ # The compiled PostScript docs from SGML sources
|
||||
pdf/ # The compiled Adobe PDF docs from SGML sources
|
||||
|
||||
A note about the XML:
|
||||
The documentation is written in DocBook 4.1.2, and attempts to adhere
|
||||
to the LinuxDoc standards where applicable (http://www.tldp.org).
|
||||
Please consult "The LDP Author Guide" at tldp.org for details on how
|
||||
to set up your personal environment for compiling XML files.
|
||||
A note about SGML:
|
||||
The documentation is written in DocBook 3.1/4.1 SGML, and attempts to adhere
|
||||
to the LinuxDoc standards everywhere applicable (http://www.linuxdoc.org).
|
||||
Please consult "The LDP Author Guide" at linuxdoc.org for details on how
|
||||
to set up your personal environment for compiling SGML files.
|
||||
If you need to make corrections to typographical errors, or other minor
|
||||
editing duties, feel free to use any text editor to make the changes. XML
|
||||
editing duties, feel free to use any text editor to make the changes. SGML
|
||||
is not rocket science -- simply make sure your text appears between
|
||||
appropriate tags (like <para>This is a paragraph</para>) and we'll be fine.
|
||||
If you are making more extensive changes, please ensure you at least validate
|
||||
your XML before checking it in with something like:
|
||||
nsgmls -s $JADE_PUB/xml.dcl Bugzilla-Guide.xml
|
||||
your SGML before checking it in with something like:
|
||||
nsgmls -s Bugzilla-Guide.sgml
|
||||
|
||||
When you validate, please validate the master document (Bugzilla-Guide.xml)
|
||||
When you validate, please validate the master document (Bugzilla-Guide.sgml)
|
||||
as well as the document you edited to ensure there are no critical errors.
|
||||
The following errors are considered "normal" when validating with nsgmls:
|
||||
|
||||
DTDDECL catalog entries are not supported
|
||||
"DOCTYPE" declaration not allowed in instance
|
||||
|
||||
The reason these occur is that free sgml/xml validators do not yet support
|
||||
The reason these occur is that free sgml validators do not yet support
|
||||
the DTDDECL catalog entries, and I've included DOCTYPE declarations in
|
||||
entities referenced from Bugzilla-Guide.xml so these entities can compile
|
||||
entities referenced from Bugzilla-Guide.sgml so these entities can compile
|
||||
individually, if necessary. I suppose I ought to comment them out at some
|
||||
point, but for now they are convenient and don't hurt anything.
|
||||
|
||||
@@ -37,13 +39,13 @@ documentation. Please address comments and questions to the newsgroup:
|
||||
news://news.mozilla.org/netscape/public/mozilla/webtools .
|
||||
|
||||
==========
|
||||
HOW TO SET UP YOUR OWN XML EDITING ENVIRONMENT:
|
||||
HOW TO SET UP YOUR OWN SGML EDITING ENVIRONMENT:
|
||||
==========
|
||||
|
||||
Trying to set up an XML Docbook editing environment the
|
||||
Trying to set up an SGML/XML Docbook editing environment the
|
||||
first time can be a daunting task.
|
||||
I use Linux-Mandrake, in part, because it has a fully-functional
|
||||
XML Docbook editing environment included as part of the
|
||||
SGML/XML Docbook editing environment included as part of the
|
||||
distribution CD's. If you have easier instructions for how to
|
||||
do this for a particular Linux distribution or platform, please
|
||||
let the team know at the mailing list: mozilla-webtools@mozilla.org.
|
||||
@@ -72,10 +74,9 @@ sgml-common
|
||||
|
||||
If you're getting these from RedHat, make sure you get the ones in the
|
||||
rawhide area. The ones in the 7.2 distribution are too old and don't
|
||||
include the XML stuff. The packages distrubuted with RedHat 8.0 and 9
|
||||
and known to work.
|
||||
include the XML stuff.
|
||||
|
||||
Download "ldp.dsl" from the Resources page on tldp.org. This is the
|
||||
Download "ldp.dsl" from the Resources page on linuxdoc.org. This is the
|
||||
stylesheet I use to get the HTML and text output. It works well, and has a
|
||||
nice, consistent look with the rest of the linuxdoc documents. You'll have to
|
||||
adjust the paths in ldp.dsl at the top of the file to reflect the actual
|
||||
@@ -91,8 +92,6 @@ dsssl>
|
||||
Note the difference is the top one points to the HTML docbook stylesheet,
|
||||
and the next one points to the PRINT docbook stylesheet.
|
||||
|
||||
Also note that modifying ldp.dsl doesn't seem to be needed on RedHat 9.
|
||||
|
||||
You know, this sure looks awful involved. Anyway, once you have this in
|
||||
place, add to your .bashrc:
|
||||
export SGML_CATALOG_FILES=/etc/sgml/catalog
|
||||
@@ -112,16 +111,13 @@ for tcsh users.
|
||||
folders are anywhere else on your system (for example, the openjade version
|
||||
might change if you get a new version at some point).
|
||||
|
||||
I suggest xemacs for editing your XML Docbook documents. The darn
|
||||
I suggest xemacs for editing your SGML/XML Docbook documents. The darn
|
||||
thing just works, and generally includes PSGML mode by default. Not to
|
||||
mention you can validate the SGML from right within it without having to
|
||||
remember the command-line syntax for nsgml (not that it's that hard
|
||||
anyway). If not, you can download psgml at
|
||||
http://www.sourceforge.net/projects/psgml.
|
||||
|
||||
Another good editor is the latest releases of vim and gvim. Vim will
|
||||
recognize DocBook tags and give them a different color than unreconized tags.
|
||||
|
||||
==========
|
||||
NOTES:
|
||||
==========
|
||||
@@ -129,17 +125,15 @@ NOTES:
|
||||
Here are the commands I use to maintain this documentation.
|
||||
You MUST have DocBook 4.1.2 set up correctly in order for this to work.
|
||||
|
||||
These commands can be run all at once using the ./makedocs.pl script.
|
||||
|
||||
To create HTML documentation:
|
||||
bash$ cd html
|
||||
bash$ jade -t sgml -i html -d $LDP_HOME/ldp.dsl\#html \
|
||||
$JADE_PUB/xml.dcl ../xml/Bugzilla-Guide.xml
|
||||
$JADE_PUB/xml.dcl ../sgml/Bugzilla-Guide.sgml
|
||||
|
||||
To create HTML documentation as a single big HTML file:
|
||||
bash$ cd html
|
||||
bash$ jade -V nochunks -t sgml -i html -d $LDP_HOME/ldp.dsl\#html \
|
||||
$JADE_PUB/xml.dcl ../xml/Bugzilla-Guide.xml >Bugzilla-Guide.html
|
||||
$JADE_PUB/xml.dcl ../sgml/Bugzilla-Guide.sgml >Bugzilla-Guide.html
|
||||
|
||||
To create TXT documentation as a single big TXT file:
|
||||
bash$ cd txt
|
||||
|
||||
16923
mozilla/webtools/bugzilla/docs/html/Bugzilla-Guide.html
Normal file
16923
mozilla/webtools/bugzilla/docs/html/Bugzilla-Guide.html
Normal file
File diff suppressed because it is too large
Load Diff
178
mozilla/webtools/bugzilla/docs/html/about.html
Normal file
178
mozilla/webtools/bugzilla/docs/html/about.html
Normal file
@@ -0,0 +1,178 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>About This Guide</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="The Bugzilla Guide"
|
||||
HREF="index.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Purpose and Scope of this Guide"
|
||||
HREF="aboutthisguide.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="index.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="aboutthisguide.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="chapter"
|
||||
><H1
|
||||
><A
|
||||
NAME="about">Chapter 1. About This Guide</H1
|
||||
><DIV
|
||||
CLASS="TOC"
|
||||
><DL
|
||||
><DT
|
||||
><B
|
||||
>Table of Contents</B
|
||||
></DT
|
||||
><DT
|
||||
>1.1. <A
|
||||
HREF="aboutthisguide.html"
|
||||
>Purpose and Scope of this Guide</A
|
||||
></DT
|
||||
><DT
|
||||
>1.2. <A
|
||||
HREF="copyright.html"
|
||||
>Copyright Information</A
|
||||
></DT
|
||||
><DT
|
||||
>1.3. <A
|
||||
HREF="disclaimer.html"
|
||||
>Disclaimer</A
|
||||
></DT
|
||||
><DT
|
||||
>1.4. <A
|
||||
HREF="newversions.html"
|
||||
>New Versions</A
|
||||
></DT
|
||||
><DT
|
||||
>1.5. <A
|
||||
HREF="credits.html"
|
||||
>Credits</A
|
||||
></DT
|
||||
><DT
|
||||
>1.6. <A
|
||||
HREF="translations.html"
|
||||
>Translations</A
|
||||
></DT
|
||||
><DT
|
||||
>1.7. <A
|
||||
HREF="conventions.html"
|
||||
>Document Conventions</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="index.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="aboutthisguide.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The Bugzilla Guide</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Purpose and Scope of this Guide</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
184
mozilla/webtools/bugzilla/docs/html/aboutthisguide.html
Normal file
184
mozilla/webtools/bugzilla/docs/html/aboutthisguide.html
Normal file
@@ -0,0 +1,184 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Purpose and Scope of this Guide</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Copyright Information"
|
||||
HREF="copyright.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="about.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="copyright.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="aboutthisguide">1.1. Purpose and Scope of this Guide</H1
|
||||
><P
|
||||
> Bugzilla is simply the best piece of bug-tracking software the
|
||||
world has ever seen. This document is intended to be the
|
||||
comprehensive guide to the installation, administration,
|
||||
maintenance, and use of the Bugzilla bug-tracking system.
|
||||
</P
|
||||
><P
|
||||
> This release of the Bugzilla Guide is the
|
||||
<EM
|
||||
>2.16</EM
|
||||
> release. It is so named that it
|
||||
may match the current version of Bugzilla. The numbering
|
||||
tradition stems from that used for many free software projects,
|
||||
in which <EM
|
||||
>even-numbered</EM
|
||||
> point releases (1.2,
|
||||
1.14, etc.) are considered "stable releases", intended for
|
||||
public consumption; on the other hand,
|
||||
<EM
|
||||
>odd-numbered</EM
|
||||
> point releases (1.3, 2.09,
|
||||
etc.) are considered unstable <EM
|
||||
>development</EM
|
||||
>
|
||||
releases intended for advanced users, systems administrators,
|
||||
developers, and those who enjoy a lot of pain.
|
||||
</P
|
||||
><P
|
||||
> Newer revisions of the Bugzilla Guide follow the numbering
|
||||
conventions of the main-tree Bugzilla releases, available at
|
||||
<A
|
||||
HREF="http://www.bugzilla.org/"
|
||||
TARGET="_top"
|
||||
>http://www.bugzilla.org/</A
|
||||
>. Intermediate releases will have
|
||||
a minor revision number following a period. The current version
|
||||
of Bugzilla, as of this writing (April 2nd, 2002) is 2.16; if
|
||||
something were seriously wrong with that edition of the Guide,
|
||||
subsequent releases would receive an additional dotted-decimal
|
||||
digit to indicate the update (2.16.1, 2.16.2, etc.).
|
||||
Got it? Good.
|
||||
</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="about.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="copyright.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>About This Guide</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Copyright Information</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
233
mozilla/webtools/bugzilla/docs/html/administration.html
Normal file
233
mozilla/webtools/bugzilla/docs/html/administration.html
Normal file
@@ -0,0 +1,233 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Administering Bugzilla</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="Win32 Installation Notes"
|
||||
HREF="win32.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Post-Installation Checklist"
|
||||
HREF="postinstall-check.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="win32.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="postinstall-check.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="chapter"
|
||||
><H1
|
||||
><A
|
||||
NAME="administration">Chapter 4. Administering Bugzilla</H1
|
||||
><DIV
|
||||
CLASS="TOC"
|
||||
><DL
|
||||
><DT
|
||||
><B
|
||||
>Table of Contents</B
|
||||
></DT
|
||||
><DT
|
||||
>4.1. <A
|
||||
HREF="postinstall-check.html"
|
||||
>Post-Installation Checklist</A
|
||||
></DT
|
||||
><DT
|
||||
>4.2. <A
|
||||
HREF="useradmin.html"
|
||||
>User Administration</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
>4.2.1. <A
|
||||
HREF="useradmin.html#defaultuser"
|
||||
>Creating the Default User</A
|
||||
></DT
|
||||
><DT
|
||||
>4.2.2. <A
|
||||
HREF="useradmin.html#manageusers"
|
||||
>Managing Other Users</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
>4.3. <A
|
||||
HREF="programadmin.html"
|
||||
>Product, Component, Milestone, and Version
|
||||
Administration</A
|
||||
></DT
|
||||
><DD
|
||||
><DL
|
||||
><DT
|
||||
>4.3.1. <A
|
||||
HREF="programadmin.html#products"
|
||||
>Products</A
|
||||
></DT
|
||||
><DT
|
||||
>4.3.2. <A
|
||||
HREF="programadmin.html#components"
|
||||
>Components</A
|
||||
></DT
|
||||
><DT
|
||||
>4.3.3. <A
|
||||
HREF="programadmin.html#versions"
|
||||
>Versions</A
|
||||
></DT
|
||||
><DT
|
||||
>4.3.4. <A
|
||||
HREF="programadmin.html#milestones"
|
||||
>Milestones</A
|
||||
></DT
|
||||
><DT
|
||||
>4.3.5. <A
|
||||
HREF="programadmin.html#voting"
|
||||
>Voting</A
|
||||
></DT
|
||||
><DT
|
||||
>4.3.6. <A
|
||||
HREF="programadmin.html#groups"
|
||||
>Groups and Group Security</A
|
||||
></DT
|
||||
></DL
|
||||
></DD
|
||||
><DT
|
||||
>4.4. <A
|
||||
HREF="security.html"
|
||||
>Bugzilla Security</A
|
||||
></DT
|
||||
></DL
|
||||
></DIV
|
||||
><FONT
|
||||
COLOR="RED"
|
||||
> Or, I just got this cool thing installed. Now what the heck do I
|
||||
do with it?
|
||||
</FONT
|
||||
><P
|
||||
> So you followed <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"<A
|
||||
HREF="installation.html"
|
||||
>Bugzilla Installation</A
|
||||
>"</SPAN
|
||||
> to the
|
||||
letter, and logged into Bugzilla for the very first time with your
|
||||
super-duper god account. You sit, contentedly staring at the
|
||||
Bugzilla Query Screen, the worst of the whole mad business of
|
||||
installing this terrific program behind you. It seems, though, you
|
||||
have nothing yet to query! Your first act of business should be to
|
||||
setup the operating parameters for Bugzilla so you can get busy
|
||||
getting data into your bug tracker.
|
||||
</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="win32.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="postinstall-check.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Win32 Installation Notes</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Post-Installation Checklist</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
160
mozilla/webtools/bugzilla/docs/html/bonsai.html
Normal file
160
mozilla/webtools/bugzilla/docs/html/bonsai.html
Normal file
@@ -0,0 +1,160 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Bonsai</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="Integrating Bugzilla with Third-Party Tools"
|
||||
HREF="integration.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Integrating Bugzilla with Third-Party Tools"
|
||||
HREF="integration.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="CVS"
|
||||
HREF="cvs.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="integration.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 5. Integrating Bugzilla with Third-Party Tools</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="cvs.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="bonsai">5.1. Bonsai</H1
|
||||
><P
|
||||
>Bonsai is a web-based tool for managing <A
|
||||
HREF="cvs.html"
|
||||
>CVS, the Concurrent Versioning System</A
|
||||
>
|
||||
. Using Bonsai, administrators can control open/closed status
|
||||
of trees, query a fast relational database back-end for change,
|
||||
branch, and comment information, and view changes made since the
|
||||
last time the tree was closed. These kinds of changes cause the
|
||||
engineer responsible to be <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"on the hook"</SPAN
|
||||
> (include
|
||||
cool URL link here for Hook policies at mozilla.org). Bonsai
|
||||
also includes gateways to <A
|
||||
HREF="tinderbox.html"
|
||||
>Tinderbox, the Mozilla automated build management system</A
|
||||
> and Bugzilla </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="integration.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="cvs.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Integrating Bugzilla with Third-Party Tools</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="integration.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>CVS</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
149
mozilla/webtools/bugzilla/docs/html/bsdinstall.html
Normal file
149
mozilla/webtools/bugzilla/docs/html/bsdinstall.html
Normal file
@@ -0,0 +1,149 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>BSD Installation Notes</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="Mac OS X Installation Notes"
|
||||
HREF="osx.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Installation General Notes"
|
||||
HREF="geninstall.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="osx.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 3. Installation</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="geninstall.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="bsdinstall">3.4. BSD Installation Notes</H1
|
||||
><P
|
||||
> For instructions on how to set up Bugzilla on FreeBSD, NetBSD, OpenBSD, BSDi, etc. please
|
||||
consult <A
|
||||
HREF="osx.html"
|
||||
>Section 3.3</A
|
||||
>.
|
||||
</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="osx.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="geninstall.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Mac OS X 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"
|
||||
>Installation General Notes</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
492
mozilla/webtools/bugzilla/docs/html/bzhacking.html
Normal file
492
mozilla/webtools/bugzilla/docs/html/bzhacking.html
Normal file
@@ -0,0 +1,492 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Hacking Bugzilla</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="Useful Patches and Utilities for Bugzilla"
|
||||
HREF="patches.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="The Quicksearch Utility"
|
||||
HREF="quicksearch.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="GNU Free Documentation License"
|
||||
HREF="gfdl.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="quicksearch.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix D. Useful Patches and Utilities for Bugzilla</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="gfdl.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="bzhacking">D.5. Hacking Bugzilla</H1
|
||||
><P
|
||||
> The following is a guide for reviewers when checking code into Bugzilla's
|
||||
CVS repostory at mozilla.org. If you wish to submit patches to Bugzilla,
|
||||
you should follow the rules and style conventions below. Any code that
|
||||
does not adhere to these basic rules will not be added to Bugzilla's
|
||||
codebase.
|
||||
</P
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="AEN2436">D.5.1. Things that have caused problems and should be avoided</H2
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> Usage of variables in Regular Expressions
|
||||
</P
|
||||
><P
|
||||
> It is very important that you don't use a variable in a regular
|
||||
expression unless that variable is supposed to contain an expression.
|
||||
This especially applies when using grep. You should use:
|
||||
</P
|
||||
><P
|
||||
> <TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> grep ($_ eq $value, @array);
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> -- NOT THIS --
|
||||
</P
|
||||
><P
|
||||
> <TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> grep (/$value/, @array);
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
>
|
||||
</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
|
||||
> If you need to use a non-expression variable inside of an expression, be
|
||||
sure to quote it properly (using <TT
|
||||
CLASS="function"
|
||||
>\Q..\E</TT
|
||||
>).
|
||||
</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></LI
|
||||
></OL
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="AEN2450">D.5.2. Coding Style for Bugzilla</H2
|
||||
><P
|
||||
> While it's true that not all of the code currently in Bugzilla adheres to
|
||||
this (or any) styleguide, it is something that is being worked toward. Therefore,
|
||||
we ask that all new code (submitted patches and new files) follow this guide
|
||||
as closely as possible (if you're only changing 1 or 2 lines, you don't have
|
||||
to reformat the entire file :).
|
||||
</P
|
||||
><P
|
||||
> The Bugzilla development team has decided to adopt the perl style guide as
|
||||
published by Larry Wall. This giude can be found in <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Programming
|
||||
Perl"</SPAN
|
||||
> (the camel book) or by typing <B
|
||||
CLASS="command"
|
||||
>man perlstyle</B
|
||||
> at
|
||||
your favorite shell prompt.
|
||||
</P
|
||||
><P
|
||||
> What appears below if a brief summary, please refer to the perl style
|
||||
guide if you don't see your question covered here. It is much better to submit
|
||||
a patch which fails these criteria than no patch at all, but please try to meet
|
||||
these minimum standards when submitting code to Bugzilla.
|
||||
</P
|
||||
><P
|
||||
></P
|
||||
><UL
|
||||
><LI
|
||||
><P
|
||||
> Whitespace
|
||||
</P
|
||||
><P
|
||||
> Bugzilla's preferred indentation is 4 spaces (no tabs, please).
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Curly braces.
|
||||
</P
|
||||
><P
|
||||
> The opening brace of a block should be on the same line as the statement
|
||||
that is causing the block and the closing brace should be at the same
|
||||
indentation level as that statement, for example:
|
||||
</P
|
||||
><P
|
||||
> <TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> if ($var) {
|
||||
print "The variable is true";
|
||||
}
|
||||
else {
|
||||
print "Try again";
|
||||
}
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> -- NOT THIS --
|
||||
</P
|
||||
><P
|
||||
> <TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> if ($var)
|
||||
{
|
||||
print "The variable is true";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "Try again";
|
||||
}
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
>
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Cookies
|
||||
</P
|
||||
><P
|
||||
> Bugzilla uses cookies to ease the user experience, but no new patches
|
||||
should <EM
|
||||
>require</EM
|
||||
> user-side cookies.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> File Names
|
||||
</P
|
||||
><P
|
||||
> File names for bugzilla code and support documention should be legal across
|
||||
multiple platforms. <TT
|
||||
CLASS="computeroutput"
|
||||
>\ / : * ? " < ></TT
|
||||
>
|
||||
and <TT
|
||||
CLASS="computeroutput"
|
||||
>|</TT
|
||||
> are all illegal characters for filenames
|
||||
on various platforms. Also, file names should not have spaces in them as they
|
||||
can cause confusion in CVS and other mozilla.org utilities.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Javascript dependencies
|
||||
</P
|
||||
><P
|
||||
> While Bugzilla uses Javascript to make the user experience easier, no patch
|
||||
to Bugzilla should <EM
|
||||
>require</EM
|
||||
> Javascript.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Patch Format
|
||||
</P
|
||||
><P
|
||||
> All patches submitted for inclusion into Bugzilla should be in the form of a
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"unified diff"</SPAN
|
||||
>. This comes from using <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"diff -u"</SPAN
|
||||
>
|
||||
instead of simply <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"diff"</SPAN
|
||||
> when creating your patch. This will
|
||||
result in quicker acceptance of the patch.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Schema Changes
|
||||
</P
|
||||
><P
|
||||
> If you make schema changes, you should modify <TT
|
||||
CLASS="filename"
|
||||
>sanitycheck.cgi</TT
|
||||
>
|
||||
to support the new schema. All referential columns should be checked.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Taint Mode
|
||||
</P
|
||||
><P
|
||||
> All new cgis must run in Taint mode (Perl taint and DBI taint), and existing cgi's
|
||||
which run in taint mode must not have taint mode turned off.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Templatization
|
||||
</P
|
||||
><P
|
||||
> Patches to Bugzilla need to support templates so they do not force user interface choices
|
||||
on Bugzilla administrators.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Variable Names
|
||||
</P
|
||||
><P
|
||||
> If a variable is scoped globally (<TT
|
||||
CLASS="computeroutput"
|
||||
>$::variable</TT
|
||||
>)
|
||||
its name should be descriptive of what it contains. Local variables can be named
|
||||
a bit looser, provided the context makes their content obvious. For example,
|
||||
<TT
|
||||
CLASS="computeroutput"
|
||||
>$ret</TT
|
||||
> could be used as a staging variable for a
|
||||
routine's return value as the line <TT
|
||||
CLASS="computeroutput"
|
||||
>return $ret;</TT
|
||||
>
|
||||
will make it blatantly obvious what the variable holds and most likely be shown
|
||||
on the same screen as <TT
|
||||
CLASS="computeroutput"
|
||||
>my $ret = "";</TT
|
||||
>.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Cross Database Compatability
|
||||
</P
|
||||
><P
|
||||
> Bugzilla was originally written to work with MySQL and therefore took advantage
|
||||
of some of its features that aren't contained in other RDBMS software. These
|
||||
should be avoided in all new code. Examples of these features are enums and
|
||||
<TT
|
||||
CLASS="function"
|
||||
>encrypt()</TT
|
||||
>.
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Cross Platform Compatability
|
||||
</P
|
||||
><P
|
||||
> While Bugzilla was written to be used on Unix based systems (and Unix/Linux is
|
||||
still the only officially supported platform) there are many who desire/need to
|
||||
run Bugzilla on Microsoft Windows boxes. Whenever possible, we should strive
|
||||
not to make the lives of these people any more complicated and avoid doing things
|
||||
that break Bugzilla's ability to run on multiple operating systems.
|
||||
</P
|
||||
></LI
|
||||
></UL
|
||||
></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="quicksearch.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="gfdl.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The Quicksearch Utility</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="patches.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>GNU Free Documentation License</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
269
mozilla/webtools/bugzilla/docs/html/cmdline.html
Normal file
269
mozilla/webtools/bugzilla/docs/html/cmdline.html
Normal file
@@ -0,0 +1,269 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Command-line Bugzilla Queries</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="Useful Patches and Utilities for Bugzilla"
|
||||
HREF="patches.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="The setperl.csh Utility"
|
||||
HREF="setperl.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="The Quicksearch Utility"
|
||||
HREF="quicksearch.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="setperl.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix D. Useful Patches and Utilities for Bugzilla</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="quicksearch.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="cmdline">D.3. Command-line Bugzilla Queries</H1
|
||||
><P
|
||||
> Users can query Bugzilla from the command line using this suite
|
||||
of utilities.
|
||||
</P
|
||||
><P
|
||||
> The query.conf file contains the mapping from options to field
|
||||
names and comparison types. Quoted option names are "grepped"
|
||||
for, so it should be easy to edit this file. Comments (#) have
|
||||
no effect; you must make sure these lines do not contain any
|
||||
quoted "option"
|
||||
</P
|
||||
><P
|
||||
> buglist is a shell script which submits a Bugzilla query and
|
||||
writes the resulting HTML page to stdout. It supports both
|
||||
short options, (such as "-Afoo" or "-Rbar") and long options
|
||||
(such as "--assignedto=foo" or "--reporter=bar"). If the first
|
||||
character of an option is not "-", it is treated as if it were
|
||||
prefixed with "--default=".
|
||||
</P
|
||||
><P
|
||||
> The columlist is taken from the COLUMNLIST environment variable.
|
||||
This is equivalent to the "Change Columns" option when you list
|
||||
bugs in buglist.cgi. If you have already used Bugzilla, use
|
||||
<B
|
||||
CLASS="command"
|
||||
>grep COLUMLIST ~/.netscape/cookies</B
|
||||
> to see
|
||||
your current COLUMNLIST setting.
|
||||
</P
|
||||
><P
|
||||
> bugs is a simple shell script which calls buglist and extracts
|
||||
the bug numbers from the output. Adding the prefix
|
||||
"http://bugzilla.mozilla.org/buglist.cgi?bug_id=" turns the bug
|
||||
list into a working link if any bugs are found. Counting bugs is
|
||||
easy. Pipe the results through <B
|
||||
CLASS="command"
|
||||
>sed -e 's/,/ /g' | wc |
|
||||
awk '{printf $2 "\n"}'</B
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> Akkana says she has good results piping buglist output through
|
||||
<B
|
||||
CLASS="command"
|
||||
>w3m -T text/html -dump</B
|
||||
>
|
||||
</P
|
||||
><DIV
|
||||
CLASS="procedure"
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> Download three files:
|
||||
</P
|
||||
><OL
|
||||
CLASS="SUBSTEPS"
|
||||
TYPE="a"
|
||||
><LI
|
||||
><P
|
||||
> <TT
|
||||
CLASS="computeroutput"
|
||||
> <TT
|
||||
CLASS="prompt"
|
||||
>bash$</TT
|
||||
> <B
|
||||
CLASS="command"
|
||||
>wget -O
|
||||
query.conf
|
||||
'http://bugzilla.mozilla.org/showattachment.cgi?attach_id=26157'</B
|
||||
> </TT
|
||||
>
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> <TT
|
||||
CLASS="computeroutput"
|
||||
> <TT
|
||||
CLASS="prompt"
|
||||
>bash$</TT
|
||||
> <B
|
||||
CLASS="command"
|
||||
>wget -O
|
||||
buglist
|
||||
'http://bugzilla.mozilla.org/showattachment.cgi?attach_id=26944'</B
|
||||
> </TT
|
||||
>
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> <TT
|
||||
CLASS="computeroutput"
|
||||
> <TT
|
||||
CLASS="prompt"
|
||||
>bash#</TT
|
||||
> <B
|
||||
CLASS="command"
|
||||
>wget -O
|
||||
bugs
|
||||
'http://bugzilla.mozilla.org/showattachment.cgi?attach_id=26215'</B
|
||||
> </TT
|
||||
>
|
||||
</P
|
||||
></LI
|
||||
></OL
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
> Make your utilities executable:
|
||||
<TT
|
||||
CLASS="computeroutput"
|
||||
> <TT
|
||||
CLASS="prompt"
|
||||
>bash$</TT
|
||||
>
|
||||
<B
|
||||
CLASS="command"
|
||||
>chmod u+x buglist bugs</B
|
||||
>
|
||||
</TT
|
||||
>
|
||||
</P
|
||||
></LI
|
||||
></OL
|
||||
></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="setperl.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="quicksearch.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The setperl.csh Utility</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="patches.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>The Quicksearch Utility</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
150
mozilla/webtools/bugzilla/docs/html/contributors.html
Normal file
150
mozilla/webtools/bugzilla/docs/html/contributors.html
Normal file
@@ -0,0 +1,150 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Contributors</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Credits"
|
||||
HREF="credits.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Feedback"
|
||||
HREF="feedback.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="credits.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="feedback.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="contributors">1.6. Contributors</H1
|
||||
><P
|
||||
> Thanks go to these people for significant contributions to this
|
||||
documentation (in no particular order):
|
||||
</P
|
||||
><P
|
||||
> Andrew Pearson, Spencer Smith, Eric Hanson, Kevin Brannen, Ron
|
||||
Teitelbaum, Jacob Steenhagen, Joe Robins
|
||||
</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="credits.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="feedback.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Credits</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Feedback</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
462
mozilla/webtools/bugzilla/docs/html/conventions.html
Normal file
462
mozilla/webtools/bugzilla/docs/html/conventions.html
Normal file
@@ -0,0 +1,462 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Document Conventions</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Translations"
|
||||
HREF="translations.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Using Bugzilla"
|
||||
HREF="using.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="translations.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="using.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="conventions">1.7. Document Conventions</H1
|
||||
><P
|
||||
> This document uses the following conventions
|
||||
</P
|
||||
><DIV
|
||||
CLASS="informaltable"
|
||||
><A
|
||||
NAME="AEN91"><P
|
||||
></P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
CLASS="CALSTABLE"
|
||||
><THEAD
|
||||
><TR
|
||||
><TH
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Descriptions</TH
|
||||
><TH
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Appearance</TH
|
||||
></TR
|
||||
></THEAD
|
||||
><TBODY
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Warnings</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><DIV
|
||||
CLASS="caution"
|
||||
><P
|
||||
></P
|
||||
><TABLE
|
||||
CLASS="caution"
|
||||
WIDTH="100%"
|
||||
BORDER="0"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="25"
|
||||
ALIGN="CENTER"
|
||||
VALIGN="TOP"
|
||||
><IMG
|
||||
SRC="../images/caution.gif"
|
||||
HSPACE="5"
|
||||
ALT="Caution"></TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="TOP"
|
||||
><P
|
||||
>Don't run with scissors!</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Hint</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><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
|
||||
>Warm jar lids under the hot tap to loosen them.</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Notes</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><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
|
||||
>Dear John...</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Information requiring special attention</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><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
|
||||
>Read this or the cat gets it.</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>File Names</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><TT
|
||||
CLASS="filename"
|
||||
>file.extension</TT
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Directory Names</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><TT
|
||||
CLASS="filename"
|
||||
>directory</TT
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Commands to be typed</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><B
|
||||
CLASS="command"
|
||||
>command</B
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Applications Names</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><SPAN
|
||||
CLASS="application"
|
||||
>application</SPAN
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><I
|
||||
CLASS="foreignphrase"
|
||||
>Prompt</I
|
||||
> of users command under bash shell</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>bash$</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><I
|
||||
CLASS="foreignphrase"
|
||||
>Prompt</I
|
||||
> of root users command under bash shell</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>bash#</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><I
|
||||
CLASS="foreignphrase"
|
||||
>Prompt</I
|
||||
> of user command under tcsh shell</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>tcsh$</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Environment Variables</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><TT
|
||||
CLASS="envar"
|
||||
>VARIABLE</TT
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Emphasized word</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><EM
|
||||
>word</EM
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
>Code Example</TD
|
||||
><TD
|
||||
ALIGN="LEFT"
|
||||
VALIGN="MIDDLE"
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
><TT
|
||||
CLASS="sgmltag"
|
||||
><para></TT
|
||||
>Beginning and end of paragraph<TT
|
||||
CLASS="sgmltag"
|
||||
></para></TT
|
||||
></PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></TD
|
||||
></TR
|
||||
></TBODY
|
||||
></TABLE
|
||||
><P
|
||||
></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="translations.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="using.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Translations</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Using Bugzilla</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
191
mozilla/webtools/bugzilla/docs/html/copyright.html
Normal file
191
mozilla/webtools/bugzilla/docs/html/copyright.html
Normal file
@@ -0,0 +1,191 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Copyright Information</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Purpose and Scope of this Guide"
|
||||
HREF="aboutthisguide.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Disclaimer"
|
||||
HREF="disclaimer.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="aboutthisguide.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="disclaimer.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="copyright">1.2. Copyright Information</H1
|
||||
><A
|
||||
NAME="AEN39"><TABLE
|
||||
BORDER="0"
|
||||
WIDTH="100%"
|
||||
CELLSPACING="0"
|
||||
CELLPADDING="0"
|
||||
CLASS="BLOCKQUOTE"
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
VALIGN="TOP"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
VALIGN="TOP"
|
||||
><P
|
||||
> Permission is granted to copy, distribute and/or modify this
|
||||
document under the terms of the GNU Free Documentation
|
||||
License, Version 1.1 or any later version published by the
|
||||
Free Software Foundation; with no Invariant Sections, no
|
||||
Front-Cover Texts, and with no Back-Cover Texts. A copy of
|
||||
the license is included in the section entitled "GNU Free
|
||||
Documentation License".
|
||||
</P
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
VALIGN="TOP"
|
||||
> </TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
COLSPAN="2"
|
||||
ALIGN="RIGHT"
|
||||
VALIGN="TOP"
|
||||
>--<SPAN
|
||||
CLASS="attribution"
|
||||
>Copyright (c) 2000-2002 Matthew P. Barnson and The Bugzilla Team</SPAN
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
> </TD
|
||||
></TR
|
||||
></TABLE
|
||||
><P
|
||||
> If you have any questions regarding this document, its
|
||||
copyright, or publishing this document in non-electronic form,
|
||||
please contact The Bugzilla Team.
|
||||
</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="aboutthisguide.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="disclaimer.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Purpose and Scope of this Guide</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Disclaimer</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
208
mozilla/webtools/bugzilla/docs/html/credits.html
Normal file
208
mozilla/webtools/bugzilla/docs/html/credits.html
Normal file
@@ -0,0 +1,208 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Credits</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="New Versions"
|
||||
HREF="newversions.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Translations"
|
||||
HREF="translations.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="newversions.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="translations.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="credits">1.5. Credits</H1
|
||||
><P
|
||||
> The people listed below have made enormous contributions to the
|
||||
creation of this Guide, through their dedicated hacking efforts,
|
||||
numerous e-mail and IRC support sessions, and overall excellent
|
||||
contribution to the Bugzilla community:
|
||||
</P
|
||||
><P
|
||||
> <A
|
||||
HREF="mailto://mbarnson@sisna.com"
|
||||
TARGET="_top"
|
||||
>Matthew P. Barnson</A
|
||||
>
|
||||
for pulling together the Bugzilla Guide and shepherding it to 2.14.
|
||||
</P
|
||||
><P
|
||||
> <A
|
||||
HREF="mailto://terry@mozilla.org"
|
||||
TARGET="_top"
|
||||
>Terry Weissman</A
|
||||
>
|
||||
for initially writing Bugzilla and creating the
|
||||
README upon which the UNIX installation documentation is largely based.
|
||||
</P
|
||||
><P
|
||||
> <A
|
||||
HREF="mailto://tara@tequilarista.org"
|
||||
TARGET="_top"
|
||||
>Tara
|
||||
Hernandez</A
|
||||
> for keeping Bugzilla development going
|
||||
strong after Terry left Mozilla.org
|
||||
</P
|
||||
><P
|
||||
> <A
|
||||
HREF="mailto://dkl@redhat.com"
|
||||
TARGET="_top"
|
||||
>Dave Lawrence</A
|
||||
> for
|
||||
providing insight into the key differences between Red Hat's
|
||||
customized Bugzilla, and being largely responsible for the "Red
|
||||
Hat Bugzilla" appendix
|
||||
</P
|
||||
><P
|
||||
> <A
|
||||
HREF="mailto://endico@mozilla.org"
|
||||
TARGET="_top"
|
||||
>Dawn Endico</A
|
||||
> for
|
||||
being a hacker extraordinaire and putting up with my incessant
|
||||
questions and arguments on irc.mozilla.org in #mozwebtools
|
||||
</P
|
||||
><P
|
||||
> Last but not least, all the members of the <A
|
||||
HREF="news://news.mozilla.org/netscape/public/mozilla/webtools"
|
||||
TARGET="_top"
|
||||
> netscape.public.mozilla.webtools</A
|
||||
> newsgroup. Without your discussions, insight, suggestions, and patches, this could never have happened.
|
||||
</P
|
||||
><P
|
||||
> Thanks also go to the following people for significant contributions
|
||||
to this documentation (in no particular order):
|
||||
</P
|
||||
><P
|
||||
> Zach Liption, Andrew Pearson, Spencer Smith, Eric Hanson, Kevin Brannen,
|
||||
Ron Teitelbaum, Jacob Steenhagen, Joe Robins.
|
||||
</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="newversions.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="translations.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>New Versions</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Translations</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
174
mozilla/webtools/bugzilla/docs/html/cvs.html
Normal file
174
mozilla/webtools/bugzilla/docs/html/cvs.html
Normal file
@@ -0,0 +1,174 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>CVS</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="Integrating Bugzilla with Third-Party Tools"
|
||||
HREF="integration.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Bonsai"
|
||||
HREF="bonsai.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Perforce SCM"
|
||||
HREF="scm.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="bonsai.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 5. Integrating Bugzilla with Third-Party Tools</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="scm.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="cvs">5.2. CVS</H1
|
||||
><P
|
||||
>CVS integration is best accomplished, at this point, using
|
||||
the Bugzilla Email Gateway. There have been some files
|
||||
submitted to allow greater CVS integration, but we need to make
|
||||
certain that Bugzilla is not tied into one particular software
|
||||
management package.</P
|
||||
><P
|
||||
> Follow the instructions in the FAQ for enabling Bugzilla e-mail
|
||||
integration. Ensure that your check-in script sends an email to
|
||||
your Bugzilla e-mail gateway with the subject of <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"[Bug
|
||||
XXXX]"</SPAN
|
||||
>, and you can have CVS check-in comments append
|
||||
to your Bugzilla bug. If you have your check-in script include
|
||||
an @resolution field, you can even change the Bugzilla bug
|
||||
state.
|
||||
</P
|
||||
><P
|
||||
> There is also a project, based upon somewhat dated Bugzilla
|
||||
code, to integrate CVS and Bugzilla through CVS' ability to
|
||||
email. Check it out at:
|
||||
<A
|
||||
HREF="http://homepages.kcbbs.gen.nz/~tonyg/"
|
||||
TARGET="_top"
|
||||
> http://homepages.kcbbs.gen.nz/~tonyg/</A
|
||||
>, under the
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"cvszilla"</SPAN
|
||||
> link.
|
||||
</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="bonsai.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="scm.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Bonsai</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="integration.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Perforce SCM</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
185
mozilla/webtools/bugzilla/docs/html/database.html
Normal file
185
mozilla/webtools/bugzilla/docs/html/database.html
Normal file
@@ -0,0 +1,185 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>The Bugzilla Database</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="Software Download Links"
|
||||
HREF="downloadlinks.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Database Schema Chart"
|
||||
HREF="dbschema.html"></HEAD
|
||||
><BODY
|
||||
CLASS="appendix"
|
||||
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="downloadlinks.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="dbschema.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="appendix"
|
||||
><H1
|
||||
><A
|
||||
NAME="database">Appendix C. The Bugzilla Database</H1
|
||||
><DIV
|
||||
CLASS="TOC"
|
||||
><DL
|
||||
><DT
|
||||
><B
|
||||
>Table of Contents</B
|
||||
></DT
|
||||
><DT
|
||||
>C.1. <A
|
||||
HREF="dbschema.html"
|
||||
>Database Schema Chart</A
|
||||
></DT
|
||||
><DT
|
||||
>C.2. <A
|
||||
HREF="dbdoc.html"
|
||||
>MySQL Bugzilla Database Introduction</A
|
||||
></DT
|
||||
><DT
|
||||
>C.3. <A
|
||||
HREF="granttables.html"
|
||||
>MySQL Permissions & Grant Tables</A
|
||||
></DT
|
||||
></DL
|
||||
></DIV
|
||||
><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
|
||||
> This document really needs to be updated with more fleshed out information about primary keys, interrelationships, and maybe some nifty tables to document dependencies. Any takers?
|
||||
</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></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="downloadlinks.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="dbschema.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Software Download Links</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Database Schema Chart</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
576
mozilla/webtools/bugzilla/docs/html/dbdoc.html
Normal file
576
mozilla/webtools/bugzilla/docs/html/dbdoc.html
Normal file
@@ -0,0 +1,576 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>MySQL Bugzilla Database 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="UP"
|
||||
TITLE="The Bugzilla Database"
|
||||
HREF="database.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Database Schema Chart"
|
||||
HREF="dbschema.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="MySQL Permissions & Grant Tables"
|
||||
HREF="granttables.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="dbschema.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix C. The Bugzilla Database</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="granttables.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="dbdoc">C.2. MySQL Bugzilla Database Introduction</H1
|
||||
><P
|
||||
> This information comes straight from my life. I was forced to learn how
|
||||
Bugzilla organizes database because of nitpicky requests from users for tiny
|
||||
changes in wording, rather than having people re-educate themselves or
|
||||
figure out how to work our procedures around the tool. It sucks, but it can
|
||||
and will happen to you, so learn how the schema works and deal with it when it
|
||||
comes.
|
||||
</P
|
||||
><P
|
||||
> So, here you are with your brand-new installation of Bugzilla. You've got
|
||||
MySQL set up, Apache working right, Perl DBI and DBD talking to the database
|
||||
flawlessly. Maybe you've even entered a few test bugs to make sure email's
|
||||
working; people seem to be notified of new bugs and changes, and you can
|
||||
enter and edit bugs to your heart's content. Perhaps you've gone through the
|
||||
trouble of setting up a gateway for people to submit bugs to your database via
|
||||
email, have had a few people test it, and received rave reviews from your beta
|
||||
testers.
|
||||
</P
|
||||
><P
|
||||
> What's the next thing you do? Outline a training strategy for your
|
||||
development team, of course, and bring them up to speed on the new tool you've
|
||||
labored over for hours.
|
||||
</P
|
||||
><P
|
||||
> Your first training session starts off very well! You have a captive
|
||||
audience which seems enraptured by the efficiency embodied in this thing called
|
||||
"Bugzilla". You are caught up describing the nifty features, how people can
|
||||
save favorite queries in the database, set them up as headers and footers on
|
||||
their pages, customize their layouts, generate reports, track status with
|
||||
greater efficiency than ever before, leap tall buildings with a single bound
|
||||
and rescue Jane from the clutches of Certain Death!
|
||||
</P
|
||||
><P
|
||||
> But Certain Death speaks up -- a tiny voice, from the dark corners of the
|
||||
conference room. "I have a concern," the voice hisses from the darkness,
|
||||
"about the use of the word 'verified'.
|
||||
</P
|
||||
><P
|
||||
> The room, previously filled with happy chatter, lapses into reverential
|
||||
silence as Certain Death (better known as the Vice President of Software
|
||||
Engineering) continues. "You see, for two years we've used the word 'verified'
|
||||
to indicate that a developer or quality assurance engineer has confirmed that,
|
||||
in fact, a bug is valid. I don't want to lose two years of training to a
|
||||
new software product. You need to change the bug status of 'verified' to
|
||||
'approved' as soon as possible. To avoid confusion, of course."
|
||||
</P
|
||||
><P
|
||||
> Oh no! Terror strikes your heart, as you find yourself mumbling "yes, yes, I
|
||||
don't think that would be a problem," You review the changes with Certain
|
||||
Death, and continue to jabber on, "no, it's not too big a change. I mean, we
|
||||
have the source code, right? You know, 'Use the Source, Luke' and all that...
|
||||
no problem," All the while you quiver inside like a beached jellyfish bubbling,
|
||||
burbling, and boiling on a hot Jamaican sand dune...
|
||||
</P
|
||||
><P
|
||||
> Thus begins your adventure into the heart of Bugzilla. You've been forced
|
||||
to learn about non-portable enum() fields, varchar columns, and tinyint
|
||||
definitions. The Adventure Awaits You!
|
||||
</P
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="AEN2272">C.2.1. Bugzilla Database Basics</H2
|
||||
><P
|
||||
> If you were like me, at this point you're totally clueless
|
||||
about the internals of MySQL, and if it weren't for this
|
||||
executive order from the Vice President you couldn't care less
|
||||
about the difference between a <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"bigint"</SPAN
|
||||
> and a
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"tinyint"</SPAN
|
||||
> entry in MySQL. I recommend you refer
|
||||
to the MySQL documentation, available at <A
|
||||
HREF="http://www.mysql.com/doc.html"
|
||||
TARGET="_top"
|
||||
>MySQL.com</A
|
||||
>. Below are the basics you need to know about the Bugzilla database. Check the chart above for more details.
|
||||
</P
|
||||
><P
|
||||
><P
|
||||
></P
|
||||
><OL
|
||||
TYPE="1"
|
||||
><LI
|
||||
><P
|
||||
> To connect to your database:
|
||||
</P
|
||||
><P
|
||||
> <TT
|
||||
CLASS="prompt"
|
||||
>bash#</TT
|
||||
><B
|
||||
CLASS="command"
|
||||
>mysql</B
|
||||
><TT
|
||||
CLASS="parameter"
|
||||
><I
|
||||
>-u root</I
|
||||
></TT
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> If this works without asking you for a password,
|
||||
<EM
|
||||
>shame on you</EM
|
||||
>! You should have
|
||||
locked your security down like the installation
|
||||
instructions told you to. You can find details on
|
||||
locking down your database in the Bugzilla FAQ in this
|
||||
directory (under "Security"), or more robust security
|
||||
generalities in the MySQL searchable documentation at
|
||||
http://www.mysql.com/php/manual.php3?section=Privilege_system .
|
||||
</P
|
||||
></LI
|
||||
><LI
|
||||
><P
|
||||
>You should now be at a prompt that looks like
|
||||
this:</P
|
||||
><P
|
||||
><TT
|
||||
CLASS="prompt"
|
||||
>mysql></TT
|
||||
></P
|
||||
><P
|
||||
>At the prompt, if <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"bugs"</SPAN
|
||||
> is the name
|
||||
you chose in the<TT
|
||||
CLASS="filename"
|
||||
>localconfig</TT
|
||||
> file
|
||||
for your Bugzilla database, type:</P
|
||||
><P
|
||||
><TT
|
||||
CLASS="prompt"
|
||||
>mysql</TT
|
||||
><B
|
||||
CLASS="command"
|
||||
>use bugs;</B
|
||||
></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
|
||||
>Don't forget the <SPAN
|
||||
CLASS="QUOTE"
|
||||
>";"</SPAN
|
||||
> at the end of
|
||||
each line, or you'll be kicking yourself later.</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></LI
|
||||
></OL
|
||||
>
|
||||
</P
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H3
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="AEN2301">C.2.1.1. Bugzilla Database Tables</H3
|
||||
><P
|
||||
> Imagine your MySQL database as a series of
|
||||
spreadsheets, and you won't be too far off. If you use this
|
||||
command:</P
|
||||
><P
|
||||
><TT
|
||||
CLASS="prompt"
|
||||
>mysql></TT
|
||||
><B
|
||||
CLASS="command"
|
||||
>show tables from bugs;</B
|
||||
></P
|
||||
><P
|
||||
>you'll be able to see all the
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"spreadsheets"</SPAN
|
||||
> (tables) in your database. It
|
||||
is similar to a file system, only faster and more robust for
|
||||
certain types of operations.</P
|
||||
><P
|
||||
>From the command issued above, ou should have some
|
||||
output that looks like this:
|
||||
<TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> +-------------------+
|
||||
| Tables in bugs |
|
||||
+-------------------+
|
||||
| attachments |
|
||||
| bugs |
|
||||
| bugs_activity |
|
||||
| cc |
|
||||
| components |
|
||||
| dependencies |
|
||||
| fielddefs |
|
||||
| groups |
|
||||
| keyworddefs |
|
||||
| keywords |
|
||||
| logincookies |
|
||||
| longdescs |
|
||||
| milestones |
|
||||
| namedqueries |
|
||||
| products |
|
||||
| profiles |
|
||||
| profiles_activity |
|
||||
| shadowlog |
|
||||
| tokens |
|
||||
| versions |
|
||||
| votes |
|
||||
| watch |
|
||||
+-------------------+
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></P
|
||||
><P
|
||||
CLASS="literallayout"
|
||||
><br>
|
||||
<br>
|
||||
Here's an overview of what each table does. Most columns in each table have<br>
|
||||
descriptive names that make it fairly trivial to figure out their jobs.<br>
|
||||
<br>
|
||||
attachments: This table stores all attachments to bugs. It tends to be your<br>
|
||||
largest table, yet also generally has the fewest entries because file<br>
|
||||
attachments are so (relatively) large.<br>
|
||||
<br>
|
||||
bugs: This is the core of your system. The bugs table stores most of the<br>
|
||||
current information about a bug, with the exception of the info stored in the<br>
|
||||
other tables.<br>
|
||||
<br>
|
||||
bugs_activity: This stores information regarding what changes are made to bugs<br>
|
||||
when -- a history file.<br>
|
||||
<br>
|
||||
cc: This tiny table simply stores all the CC information for any bug which has<br>
|
||||
any entries in the CC field of the bug. Note that, like most other tables in<br>
|
||||
Bugzilla, it does not refer to users by their user names, but by their unique<br>
|
||||
userid, stored as a primary key in the profiles table.<br>
|
||||
<br>
|
||||
components: This stores the programs and components (or products and<br>
|
||||
components, in newer Bugzilla parlance) for Bugzilla. Curiously, the "program"<br>
|
||||
(product) field is the full name of the product, rather than some other unique<br>
|
||||
identifier, like bug_id and user_id are elsewhere in the database.<br>
|
||||
<br>
|
||||
dependencies: Stores data about those cool dependency trees.<br>
|
||||
<br>
|
||||
fielddefs: A nifty table that defines other tables. For instance, when you<br>
|
||||
submit a form that changes the value of "AssignedTo" this table allows<br>
|
||||
translation to the actual field name "assigned_to" for entry into MySQL.<br>
|
||||
<br>
|
||||
groups: defines bitmasks for groups. A bitmask is a number that can uniquely<br>
|
||||
identify group memberships. For instance, say the group that is allowed to<br>
|
||||
tweak parameters is assigned a value of "1", the group that is allowed to edit<br>
|
||||
users is assigned a "2", and the group that is allowed to create new groups is<br>
|
||||
assigned the bitmask of "4". By uniquely combining the group bitmasks (much<br>
|
||||
like the chmod command in UNIX,) you can identify a user is allowed to tweak<br>
|
||||
parameters and create groups, but not edit users, by giving him a bitmask of<br>
|
||||
"5", or a user allowed to edit users and create groups, but not tweak<br>
|
||||
parameters, by giving him a bitmask of "6" Simple, huh?<br>
|
||||
If this makes no sense to you, try this at the mysql prompt:<br>
|
||||
mysql> select * from groups;<br>
|
||||
You'll see the list, it makes much more sense that way.<br>
|
||||
<br>
|
||||
keyworddefs: Definitions of keywords to be used<br>
|
||||
<br>
|
||||
keywords: Unlike what you'd think, this table holds which keywords are<br>
|
||||
associated with which bug id's.<br>
|
||||
<br>
|
||||
logincookies: This stores every login cookie ever assigned to you for every<br>
|
||||
machine you've ever logged into Bugzilla from. Curiously, it never does any<br>
|
||||
housecleaning -- I see cookies in this file I've not used for months. However,<br>
|
||||
since Bugzilla never expires your cookie (for convenience' sake), it makes<br>
|
||||
sense.<br>
|
||||
<br>
|
||||
longdescs: The meat of bugzilla -- here is where all user comments are stored!<br>
|
||||
You've only got 2^24 bytes per comment (it's a mediumtext field), so speak<br>
|
||||
sparingly -- that's only the amount of space the Old Testament from the Bible<br>
|
||||
would take (uncompressed, 16 megabytes). Each comment is keyed to the<br>
|
||||
bug_id to which it's attached, so the order is necessarily chronological, for<br>
|
||||
comments are played back in the order in which they are received.<br>
|
||||
<br>
|
||||
milestones: Interesting that milestones are associated with a specific product<br>
|
||||
in this table, but Bugzilla does not yet support differing milestones by<br>
|
||||
product through the standard configuration interfaces.<br>
|
||||
<br>
|
||||
namedqueries: This is where everybody stores their "custom queries". Very<br>
|
||||
cool feature; it beats the tar out of having to bookmark each cool query you<br>
|
||||
construct.<br>
|
||||
<br>
|
||||
products: What products you have, whether new bug entries are allowed for the<br>
|
||||
product, what milestone you're working toward on that product, votes, etc. It<br>
|
||||
will be nice when the components table supports these same features, so you<br>
|
||||
could close a particular component for bug entry without having to close an<br>
|
||||
entire product...<br>
|
||||
<br>
|
||||
profiles: Ahh, so you were wondering where your precious user information was<br>
|
||||
stored? Here it is! With the passwords in plain text for all to see! (but<br>
|
||||
sshh... don't tell your users!)<br>
|
||||
<br>
|
||||
profiles_activity: Need to know who did what when to who's profile? This'll<br>
|
||||
tell you, it's a pretty complete history.<br>
|
||||
<br>
|
||||
shadowlog: I could be mistaken here, but I believe this table tells you when<br>
|
||||
your shadow database is updated and what commands were used to update it. We<br>
|
||||
don't use a shadow database at our site yet, so it's pretty empty for us.<br>
|
||||
<br>
|
||||
versions: Version information for every product<br>
|
||||
<br>
|
||||
votes: Who voted for what when<br>
|
||||
<br>
|
||||
watch: Who (according to userid) is watching who's bugs (according to their<br>
|
||||
userid).<br>
|
||||
<br>
|
||||
<br>
|
||||
===<br>
|
||||
THE DETAILS<br>
|
||||
===<br>
|
||||
<br>
|
||||
Ahh, so you're wondering just what to do with the information above? At the<br>
|
||||
mysql prompt, you can view any information about the columns in a table with<br>
|
||||
this command (where "table" is the name of the table you wish to view):<br>
|
||||
<br>
|
||||
mysql> show columns from table;<br>
|
||||
<br>
|
||||
You can also view all the data in a table with this command:<br>
|
||||
<br>
|
||||
mysql> select * from table;<br>
|
||||
<br>
|
||||
-- note: this is a very bad idea to do on, for instance, the "bugs" table if<br>
|
||||
you have 50,000 bugs. You'll be sitting there a while until you ctrl-c or<br>
|
||||
50,000 bugs play across your screen.<br>
|
||||
<br>
|
||||
You can limit the display from above a little with the command, where<br>
|
||||
"column" is the name of the column for which you wish to restrict information:<br>
|
||||
<br>
|
||||
mysql> select * from table where (column = "some info");<br>
|
||||
<br>
|
||||
-- or the reverse of this<br>
|
||||
<br>
|
||||
mysql> select * from table where (column != "some info");<br>
|
||||
<br>
|
||||
Let's take our example from the introduction, and assume you need to change<br>
|
||||
the word "verified" to "approved" in the resolution field. We know from the<br>
|
||||
above information that the resolution is likely to be stored in the "bugs"<br>
|
||||
table. Note we'll need to change a little perl code as well as this database<br>
|
||||
change, but I won't plunge into that in this document. Let's verify the<br>
|
||||
information is stored in the "bugs" table:<br>
|
||||
<br>
|
||||
mysql> show columns from bugs<br>
|
||||
<br>
|
||||
(exceedingly long output truncated here)<br>
|
||||
| bug_status| enum('UNCONFIRMED','NEW','ASSIGNED','REOPENED','RESOLVED','VERIFIED','CLOSED')||MUL | UNCONFIRMED||<br>
|
||||
<br>
|
||||
Sorry about that long line. We see from this that the "bug status" column is<br>
|
||||
an "enum field", which is a MySQL peculiarity where a string type field can<br>
|
||||
only have certain types of entries. While I think this is very cool, it's not<br>
|
||||
standard SQL. Anyway, we need to add the possible enum field entry<br>
|
||||
'APPROVED' by altering the "bugs" table.<br>
|
||||
<br>
|
||||
mysql> ALTER table bugs CHANGE bug_status bug_status<br>
|
||||
-> enum("UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED", "RESOLVED",<br>
|
||||
-> "VERIFIED", "APPROVED", "CLOSED") not null;<br>
|
||||
<br>
|
||||
(note we can take three lines or more -- whatever you put in before the<br>
|
||||
semicolon is evaluated as a single expression)<br>
|
||||
<br>
|
||||
Now if you do this:<br>
|
||||
<br>
|
||||
mysql> show columns from bugs;<br>
|
||||
<br>
|
||||
you'll see that the bug_status field has an extra "APPROVED" enum that's<br>
|
||||
available! Cool thing, too, is that this is reflected on your query page as<br>
|
||||
well -- you can query by the new status. But how's it fit into the existing<br>
|
||||
scheme of things?<br>
|
||||
Looks like you need to go back and look for instances of the word "verified"<br>
|
||||
in the perl code for Bugzilla -- wherever you find "verified", change it to<br>
|
||||
"approved" and you're in business (make sure that's a case-insensitive search).<br>
|
||||
Although you can query by the enum field, you can't give something a status<br>
|
||||
of "APPROVED" until you make the perl changes. Note that this change I<br>
|
||||
mentioned can also be done by editing checksetup.pl, which automates a lot of<br>
|
||||
this. But you need to know this stuff anyway, right?<br>
|
||||
<br>
|
||||
I hope this database tutorial has been useful for you. If you have comments<br>
|
||||
to add, questions, concerns, etc. please direct them to<br>
|
||||
mbarnson@excitehome.net. Please direct flames to /dev/null :) Have a nice<br>
|
||||
day!<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
===<br>
|
||||
LINKS<br>
|
||||
===<br>
|
||||
<br>
|
||||
Great MySQL tutorial site:<br>
|
||||
http://www.devshed.com/Server_Side/MySQL/<br>
|
||||
<br>
|
||||
</P
|
||||
></DIV
|
||||
></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="dbschema.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="granttables.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Database Schema Chart</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="database.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>MySQL Permissions & Grant Tables</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
156
mozilla/webtools/bugzilla/docs/html/dbschema.html
Normal file
156
mozilla/webtools/bugzilla/docs/html/dbschema.html
Normal file
@@ -0,0 +1,156 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Database Schema Chart</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="The Bugzilla Database"
|
||||
HREF="database.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="The Bugzilla Database"
|
||||
HREF="database.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="MySQL Bugzilla Database Introduction"
|
||||
HREF="dbdoc.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="database.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix C. The Bugzilla Database</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="dbdoc.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="dbschema">C.1. Database Schema Chart</H1
|
||||
><P
|
||||
> <DIV
|
||||
CLASS="mediaobject"
|
||||
><P
|
||||
><IMG
|
||||
SRC="../images/dbschema.jpg"><DIV
|
||||
CLASS="caption"
|
||||
><P
|
||||
>Bugzilla database relationships chart</P
|
||||
></DIV
|
||||
></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="database.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="dbdoc.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The Bugzilla Database</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="database.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>MySQL Bugzilla Database Introduction</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
184
mozilla/webtools/bugzilla/docs/html/disclaimer.html
Normal file
184
mozilla/webtools/bugzilla/docs/html/disclaimer.html
Normal file
@@ -0,0 +1,184 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Disclaimer</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Copyright Information"
|
||||
HREF="copyright.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="New Versions"
|
||||
HREF="newversions.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="copyright.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="newversions.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="disclaimer">1.3. Disclaimer</H1
|
||||
><P
|
||||
> No liability for the contents of this document can be accepted.
|
||||
Use the concepts, examples, and other content at your own risk.
|
||||
As this is a new edition of this document, there may be errors
|
||||
and inaccuracies that may damage your system. Use of this
|
||||
document may cause your girlfriend to leave you, your cats to
|
||||
pee on your furniture and clothing, your computer to cease
|
||||
functioning, your boss to fire you, and global thermonuclear
|
||||
war. Proceed with caution.
|
||||
</P
|
||||
><P
|
||||
> All copyrights are held by their respective owners, unless
|
||||
specifically noted otherwise. Use of a term in this document
|
||||
should not be regarded as affecting the validity of any
|
||||
trademark or service mark.
|
||||
</P
|
||||
><P
|
||||
> Naming of particular products or brands should not be seen as
|
||||
endorsements, with the exception of the term "GNU/Linux". We
|
||||
wholeheartedly endorse the use of GNU/Linux in every situation
|
||||
where it is appropriate. It is an extremely versatile, stable,
|
||||
and robust operating system that offers an ideal operating
|
||||
environment for Bugzilla.
|
||||
</P
|
||||
><P
|
||||
> You are strongly recommended to make a backup of your system
|
||||
before installing Bugzilla and at regular intervals thereafter.
|
||||
If you implement any suggestion in this Guide, implement this one!
|
||||
</P
|
||||
><P
|
||||
> Although the Bugzilla development team has taken great care to
|
||||
ensure that all easily-exploitable bugs or options are
|
||||
documented or fixed in the code, security holes surely exist.
|
||||
Great care should be taken both in the installation and usage of
|
||||
this software. Carefully consider the implications of installing
|
||||
other network services with Bugzilla. The Bugzilla development
|
||||
team members, Netscape Communications, America Online Inc., and
|
||||
any affiliated developers or sponsors assume no liability for
|
||||
your use of this product. You have the source code to this
|
||||
product, and are responsible for auditing it yourself to insure
|
||||
your security needs are met.
|
||||
</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="copyright.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="newversions.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Copyright Information</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>New Versions</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
237
mozilla/webtools/bugzilla/docs/html/downloadlinks.html
Normal file
237
mozilla/webtools/bugzilla/docs/html/downloadlinks.html
Normal file
@@ -0,0 +1,237 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Software Download Links</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="The Bugzilla FAQ"
|
||||
HREF="faq.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="The Bugzilla Database"
|
||||
HREF="database.html"></HEAD
|
||||
><BODY
|
||||
CLASS="appendix"
|
||||
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="faq.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="database.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="appendix"
|
||||
><H1
|
||||
><A
|
||||
NAME="downloadlinks">Appendix B. Software Download Links</H1
|
||||
><P
|
||||
> All of these sites are current as of April, 2001. Hopefully
|
||||
they'll stay current for a while.
|
||||
</P
|
||||
><P
|
||||
> Apache Web Server: <A
|
||||
HREF="http://www.apache.org/"
|
||||
TARGET="_top"
|
||||
>http://www.apache.org</A
|
||||
>
|
||||
Optional web server for Bugzilla, but recommended because of broad user base and support.
|
||||
</P
|
||||
><P
|
||||
> Bugzilla: <A
|
||||
HREF="http://www.mozilla.org/projects/bugzilla/"
|
||||
TARGET="_top"
|
||||
> http://www.mozilla.org/projects/bugzilla/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> MySQL: <A
|
||||
HREF="http://www.mysql.com/"
|
||||
TARGET="_top"
|
||||
>http://www.mysql.com/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> Perl: <A
|
||||
HREF="http://www.perl.org"
|
||||
TARGET="_top"
|
||||
>http://www.perl.org/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> CPAN: <A
|
||||
HREF="http://www.cpan.org/"
|
||||
TARGET="_top"
|
||||
>http://www.cpan.org/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> DBI Perl module:
|
||||
<A
|
||||
HREF="http://www.cpan.org/modules/by-module/DBI/"
|
||||
TARGET="_top"
|
||||
> http://www.cpan.org/modules/by-module/DBI/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> Data::Dumper module:
|
||||
<A
|
||||
HREF="http://www.cpan.org/modules/by-module/Data/"
|
||||
TARGET="_top"
|
||||
> http://www.cpan.org/modules/by-module/Data/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> MySQL related Perl modules:
|
||||
<A
|
||||
HREF="http://www.cpan.org/modules/by-module/Mysql/"
|
||||
TARGET="_top"
|
||||
> http://www.cpan.org/modules/by-module/Mysql/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> TimeDate Perl module collection:
|
||||
<A
|
||||
HREF="http://www.cpan.org/modules/by-module/Date/"
|
||||
TARGET="_top"
|
||||
> http://www.cpan.org/modules/by-module/Date/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> GD Perl module:
|
||||
<A
|
||||
HREF="http://www.cpan.org/modules/by-module/GD/"
|
||||
TARGET="_top"
|
||||
> http://www.cpan.org/modules/by-module/GD/</A
|
||||
>
|
||||
Alternately, you should be able to find the latest version of
|
||||
GD at <A
|
||||
HREF="http://www.boutell.com/gd/"
|
||||
TARGET="_top"
|
||||
>http://www.boutell.com/gd/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> Chart::Base module:
|
||||
<A
|
||||
HREF="http://www.cpan.org/modules/by-module/Chart/"
|
||||
TARGET="_top"
|
||||
> http://www.cpan.org/modules/by-module/Chart/</A
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> LinuxDoc Software:
|
||||
<A
|
||||
HREF="http://www.linuxdoc.org/"
|
||||
TARGET="_top"
|
||||
>http://www.linuxdoc.org/</A
|
||||
>
|
||||
(for documentation maintenance)
|
||||
</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="faq.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="database.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>The Bugzilla FAQ</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
> </TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>The Bugzilla Database</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
279
mozilla/webtools/bugzilla/docs/html/errata.html
Normal file
279
mozilla/webtools/bugzilla/docs/html/errata.html
Normal file
@@ -0,0 +1,279 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>ERRATA</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="Installation"
|
||||
HREF="installation.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Step-by-step Install"
|
||||
HREF="stepbystep.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="installation.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 3. Installation</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="stepbystep.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="errata">3.1. ERRATA</H1
|
||||
><P
|
||||
>Here are some miscellaneous notes about possible issues you
|
||||
main run into when you begin your Bugzilla installation.
|
||||
Reference platforms for Bugzilla installation are Redhat Linux
|
||||
7.2, Linux-Mandrake 8.0, and Solaris 8.</P
|
||||
><P
|
||||
></P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
><TBODY
|
||||
><TR
|
||||
><TD
|
||||
> If you are installing Bugzilla on S.u.S.e. 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: <SPAN
|
||||
CLASS="errorname"
|
||||
>cannot chdir(/var/spool/mqueue):
|
||||
Permission denied</SPAN
|
||||
> 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.
|
||||
</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
> Bugzilla may be installed on Macintosh OS X (10), which is a
|
||||
unix-based (BSD) operating system. Everything required for
|
||||
Bugzilla on OS X will install cleanly, but the optional GD
|
||||
perl module which is used for bug charting requires some
|
||||
additional setup for installation. Please see the Mac OS X
|
||||
installation section below for details
|
||||
</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
> Release Notes for Bugzilla 2.16 are available at
|
||||
<TT
|
||||
CLASS="filename"
|
||||
>docs/rel_notes.txt</TT
|
||||
> in your Bugzilla
|
||||
source distribution.
|
||||
</TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
> The preferred documentation for Bugzilla is available in
|
||||
docs/, with a variety of document types available. Please
|
||||
refer to these documents when installing, configuring, and
|
||||
maintaining your Bugzilla installation.
|
||||
</TD
|
||||
></TR
|
||||
></TBODY
|
||||
></TABLE
|
||||
><P
|
||||
></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
|
||||
> Bugzilla is not a package where you can just plop it in a directory,
|
||||
twiddle a few things, and you're off. Installing Bugzilla assumes you
|
||||
know your variant of UNIX or Microsoft Windows well, are familiar with the
|
||||
command line, and are comfortable compiling and installing a plethora
|
||||
of third-party utilities. To install Bugzilla on Win32 requires
|
||||
fair Perl proficiency, and if you use a webserver other than Apache you
|
||||
should be intimately familiar with the security mechanisms and CGI
|
||||
environment thereof.
|
||||
</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
><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
|
||||
> Bugzilla has not undergone a complete security review. Security holes
|
||||
may exist in the code. Great care should be taken both in the installation
|
||||
and usage of this software. Carefully consider the implications of
|
||||
installing other network services with Bugzilla.
|
||||
</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></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="installation.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="stepbystep.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Installation</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="installation.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Step-by-step Install</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
3736
mozilla/webtools/bugzilla/docs/html/faq.html
Normal file
3736
mozilla/webtools/bugzilla/docs/html/faq.html
Normal file
File diff suppressed because it is too large
Load Diff
161
mozilla/webtools/bugzilla/docs/html/feedback.html
Normal file
161
mozilla/webtools/bugzilla/docs/html/feedback.html
Normal file
@@ -0,0 +1,161 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Feedback</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="About This Guide"
|
||||
HREF="about.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="Contributors"
|
||||
HREF="contributors.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="Translations"
|
||||
HREF="translations.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="contributors.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 1. About This Guide</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="translations.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H1
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="feedback">1.7. Feedback</H1
|
||||
><P
|
||||
> I welcome feedback on this document. Without your submissions
|
||||
and input, this Guide cannot continue to exist. Please mail
|
||||
additions, comments, criticisms, etc. to
|
||||
<TT
|
||||
CLASS="email"
|
||||
><<A
|
||||
HREF="mailto:barnboy@trilobyte.net"
|
||||
>barnboy@trilobyte.net</A
|
||||
>></TT
|
||||
>. Please send flames to
|
||||
<TT
|
||||
CLASS="email"
|
||||
><<A
|
||||
HREF="mailto:devnull@localhost"
|
||||
>devnull@localhost</A
|
||||
>></TT
|
||||
>
|
||||
</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="contributors.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="translations.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>Contributors</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="about.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>Translations</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
489
mozilla/webtools/bugzilla/docs/html/geninstall.html
Normal file
489
mozilla/webtools/bugzilla/docs/html/geninstall.html
Normal file
@@ -0,0 +1,489 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>Installation General Notes</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="BSD Installation Notes"
|
||||
HREF="bsdinstall.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="bsdinstall.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Chapter 3. 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="geninstall">3.5. Installation General Notes</H1
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="AEN874">3.5.1. Modifying Your Running System</H2
|
||||
><P
|
||||
> Bugzilla optimizes database lookups by storing all relatively static
|
||||
information in the versioncache file, located in the data/ subdirectory
|
||||
under your installation directory.
|
||||
</P
|
||||
><P
|
||||
> If you make a change to the structural data in your database
|
||||
(the versions table for example), or to the
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"constants"</SPAN
|
||||
> encoded in defparams.pl, you will
|
||||
need to remove the cached content from the data directory
|
||||
(by doing a <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"rm data/versioncache"</SPAN
|
||||
>), or your
|
||||
changes won't show up.
|
||||
</P
|
||||
><P
|
||||
> That file gets automatically regenerated whenever it's more than an
|
||||
hour old, so Bugzilla will eventually notice your changes by itself, but
|
||||
generally you want it to notice right away, so that you can test things.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="AEN881">3.5.2. Upgrading From Previous Versions</H2
|
||||
><P
|
||||
> A plain Bugzilla is fairly easy to upgrade from one version to a newer one.
|
||||
However, things get a bit more complicated if you've made changes to
|
||||
Bugzilla's code. In this case, you may have to re-make or reapply those
|
||||
changes.
|
||||
It is recommended that you take a backup of your database and your entire
|
||||
Bugzilla installation before attempting an upgrade. You can upgrade a 'clean'
|
||||
installation by untarring a new tarball over the old installation. If you
|
||||
are upgrading from 2.12 or later, you can type <TT
|
||||
CLASS="filename"
|
||||
>cvs -z3
|
||||
update</TT
|
||||
>, and resolve conflicts if there are any.
|
||||
</P
|
||||
><P
|
||||
> Because the developers of Bugzilla are constantly adding new tables, columns
|
||||
and fields, you'll probably get SQL errors if you just update the code and
|
||||
attempt to use Bugzilla. Always run the checksetup.pl script whenever
|
||||
you upgrade your installation.
|
||||
</P
|
||||
><P
|
||||
> If you are running Bugzilla version 2.8 or lower, and wish to upgrade to
|
||||
the latest version, please consult the file, "UPGRADING-pre-2.8" in the
|
||||
Bugzilla root directory after untarring the archive.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="htaccess">3.5.3. <TT
|
||||
CLASS="filename"
|
||||
>.htaccess</TT
|
||||
> files and security</H2
|
||||
><P
|
||||
> To enhance the security of your Bugzilla installation,
|
||||
Bugzilla will generate
|
||||
<I
|
||||
CLASS="glossterm"
|
||||
><TT
|
||||
CLASS="filename"
|
||||
>.htaccess</TT
|
||||
></I
|
||||
> files
|
||||
which the Apache webserver can use to restrict access to
|
||||
the bugzilla data files. The checksetup script will
|
||||
generate the <TT
|
||||
CLASS="filename"
|
||||
>.htaccess</TT
|
||||
> files. These .htaccess files
|
||||
will not work with Apache 1.2.x - but this has security holes, so you
|
||||
shouldn't be using it anyway.
|
||||
|
||||
<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
|
||||
> If you are using an alternate provider of
|
||||
<SPAN
|
||||
CLASS="productname"
|
||||
>webdot</SPAN
|
||||
> services for graphing
|
||||
(as described when viewing
|
||||
<TT
|
||||
CLASS="filename"
|
||||
>editparams.cgi</TT
|
||||
> in your web
|
||||
browser), you will need to change the ip address in
|
||||
<TT
|
||||
CLASS="filename"
|
||||
>data/webdot/.htaccess</TT
|
||||
> to the ip
|
||||
address of the webdot server that you are using.
|
||||
</P
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
>
|
||||
|
||||
</P
|
||||
><P
|
||||
> The default .htaccess file may not provide adequate access
|
||||
restrictions, depending on your web server configuration.
|
||||
Be sure to check the <Directory> entries for your
|
||||
Bugzilla directory so that the <TT
|
||||
CLASS="filename"
|
||||
>.htaccess</TT
|
||||
>
|
||||
file is allowed to override web server defaults. For instance,
|
||||
let's assume your installation of Bugzilla is installed to
|
||||
<TT
|
||||
CLASS="filename"
|
||||
>/usr/local/bugzilla</TT
|
||||
>. You should have
|
||||
this <Directory> entry in your <TT
|
||||
CLASS="filename"
|
||||
>httpd.conf</TT
|
||||
>
|
||||
file:
|
||||
</P
|
||||
><P
|
||||
> <TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
>
|
||||
<Directory /usr/local/bugzilla/>
|
||||
Options +FollowSymLinks +Indexes +Includes +ExecCGI
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
>
|
||||
</P
|
||||
><P
|
||||
> The important part above is <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"AllowOverride All"</SPAN
|
||||
>.
|
||||
Without that, the <TT
|
||||
CLASS="filename"
|
||||
>.htaccess</TT
|
||||
> file created by
|
||||
<TT
|
||||
CLASS="filename"
|
||||
>checksetup.pl</TT
|
||||
> will not have sufficient
|
||||
permissions to protect your Bugzilla installation.
|
||||
</P
|
||||
><P
|
||||
> If you are using Internet Information Server or other web
|
||||
server which does not observe <TT
|
||||
CLASS="filename"
|
||||
>.htaccess</TT
|
||||
>
|
||||
conventions, you can disable their creation by editing
|
||||
<TT
|
||||
CLASS="filename"
|
||||
>localconfig</TT
|
||||
> and setting the
|
||||
<TT
|
||||
CLASS="varname"
|
||||
>$create_htaccess</TT
|
||||
> variable to
|
||||
<TT
|
||||
CLASS="parameter"
|
||||
><I
|
||||
>0</I
|
||||
></TT
|
||||
>.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="mod-throttle">3.5.4. <TT
|
||||
CLASS="filename"
|
||||
>mod_throttle</TT
|
||||
> and Security</H2
|
||||
><P
|
||||
> It is possible for a user, by mistake or on purpose, to access
|
||||
the database many times in a row which can result in very slow
|
||||
access speeds for other users. If your Bugzilla installation
|
||||
is experiencing this problem , you may install the Apache
|
||||
module <TT
|
||||
CLASS="filename"
|
||||
>mod_throttle</TT
|
||||
> which can limit
|
||||
connections by ip-address. You may download this module at
|
||||
<A
|
||||
HREF="http://www.snert.com/Software/Throttle/"
|
||||
TARGET="_top"
|
||||
>http://www.snert.com/Software/Throttle/</A
|
||||
>. Follow the instructions to install into your Apache install. <EM
|
||||
>This module only functions with the Apache web server!</EM
|
||||
>. You may use the <B
|
||||
CLASS="command"
|
||||
>ThrottleClientIP</B
|
||||
> command provided by this module to accomplish this goal. See the <A
|
||||
HREF="http://www.snert.com/Software/Throttle/"
|
||||
TARGET="_top"
|
||||
>Module Instructions</A
|
||||
> for more information. </P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="content-type">3.5.5. Preventing untrusted Bugzilla content from executing malicious Javascript code</H2
|
||||
><P
|
||||
>It is possible for a Bugzilla to execute malicious
|
||||
Javascript code. Due to internationalization concerns, we are
|
||||
unable to incorporate the code changes necessary to fulfill
|
||||
the CERT advisory requirements mentioned in <A
|
||||
HREF="http://www.cet.org/tech_tips/malicious_code_mitigation.html/#3"
|
||||
TARGET="_top"
|
||||
>http://www.cet.org/tech_tips/malicious_code_mitigation.html/#3</A
|
||||
>. Executing the following code snippet from a UNIX command shell will rectify the problem if your Bugzilla installation is intended for an English-speaking audience. As always, be sure your Bugzilla installation has a good backup before making changes, and I recommend you understand what the script is doing before executing it. </P
|
||||
><P
|
||||
><TABLE
|
||||
BORDER="0"
|
||||
BGCOLOR="#E0E0E0"
|
||||
WIDTH="100%"
|
||||
><TR
|
||||
><TD
|
||||
><FONT
|
||||
COLOR="#000000"
|
||||
><PRE
|
||||
CLASS="programlisting"
|
||||
> bash# cd $BUGZILLA_HOME; for i in `ls *.cgi`; \
|
||||
do cat $i | sed 's/Content-type\: text\/html/Content-Type: text\/html\; charset=ISO-8859-1/' >$i.tmp; \
|
||||
mv $i.tmp $i; done
|
||||
</PRE
|
||||
></FONT
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
></P
|
||||
><P
|
||||
> All this one-liner command does is search for all instances of
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Content-type: text/html"</SPAN
|
||||
> and replaces it with
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"Content-Type: text/html; charset=ISO-8859-1"</SPAN
|
||||
>.
|
||||
This specification prevents possible Javascript attacks on the
|
||||
browser, and is suggested for all English-speaking sites. For
|
||||
non-english-speaking Bugzilla sites, I suggest changing
|
||||
<SPAN
|
||||
CLASS="QUOTE"
|
||||
>"ISO-8859-1"</SPAN
|
||||
>, above, to <SPAN
|
||||
CLASS="QUOTE"
|
||||
>"UTF-8"</SPAN
|
||||
>.
|
||||
</P
|
||||
></DIV
|
||||
><DIV
|
||||
CLASS="section"
|
||||
><H2
|
||||
CLASS="section"
|
||||
><A
|
||||
NAME="unixhistory">3.5.6. UNIX Installation Instructions History</H2
|
||||
><P
|
||||
> This document was originally adapted from the Bonsai
|
||||
installation instructions by Terry Weissman
|
||||
<terry@mozilla.org>.
|
||||
</P
|
||||
><P
|
||||
> The February 25, 1999 re-write of this page was done by Ry4an
|
||||
Brase <ry4an@ry4an.org>, with some edits by Terry
|
||||
Weissman, Bryce Nesbitt, Martin Pool, & Dan Mosedale (But
|
||||
don't send bug reports to them; report them using bugzilla, at <A
|
||||
HREF="http://bugzilla.mozilla.org/enter_bug.cgi?product=Bugzilla"
|
||||
TARGET="_top"
|
||||
>http://bugzilla.mozilla.org/enter_bug.cgi?product=Bugzilla</A
|
||||
> ).
|
||||
</P
|
||||
><P
|
||||
> This document was heavily modified again Wednesday, March 07
|
||||
2001 to reflect changes for Bugzilla 2.12 release by Matthew
|
||||
P. Barnson. The securing MySQL section should be changed to
|
||||
become standard procedure for Bugzilla installations.
|
||||
</P
|
||||
><P
|
||||
> Finally, the README in its entirety was marked up in SGML and
|
||||
included into the Guide on April 24, 2001 by Matt Barnson.
|
||||
Since that time, it's undergone extensive modification as
|
||||
Bugzilla grew.
|
||||
</P
|
||||
><P
|
||||
> Comments from people using this Guide for the first time are
|
||||
particularly welcome.
|
||||
</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="bsdinstall.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"
|
||||
>BSD 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"
|
||||
>Win32 Installation Notes</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
165
mozilla/webtools/bugzilla/docs/html/gfdl-0.html
Normal file
165
mozilla/webtools/bugzilla/docs/html/gfdl-0.html
Normal file
@@ -0,0 +1,165 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>PREAMBLE</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="GNU Free Documentation License"
|
||||
HREF="gfdl.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="GNU Free Documentation License"
|
||||
HREF="gfdl.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="APPLICABILITY AND DEFINITIONS"
|
||||
HREF="gfdl-1.html"></HEAD
|
||||
><BODY
|
||||
CLASS="sect1"
|
||||
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="gfdl.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix E. GNU Free Documentation License</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="gfdl-1.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="sect1"
|
||||
><H1
|
||||
CLASS="sect1"
|
||||
><A
|
||||
NAME="gfdl-0">0. PREAMBLE</H1
|
||||
><P
|
||||
>The purpose of this License is to make a manual, textbook,
|
||||
or other written document "free" in the sense of freedom: to
|
||||
assure everyone the effective freedom to copy and redistribute it,
|
||||
with or without modifying it, either commercially or
|
||||
noncommercially. Secondarily, this License preserves for the
|
||||
author and publisher a way to get credit for their work, while not
|
||||
being considered responsible for modifications made by
|
||||
others.</P
|
||||
><P
|
||||
>This License is a kind of "copyleft", which means that
|
||||
derivative works of the document must themselves be free in the
|
||||
same sense. It complements the GNU General Public License, which
|
||||
is a copyleft license designed for free software.</P
|
||||
><P
|
||||
>We have designed this License in order to use it for manuals
|
||||
for free software, because free software needs free documentation:
|
||||
a free program should come with manuals providing the same
|
||||
freedoms that the software does. But this License is not limited
|
||||
to software manuals; it can be used for any textual work,
|
||||
regardless of subject matter or whether it is published as a
|
||||
printed book. We recommend this License principally for works
|
||||
whose purpose is instruction or reference.</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="gfdl.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="gfdl-1.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>GNU Free Documentation License</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="gfdl.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>APPLICABILITY AND DEFINITIONS</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
206
mozilla/webtools/bugzilla/docs/html/gfdl-1.html
Normal file
206
mozilla/webtools/bugzilla/docs/html/gfdl-1.html
Normal file
@@ -0,0 +1,206 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>APPLICABILITY AND DEFINITIONS</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="GNU Free Documentation License"
|
||||
HREF="gfdl.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="PREAMBLE"
|
||||
HREF="gfdl-0.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="VERBATIM COPYING"
|
||||
HREF="gfdl-2.html"></HEAD
|
||||
><BODY
|
||||
CLASS="sect1"
|
||||
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="gfdl-0.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix E. GNU Free Documentation License</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="gfdl-2.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="sect1"
|
||||
><H1
|
||||
CLASS="sect1"
|
||||
><A
|
||||
NAME="gfdl-1">1. APPLICABILITY AND DEFINITIONS</H1
|
||||
><P
|
||||
>This License applies to any manual or other work that
|
||||
contains a notice placed by the copyright holder saying it can be
|
||||
distributed under the terms of this License. The "Document",
|
||||
below, refers to any such manual or work. Any member of the
|
||||
public is a licensee, and is addressed as "you".</P
|
||||
><P
|
||||
>A "Modified Version" of the Document means any work
|
||||
containing the Document or a portion of it, either copied
|
||||
verbatim, or with modifications and/or translated into another
|
||||
language.</P
|
||||
><P
|
||||
>A "Secondary Section" is a named appendix or a front-matter
|
||||
section of the Document that deals exclusively with the
|
||||
relationship of the publishers or authors of the Document to the
|
||||
Document's overall subject (or to related matters) and contains
|
||||
nothing that could fall directly within that overall subject.
|
||||
(For example, if the Document is in part a textbook of
|
||||
mathematics, a Secondary Section may not explain any mathematics.)
|
||||
The relationship could be a matter of historical connection with
|
||||
the subject or with related matters, or of legal, commercial,
|
||||
philosophical, ethical or political position regarding
|
||||
them.</P
|
||||
><P
|
||||
>The "Invariant Sections" are certain Secondary Sections
|
||||
whose titles are designated, as being those of Invariant Sections,
|
||||
in the notice that says that the Document is released under this
|
||||
License.</P
|
||||
><P
|
||||
>The "Cover Texts" are certain short passages of text that
|
||||
are listed, as Front-Cover Texts or Back-Cover Texts, in the
|
||||
notice that says that the Document is released under this
|
||||
License.</P
|
||||
><P
|
||||
>A "Transparent" copy of the Document means a
|
||||
machine-readable copy, represented in a format whose specification
|
||||
is available to the general public, whose contents can be viewed
|
||||
and edited directly and straightforwardly with generic text
|
||||
editors or (for images composed of pixels) generic paint programs
|
||||
or (for drawings) some widely available drawing editor, and that
|
||||
is suitable for input to text formatters or for automatic
|
||||
translation to a variety of formats suitable for input to text
|
||||
formatters. A copy made in an otherwise Transparent file format
|
||||
whose markup has been designed to thwart or discourage subsequent
|
||||
modification by readers is not Transparent. A copy that is not
|
||||
"Transparent" is called "Opaque".</P
|
||||
><P
|
||||
>Examples of suitable formats for Transparent copies include
|
||||
plain ASCII without markup, Texinfo input format, LaTeX input
|
||||
format, SGML or XML using a publicly available DTD, and
|
||||
standard-conforming simple HTML designed for human modification.
|
||||
Opaque formats include PostScript, PDF, proprietary formats that
|
||||
can be read and edited only by proprietary word processors, SGML
|
||||
or XML for which the DTD and/or processing tools are not generally
|
||||
available, and the machine-generated HTML produced by some word
|
||||
processors for output purposes only.</P
|
||||
><P
|
||||
>The "Title Page" means, for a printed book, the title page
|
||||
itself, plus such following pages as are needed to hold, legibly,
|
||||
the material this License requires to appear in the title page.
|
||||
For works in formats which do not have any title page as such,
|
||||
"Title Page" means the text near the most prominent appearance of
|
||||
the work's title, preceding the beginning of the body of the
|
||||
text.</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="gfdl-0.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="gfdl-2.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>PREAMBLE</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="gfdl.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>VERBATIM COPYING</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
162
mozilla/webtools/bugzilla/docs/html/gfdl-10.html
Normal file
162
mozilla/webtools/bugzilla/docs/html/gfdl-10.html
Normal file
@@ -0,0 +1,162 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>FUTURE REVISIONS OF THIS LICENSE</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="GNU Free Documentation License"
|
||||
HREF="gfdl.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="TERMINATION"
|
||||
HREF="gfdl-9.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="How to use this License for your documents"
|
||||
HREF="gfdl-howto.html"></HEAD
|
||||
><BODY
|
||||
CLASS="sect1"
|
||||
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="gfdl-9.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix E. GNU Free Documentation License</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="gfdl-howto.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="sect1"
|
||||
><H1
|
||||
CLASS="sect1"
|
||||
><A
|
||||
NAME="gfdl-10">10. FUTURE REVISIONS OF THIS LICENSE</H1
|
||||
><P
|
||||
>The Free Software Foundation may publish new, revised
|
||||
versions of the GNU Free Documentation License from time to time.
|
||||
Such new versions will be similar in spirit to the present
|
||||
version, but may differ in detail to address new problems or
|
||||
concerns. See <A
|
||||
HREF="http://www.gnu.org/copyleft/"
|
||||
TARGET="_top"
|
||||
>http://www.gnu.org/copyleft/</A
|
||||
>.</P
|
||||
><P
|
||||
>Each version of the License is given a distinguishing
|
||||
version number. If the Document specifies that a particular
|
||||
numbered version of this License "or any later version" applies to
|
||||
it, you have the option of following the terms and conditions
|
||||
either of that specified version or of any later version that has
|
||||
been published (not as a draft) by the Free Software Foundation.
|
||||
If the Document does not specify a version number of this License,
|
||||
you may choose any version ever published (not as a draft) by the
|
||||
Free Software Foundation.</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="gfdl-9.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="gfdl-howto.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>TERMINATION</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="gfdl.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>How to use this License for your documents</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
156
mozilla/webtools/bugzilla/docs/html/gfdl-2.html
Normal file
156
mozilla/webtools/bugzilla/docs/html/gfdl-2.html
Normal file
@@ -0,0 +1,156 @@
|
||||
<HTML
|
||||
><HEAD
|
||||
><TITLE
|
||||
>VERBATIM COPYING</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="GNU Free Documentation License"
|
||||
HREF="gfdl.html"><LINK
|
||||
REL="PREVIOUS"
|
||||
TITLE="APPLICABILITY AND DEFINITIONS"
|
||||
HREF="gfdl-1.html"><LINK
|
||||
REL="NEXT"
|
||||
TITLE="COPYING IN QUANTITY"
|
||||
HREF="gfdl-3.html"></HEAD
|
||||
><BODY
|
||||
CLASS="sect1"
|
||||
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="gfdl-1.html"
|
||||
ACCESSKEY="P"
|
||||
>Prev</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="80%"
|
||||
ALIGN="center"
|
||||
VALIGN="bottom"
|
||||
>Appendix E. GNU Free Documentation License</TD
|
||||
><TD
|
||||
WIDTH="10%"
|
||||
ALIGN="right"
|
||||
VALIGN="bottom"
|
||||
><A
|
||||
HREF="gfdl-3.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
></TABLE
|
||||
><HR
|
||||
ALIGN="LEFT"
|
||||
WIDTH="100%"></DIV
|
||||
><DIV
|
||||
CLASS="sect1"
|
||||
><H1
|
||||
CLASS="sect1"
|
||||
><A
|
||||
NAME="gfdl-2">2. VERBATIM COPYING</H1
|
||||
><P
|
||||
>You may copy and distribute the Document in any medium,
|
||||
either commercially or noncommercially, provided that this
|
||||
License, the copyright notices, and the license notice saying this
|
||||
License applies to the Document are reproduced in all copies, and
|
||||
that you add no other conditions whatsoever to those of this
|
||||
License. You may not use technical measures to obstruct or
|
||||
control the reading or further copying of the copies you make or
|
||||
distribute. However, you may accept compensation in exchange for
|
||||
copies. If you distribute a large enough number of copies you
|
||||
must also follow the conditions in section 3.</P
|
||||
><P
|
||||
>You may also lend copies, under the same conditions stated
|
||||
above, and you may publicly display copies.</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="gfdl-1.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="gfdl-3.html"
|
||||
ACCESSKEY="N"
|
||||
>Next</A
|
||||
></TD
|
||||
></TR
|
||||
><TR
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="left"
|
||||
VALIGN="top"
|
||||
>APPLICABILITY AND DEFINITIONS</TD
|
||||
><TD
|
||||
WIDTH="34%"
|
||||
ALIGN="center"
|
||||
VALIGN="top"
|
||||
><A
|
||||
HREF="gfdl.html"
|
||||
ACCESSKEY="U"
|
||||
>Up</A
|
||||
></TD
|
||||
><TD
|
||||
WIDTH="33%"
|
||||
ALIGN="right"
|
||||
VALIGN="top"
|
||||
>COPYING IN QUANTITY</TD
|
||||
></TR
|
||||
></TABLE
|
||||
></DIV
|
||||
></BODY
|
||||
></HTML
|
||||
>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user