зеркало из https://github.com/mozilla/pjs.git
1068 строки
31 KiB
Perl
Executable File
1068 строки
31 KiB
Perl
Executable File
#!/usr/bonsaitools/bin/perl -w
|
|
# -*- 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>
|
|
# David Gardiner <david.gardiner@unisa.edu.au>
|
|
# Matthias Radestock <matthias@sorted.org>
|
|
# Chris Lahey <clahey@ximian.com> [javascript fixes]
|
|
# Christian Reis <kiko@async.com.br> [javascript rewrite]
|
|
|
|
use diagnostics;
|
|
use strict;
|
|
|
|
require "CGI.pl";
|
|
|
|
$::CheckOptionValues = 0; # It's OK if we have some bogus things in the
|
|
# pop-up lists here, from a remembered query
|
|
# that is no longer quite valid. We don't
|
|
# want to crap out in the query page.
|
|
|
|
# Shut up misguided -w warnings about "used only once":
|
|
|
|
use vars
|
|
@::CheckOptionValues,
|
|
@::legal_resolution,
|
|
@::legal_bug_status,
|
|
@::legal_components,
|
|
@::legal_keywords,
|
|
@::legal_opsys,
|
|
@::legal_platform,
|
|
@::legal_priority,
|
|
@::legal_product,
|
|
@::legal_severity,
|
|
@::legal_target_milestone,
|
|
@::legal_versions,
|
|
@::log_columns,
|
|
%::versions,
|
|
%::components,
|
|
%::FORM;
|
|
|
|
|
|
if (defined $::FORM{"GoAheadAndLogIn"}) {
|
|
# We got here from a login page, probably from relogin.cgi. We better
|
|
# make sure the password is legit.
|
|
confirm_login();
|
|
} else {
|
|
quietly_check_login();
|
|
}
|
|
my $userid = 0;
|
|
if (defined $::COOKIE{"Bugzilla_login"}) {
|
|
$userid = DBNameToIdAndCheck($::COOKIE{"Bugzilla_login"});
|
|
}
|
|
|
|
# Backwards compatability hack -- if there are any of the old QUERY_*
|
|
# cookies around, and we are logged in, then move them into the database
|
|
# and nuke the cookie.
|
|
if ($userid) {
|
|
my @oldquerycookies;
|
|
foreach my $i (keys %::COOKIE) {
|
|
if ($i =~ /^QUERY_(.*)$/) {
|
|
push(@oldquerycookies, [$1, $i, $::COOKIE{$i}]);
|
|
}
|
|
}
|
|
if (defined $::COOKIE{'DEFAULTQUERY'}) {
|
|
push(@oldquerycookies, [$::defaultqueryname, 'DEFAULTQUERY',
|
|
$::COOKIE{'DEFAULTQUERY'}]);
|
|
}
|
|
if (@oldquerycookies) {
|
|
foreach my $ref (@oldquerycookies) {
|
|
my ($name, $cookiename, $value) = (@$ref);
|
|
if ($value) {
|
|
my $qname = SqlQuote($name);
|
|
SendSQL("SELECT query FROM namedqueries " .
|
|
"WHERE userid = $userid AND name = $qname");
|
|
my $query = FetchOneColumn();
|
|
if (!$query) {
|
|
SendSQL("REPLACE INTO namedqueries " .
|
|
"(userid, name, query) VALUES " .
|
|
"($userid, $qname, " . SqlQuote($value) . ")");
|
|
}
|
|
}
|
|
print "Set-Cookie: $cookiename= ; path=/ ; expires=Sun, 30-Jun-1980 00:00:00 GMT\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($::FORM{'nukedefaultquery'}) {
|
|
if ($userid) {
|
|
SendSQL("DELETE FROM namedqueries " .
|
|
"WHERE userid = $userid AND name = '$::defaultqueryname'");
|
|
}
|
|
$::buffer = "";
|
|
}
|
|
|
|
|
|
my $userdefaultquery;
|
|
if ($userid) {
|
|
SendSQL("SELECT query FROM namedqueries " .
|
|
"WHERE userid = $userid AND name = '$::defaultqueryname'");
|
|
$userdefaultquery = FetchOneColumn();
|
|
}
|
|
|
|
my %default;
|
|
my %type;
|
|
|
|
sub ProcessFormStuff {
|
|
my ($buf) = (@_);
|
|
my $foundone = 0;
|
|
foreach my $name ("bug_status", "resolution", "assigned_to",
|
|
"rep_platform", "priority", "bug_severity",
|
|
"product", "reporter", "op_sys",
|
|
"component", "version", "chfield", "chfieldfrom",
|
|
"chfieldto", "chfieldvalue", "target_milestone",
|
|
"email1", "emailtype1", "emailreporter1",
|
|
"emailassigned_to1", "emailcc1", "emailqa_contact1",
|
|
"emaillongdesc1",
|
|
"email2", "emailtype2", "emailreporter2",
|
|
"emailassigned_to2", "emailcc2", "emailqa_contact2",
|
|
"emaillongdesc2",
|
|
"changedin", "votes", "short_desc", "short_desc_type",
|
|
"long_desc", "long_desc_type", "bug_file_loc",
|
|
"bug_file_loc_type", "status_whiteboard",
|
|
"status_whiteboard_type", "bug_id",
|
|
"bugidtype", "keywords", "keywords_type") {
|
|
$default{$name} = "";
|
|
$type{$name} = 0;
|
|
}
|
|
|
|
|
|
foreach my $item (split(/\&/, $buf)) {
|
|
my @el = split(/=/, $item);
|
|
my $name = $el[0];
|
|
my $value;
|
|
if ($#el > 0) {
|
|
$value = url_decode($el[1]);
|
|
} else {
|
|
$value = "";
|
|
}
|
|
if (defined $default{$name}) {
|
|
$foundone = 1;
|
|
if ($default{$name} ne "") {
|
|
$default{$name} .= "|$value";
|
|
$type{$name} = 1;
|
|
} else {
|
|
$default{$name} = $value;
|
|
}
|
|
}
|
|
}
|
|
return $foundone;
|
|
}
|
|
|
|
|
|
if (!ProcessFormStuff($::buffer)) {
|
|
# Ah-hah, there was no form stuff specified. Do it again with the
|
|
# default query.
|
|
if ($userdefaultquery) {
|
|
ProcessFormStuff($userdefaultquery);
|
|
} else {
|
|
ProcessFormStuff(Param("defaultquery"));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($default{'chfieldto'} eq "") {
|
|
$default{'chfieldto'} = "Now";
|
|
}
|
|
|
|
|
|
|
|
print "Set-Cookie: BUGLIST=
|
|
Content-type: text/html\n\n";
|
|
|
|
GetVersionTable();
|
|
|
|
sub GenerateEmailInput {
|
|
my ($id) = (@_);
|
|
my $defstr = value_quote($default{"email$id"});
|
|
my $deftype = $default{"emailtype$id"};
|
|
if ($deftype eq "") {
|
|
$deftype = "substring";
|
|
}
|
|
my $assignedto = ($default{"emailassigned_to$id"} eq "1") ? "checked" : "";
|
|
my $reporter = ($default{"emailreporter$id"} eq "1") ? "checked" : "";
|
|
my $cc = ($default{"emailcc$id"} eq "1") ? "checked" : "";
|
|
my $longdesc = ($default{"emaillongdesc$id"} eq "1") ? "checked" : "";
|
|
|
|
my $qapart = "";
|
|
my $qacontact = "";
|
|
if (Param("useqacontact")) {
|
|
$qacontact = ($default{"emailqa_contact$id"} eq "1") ? "checked" : "";
|
|
$qapart = qq|
|
|
<tr>
|
|
<td></td>
|
|
<td>
|
|
<input type="checkbox" name="emailqa_contact$id" value=1 $qacontact>QA Contact
|
|
</td>
|
|
</tr>
|
|
|;
|
|
}
|
|
if ($assignedto eq "" && $reporter eq "" && $cc eq "" &&
|
|
$qacontact eq "") {
|
|
if ($id eq "1") {
|
|
$assignedto = "checked";
|
|
} else {
|
|
$reporter = "checked";
|
|
}
|
|
}
|
|
|
|
|
|
$default{"emailtype$id"} ||= "substring";
|
|
|
|
return qq{
|
|
<table border=1 cellspacing=0 cellpadding=0>
|
|
<tr><td>
|
|
<table cellspacing=0 cellpadding=0>
|
|
<tr>
|
|
<td rowspan=2 valign=top><a href="queryhelp.cgi#peopleinvolved">Email:</a>
|
|
<input name="email$id" size="30" value="$defstr"> matching as
|
|
} . BuildPulldown("emailtype$id",
|
|
[["regexp", "regexp"],
|
|
["notregexp", "not regexp"],
|
|
["substring", "substring"],
|
|
["exact", "exact"]],
|
|
$default{"emailtype$id"}) . qq{
|
|
</td>
|
|
<td>
|
|
<input type="checkbox" name="emailassigned_to$id" value=1 $assignedto>Assigned To
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<input type="checkbox" name="emailreporter$id" value=1 $reporter>Reporter
|
|
</td>
|
|
</tr>$qapart
|
|
<tr>
|
|
<td align=right>(Will match any of the selected fields)</td>
|
|
<td>
|
|
<input type="checkbox" name="emailcc$id" value=1 $cc>CC
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td></td>
|
|
<td>
|
|
<input type="checkbox" name="emaillongdesc$id" value=1 $longdesc>Added comment
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</table>
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
my $emailinput1 = GenerateEmailInput(1);
|
|
my $emailinput2 = GenerateEmailInput(2);
|
|
|
|
# if using usebuggroups, then we don't want people to see products they don't
|
|
# have access to. remove them from the list.
|
|
|
|
@::product_list = ();
|
|
my %component_set;
|
|
my %version_set;
|
|
my %milestone_set;
|
|
foreach my $p (@::legal_product) {
|
|
if(Param("usebuggroups")
|
|
&& GroupExists($p)
|
|
&& !UserInGroup($p)) {
|
|
# If we're using bug groups to restrict entry on products, and
|
|
# this product has a bug group, and the user is not in that
|
|
# group, we don't want to include that product in this list.
|
|
next;
|
|
}
|
|
push @::product_list, $p;
|
|
if ($::components{$p}) {
|
|
foreach my $c (@{$::components{$p}}) {
|
|
$component_set{$c} = 1;
|
|
}
|
|
}
|
|
foreach my $v (@{$::versions{$p}}) {
|
|
$version_set{$v} = 1;
|
|
}
|
|
foreach my $m (@{$::target_milestone{$p}}) {
|
|
$milestone_set{$m} = 1;
|
|
}
|
|
}
|
|
|
|
@::component_list = ();
|
|
@::version_list = ();
|
|
@::milestone_list = ();
|
|
foreach my $c (@::legal_components) {
|
|
if ($component_set{$c}) {
|
|
push @::component_list, $c;
|
|
}
|
|
}
|
|
foreach my $v (@::legal_versions) {
|
|
if ($version_set{$v}) {
|
|
push @::version_list, $v;
|
|
}
|
|
}
|
|
foreach my $m (@::legal_target_milestone) {
|
|
if ($milestone_set{$m}) {
|
|
push @::milestone_list, $m;
|
|
}
|
|
}
|
|
|
|
# SELECT box javascript handling. This is done to make the component,
|
|
# versions and milestone SELECTs repaint automatically when a product is
|
|
# selected. Refactored for bug 96534.
|
|
|
|
# make_js_array: iterates through the product array creating a
|
|
# javascript array keyed by product with an alphabetically ordered array
|
|
# for the corresponding elements in the components array passed in.
|
|
# return a string with javascript definitions for the product in a nice
|
|
# arrays which can be linearly appended later on.
|
|
|
|
# make_js_array ( \@products, \%[components/versions/milestones], $array )
|
|
|
|
sub make_js_array {
|
|
my @prods = @{$_[0]};
|
|
my %data = %{$_[1]};
|
|
my $arr = $_[2];
|
|
|
|
my $ret = "\nvar $arr = new Array();\n";
|
|
foreach my $p ( @prods ) {
|
|
# join each element with a "," case-insensitively alpha sorted
|
|
if ( $data{$p} ) {
|
|
$ret .= $arr."[".SqlQuote($p)."] = [";
|
|
# the SqlQuote() protects our 's.
|
|
my @tmp = map( SqlQuote( $_ ), @{ $data{$p} } );
|
|
# do the join on a sorted, quoted list
|
|
@tmp = sort { lc( $a ) cmp lc( $b ) } @tmp;
|
|
$ret .= join( ", ", @tmp );
|
|
$ret .= "];\n";
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
my $jscript = '<script language="JavaScript" type="text/javascript">';
|
|
$jscript .= "\n<!--\n\n";
|
|
|
|
# Add the javascript code for the arrays of components and versions
|
|
# This is used in our javascript functions
|
|
|
|
$jscript .= "var usetms = 0; // do we have target milestone?\n";
|
|
$jscript .= "var first_load = 1; // is this the first time we load the page?\n";
|
|
$jscript .= "var last_sel = []; // caches last selection\n";
|
|
$jscript .= make_js_array( \@::product_list, \%::components, "cpts" );
|
|
$jscript .= make_js_array( \@::product_list, \%::versions, "vers" );
|
|
|
|
if ( Param( "usetargetmilestone" ) ) {
|
|
$jscript .= make_js_array(\@::product_list, \%::target_milestone, "tms");
|
|
$jscript .= "\nusetms = 1; // hooray, we use target milestones\n";
|
|
}
|
|
|
|
$jscript .= << 'ENDSCRIPT';
|
|
|
|
// Adds to the target select object all elements in array that
|
|
// correspond to the elements selected in source.
|
|
// - array should be a array of arrays, indexed by product name. the
|
|
// array should contain the elements that correspont to that
|
|
// product. Example:
|
|
// var array = Array();
|
|
// array['ProductOne'] = [ 'ComponentA', 'ComponentB' ];
|
|
// updateSelect(array, source, target);
|
|
// - sel is a list of selected items, either whole or a diff
|
|
// depending on sel_is_diff.
|
|
// - sel_is_diff determines if we are sending in just a diff or the
|
|
// whole selection. a diff is used to optimize adding selections.
|
|
// - target should be the target select object.
|
|
// - single specifies if we selected a single item. if we did, no
|
|
// need to merge.
|
|
|
|
function updateSelect( array, sel, target, sel_is_diff, single ) {
|
|
|
|
var i;
|
|
|
|
// if single, even if it's a diff (happens when you have nothing
|
|
// selected and select one item alone), skip this.
|
|
if ( ! single ) {
|
|
|
|
// array merging/sorting in the case of multiple selections
|
|
if ( sel_is_diff ) {
|
|
|
|
// merge in the current options with the first selection
|
|
comp = merge_arrays( array[sel[0]], target.options, 1 );
|
|
|
|
// merge the rest of the selection with the results
|
|
for ( i = 1 ; i < sel.length ; i++ ) {
|
|
comp = merge_arrays( array[sel[i]], comp, 0 );
|
|
}
|
|
} else {
|
|
// here we micro-optimize for two arrays to avoid merging with a
|
|
// null array
|
|
comp = merge_arrays( array[sel[0]],array[sel[1]], 0 );
|
|
|
|
// merge the arrays. not very good for multiple selections.
|
|
for ( i = 2; i < sel.length; i++ ) {
|
|
comp = merge_arrays( comp, array[sel[i]], 0 );
|
|
}
|
|
}
|
|
} else {
|
|
// single item in selection, just get me the list
|
|
comp = array[sel[0]];
|
|
}
|
|
|
|
// clear select
|
|
target.options.length = 0;
|
|
|
|
// load elements of list into select
|
|
for ( i = 0; i < comp.length; i++ ) {
|
|
target.options[i] = new Option( comp[i], comp[i] );
|
|
}
|
|
}
|
|
|
|
// Returns elements in a that are not in b.
|
|
// NOT A REAL DIFF: does not check the reverse.
|
|
// - a,b: arrays of values to be compare.
|
|
|
|
function fake_diff_array( a, b ) {
|
|
var newsel = new Array();
|
|
|
|
// do a boring array diff to see who's new
|
|
for ( ia in a ) {
|
|
var found = 0;
|
|
for ( ib in b ) {
|
|
if ( a[ia] == b[ib] ) {
|
|
found = 1;
|
|
}
|
|
}
|
|
if ( ! found ) {
|
|
newsel[newsel.length] = a[ia];
|
|
}
|
|
found = 0;
|
|
}
|
|
return newsel;
|
|
}
|
|
|
|
// takes two arrays and sorts them by string, returning a new, sorted
|
|
// array. the merge removes dupes, too.
|
|
// - a, b: arrays to be merge.
|
|
// - b_is_select: if true, then b is actually an optionitem and as
|
|
// such we need to use item.value on it.
|
|
|
|
function merge_arrays( a, b, b_is_select ) {
|
|
var pos_a = 0;
|
|
var pos_b = 0;
|
|
var ret = new Array();
|
|
|
|
// iterate through both arrays and add the larger item to the return
|
|
// list. remove dupes, too. Use toLowerCase to provide
|
|
// case-insensitivity.
|
|
|
|
while ( ( pos_a < a.length ) && ( pos_b < b.length ) ) {
|
|
|
|
if ( b_is_select ) {
|
|
bitem = b[pos_b].value;
|
|
} else {
|
|
bitem = b[pos_b];
|
|
}
|
|
aitem = a[pos_a];
|
|
|
|
// smaller item in list a
|
|
if ( aitem.toLowerCase() < bitem.toLowerCase() ) {
|
|
ret[ret.length] = aitem;
|
|
pos_a++;
|
|
} else {
|
|
// smaller item in list b
|
|
if ( aitem.toLowerCase() > bitem.toLowerCase() ) {
|
|
ret[ret.length] = bitem;
|
|
pos_b++;
|
|
} else {
|
|
// list contents are equal, inc both counters.
|
|
ret[ret.length] = aitem;
|
|
pos_a++;
|
|
pos_b++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// catch leftovers here. these sections are ugly code-copying.
|
|
if ( pos_a < a.length ) {
|
|
for ( ; pos_a < a.length ; pos_a++ ) {
|
|
ret[ret.length] = a[pos_a];
|
|
}
|
|
}
|
|
|
|
if ( pos_b < b.length ) {
|
|
for ( ; pos_b < b.length; pos_b++ ) {
|
|
if ( b_is_select ) {
|
|
bitem = b[pos_b].value;
|
|
} else {
|
|
bitem = b[pos_b];
|
|
}
|
|
ret[ret.length] = bitem;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// selectProduct reads the selection from f.product and updates
|
|
// f.version, component and target_milestone accordingly.
|
|
// - f: a form containing product, component, varsion and
|
|
// target_milestone select boxes.
|
|
// globals (3vil!):
|
|
// - cpts, vers, tms: array of arrays, indexed by product name. the
|
|
// subarrays contain a list of names to be fed to the respective
|
|
// selectboxes. For bugzilla, these are generated with perl code
|
|
// at page start.
|
|
// - usetms: this is a global boolean that is defined if the
|
|
// bugzilla installation has it turned on. generated in perl too.
|
|
// - first_load: boolean, specifying if it's the first time we load
|
|
// the query page.
|
|
// - last_sel: saves our last selection list so we know what has
|
|
// changed, and optimize for additions.
|
|
|
|
function selectProduct( f ) {
|
|
|
|
// this is to avoid events that occur before the form itself is
|
|
// ready. mozilla doesn't seem to trigger this, though.
|
|
|
|
if ( !f ) {
|
|
return;
|
|
}
|
|
|
|
// if this is the first load and nothing is selected, no need to
|
|
// merge and sort all components; perl gives it to us sorted.
|
|
|
|
if ( ( first_load ) && ( f.product.selectedIndex == -1 ) ) {
|
|
first_load = 0;
|
|
return;
|
|
}
|
|
|
|
// turn first_load off. this is tricky, since it seems to be
|
|
// redundant with the above clause. It's not: if when we first load
|
|
// the page there is _one_ element selected, it won't fall into that
|
|
// clause, and first_load will remain 1. Then, if we unselect that
|
|
// item, selectProduct will be called but the clause will be valid
|
|
// (since selectedIndex == -1), and we will return - incorrectly -
|
|
// without merge/sorting.
|
|
|
|
first_load = 0;
|
|
|
|
// - sel keeps the array of products we are selected.
|
|
// - is_diff says if it's a full list or just a list of products that
|
|
// were added to the current selection.
|
|
// - single indicates if a single item was selected
|
|
var sel = Array();
|
|
var is_diff = 0;
|
|
var single;
|
|
|
|
// is nothing selected, pick all
|
|
if ( f.product.selectedIndex == -1 ) {
|
|
for ( i=0 ; i<f.product.length ; i++ ) {
|
|
sel[sel.length] = f.product.options[i].value;
|
|
}
|
|
single = 0;
|
|
} else {
|
|
|
|
for ( i=0 ; i<f.product.length ; i++ ) {
|
|
if ( f.product.options[i].selected ) {
|
|
sel[sel.length] = f.product.options[i].value;
|
|
}
|
|
}
|
|
|
|
single = ( sel.length == 1 );
|
|
|
|
// save last_sel before we kill it
|
|
var tmp = last_sel;
|
|
last_sel = sel;
|
|
|
|
// this is an optimization: if we've added components, no need
|
|
// to remerge them; just merge the new ones with the existing
|
|
// options.
|
|
|
|
if ( ( tmp ) && ( tmp.length < sel.length ) ) {
|
|
sel = fake_diff_array(sel, tmp);
|
|
is_diff = 1;
|
|
}
|
|
}
|
|
|
|
// do the actual fill/update
|
|
updateSelect( cpts, sel, f.component, is_diff, single );
|
|
updateSelect( vers, sel, f.version, is_diff, single );
|
|
if ( usetms ) {
|
|
updateSelect( tms, sel, f.target_milestone, is_diff, single );
|
|
}
|
|
}
|
|
|
|
// -->
|
|
</script>
|
|
ENDSCRIPT
|
|
|
|
#
|
|
# End the fearsome Javascript section.
|
|
#
|
|
|
|
# Muck the "legal product" list so that the default one is always first (and
|
|
# is therefore visibly selected.
|
|
|
|
# Commented out, until we actually have enough products for this to matter.
|
|
|
|
# set w [lsearch $legal_product $default{"product"}]
|
|
# if {$w >= 0} {
|
|
# set legal_product [concat $default{"product"} [lreplace $legal_product $w $w]]
|
|
# }
|
|
|
|
PutHeader("Bugzilla Query Page", "Query",
|
|
"This page lets you search the database for recorded bugs.",
|
|
q{onLoad="selectProduct(document.forms[0]);"}, $jscript);
|
|
|
|
push @::legal_resolution, "---"; # Oy, what a hack.
|
|
|
|
my @logfields = ("[Bug creation]", @::log_columns);
|
|
|
|
print"<P>Give me a <A HREF=\"queryhelp.cgi\">clue</A> about how to use this form.<P>";
|
|
|
|
print qq{
|
|
<FORM METHOD=GET ACTION="buglist.cgi">
|
|
|
|
<table>
|
|
<tr>
|
|
<th align=left><A HREF="queryhelp.cgi#status">Status</a>:</th>
|
|
<th align=left><A HREF="queryhelp.cgi#resolution">Resolution</a>:</th>
|
|
<th align=left><A HREF="queryhelp.cgi#platform">Platform</a>:</th>
|
|
<th align=left><A HREF="queryhelp.cgi#opsys">OpSys</a>:</th>
|
|
<th align=left><A HREF="queryhelp.cgi#priority">Priority</a>:</th>
|
|
<th align=left><A HREF="queryhelp.cgi#severity">Severity</a>:</th>
|
|
};
|
|
|
|
print "
|
|
</tr>
|
|
<tr>
|
|
<td align=left valign=top>
|
|
|
|
@{[make_selection_widget(\"bug_status\",\@::legal_bug_status,$default{'bug_status'}, $type{'bug_status'}, 1)]}
|
|
|
|
</td>
|
|
<td align=left valign=top>
|
|
@{[make_selection_widget(\"resolution\",\@::legal_resolution,$default{'resolution'}, $type{'resolution'}, 1)]}
|
|
|
|
</td>
|
|
<td align=left valign=top>
|
|
@{[make_selection_widget(\"rep_platform\",\@::legal_platform,$default{'platform'}, $type{'platform'}, 1)]}
|
|
|
|
</td>
|
|
<td align=left valign=top>
|
|
@{[make_selection_widget(\"op_sys\",\@::legal_opsys,$default{'op_sys'}, $type{'op_sys'}, 1)]}
|
|
|
|
</td>
|
|
<td align=left valign=top>
|
|
@{[make_selection_widget(\"priority\",\@::legal_priority,$default{'priority'}, $type{'priority'}, 1)]}
|
|
|
|
</td>
|
|
<td align=left valign=top>
|
|
@{[make_selection_widget(\"bug_severity\",\@::legal_severity,$default{'bug_severity'}, $type{'bug_severity'}, 1)]}
|
|
|
|
</tr>
|
|
</table>
|
|
|
|
<p>
|
|
|
|
<table>
|
|
<tr><td colspan=2>
|
|
$emailinput1<p>
|
|
</td></tr><tr><td colspan=2>
|
|
$emailinput2<p>
|
|
</td></tr>";
|
|
|
|
my $inclselected = "SELECTED";
|
|
my $exclselected = "";
|
|
|
|
|
|
if ($default{'bugidtype'} eq "exclude") {
|
|
$inclselected = "";
|
|
$exclselected = "SELECTED";
|
|
}
|
|
my $bug_id = value_quote($default{'bug_id'});
|
|
|
|
print qq{
|
|
<TR>
|
|
<TD COLSPAN="3">
|
|
<SELECT NAME="bugidtype">
|
|
<OPTION VALUE="include" $inclselected>Only
|
|
<OPTION VALUE="exclude" $exclselected>Exclude
|
|
</SELECT>
|
|
bugs numbered:
|
|
<INPUT TYPE="text" NAME="bug_id" VALUE="$bug_id" SIZE=30>
|
|
</TD>
|
|
</TR>
|
|
};
|
|
|
|
print "
|
|
<tr>
|
|
<td>
|
|
Changed in the <NOBR>last <INPUT NAME=changedin SIZE=2 VALUE=\"$default{'changedin'}\"> days.</NOBR>
|
|
</td>
|
|
<td align=right>
|
|
At <NOBR>least <INPUT NAME=votes SIZE=3 VALUE=\"$default{'votes'}\"> votes.</NOBR>
|
|
</tr>
|
|
</table>
|
|
|
|
|
|
<table>
|
|
<tr>
|
|
<td rowspan=2 align=right>Where the field(s)
|
|
</td><td rowspan=2>
|
|
<SELECT NAME=\"chfield\" MULTIPLE SIZE=4>
|
|
@{[make_options(\@logfields, $default{'chfield'}, $type{'chfield'})]}
|
|
</SELECT>
|
|
</td><td rowspan=2>
|
|
changed.
|
|
</td><td>
|
|
<nobr>dates <INPUT NAME=chfieldfrom SIZE=10 VALUE=\"$default{'chfieldfrom'}\"></nobr>
|
|
<nobr>to <INPUT NAME=chfieldto SIZE=10 VALUE=\"$default{'chfieldto'}\"></nobr>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>changed to value <nobr><INPUT NAME=chfieldvalue SIZE=10> (optional)</nobr>
|
|
</td>
|
|
</table>
|
|
|
|
|
|
<P>
|
|
|
|
<table>
|
|
<tr>
|
|
<TH ALIGN=LEFT VALIGN=BOTTOM>Program:</th>
|
|
<TH ALIGN=LEFT VALIGN=BOTTOM>Version:</th>
|
|
<TH ALIGN=LEFT VALIGN=BOTTOM><A HREF=describecomponents.cgi>Component:</a></th>
|
|
";
|
|
|
|
if (Param("usetargetmilestone")) {
|
|
print "<TH ALIGN=LEFT VALIGN=BOTTOM>Target Milestone:</th>";
|
|
}
|
|
|
|
print "
|
|
</tr>
|
|
<tr>
|
|
|
|
<td align=left valign=top>
|
|
<SELECT NAME=\"product\" MULTIPLE SIZE=5 onChange=\"selectProduct(this.form);\">
|
|
@{[make_options(\@::product_list, $default{'product'}, $type{'product'})]}
|
|
</SELECT>
|
|
</td>
|
|
|
|
<td align=left valign=top>
|
|
<SELECT NAME=\"version\" MULTIPLE SIZE=5>
|
|
@{[make_options(\@::version_list, $default{'version'}, $type{'version'})]}
|
|
</SELECT>
|
|
</td>
|
|
|
|
<td align=left valign=top>
|
|
<SELECT NAME=\"component\" MULTIPLE SIZE=5>
|
|
@{[make_options(\@::component_list, $default{'component'}, $type{'component'})]}
|
|
</SELECT>
|
|
</td>";
|
|
|
|
if (Param("usetargetmilestone")) {
|
|
print "
|
|
<td align=left valign=top>
|
|
<SELECT NAME=\"target_milestone\" MULTIPLE SIZE=5>
|
|
@{[make_options(\@::milestone_list, $default{'target_milestone'}, $type{'target_milestone'})]}
|
|
</SELECT>
|
|
</td>";
|
|
}
|
|
|
|
|
|
sub StringSearch {
|
|
my ($desc, $name) = (@_);
|
|
my $type = $name . "_type";
|
|
my $def = value_quote($default{$name});
|
|
print qq{<tr>
|
|
<td align=right>$desc:</td>
|
|
<td><input name=$name size=30 value="$def"></td>
|
|
<td><SELECT NAME=$type>
|
|
};
|
|
if ($default{$type} eq "") {
|
|
$default{$type} = "allwordssubstr";
|
|
}
|
|
foreach my $i (["substring", "case-insensitive substring"],
|
|
["casesubstring", "case-sensitive substring"],
|
|
["allwordssubstr", "all words as substrings"],
|
|
["anywordssubstr", "any words as substrings"],
|
|
["allwords", "all words"],
|
|
["anywords", "any words"],
|
|
["regexp", "regular expression"],
|
|
["notregexp", "not ( regular expression )"]) {
|
|
my ($n, $d) = (@$i);
|
|
my $sel = "";
|
|
if ($default{$type} eq $n) {
|
|
$sel = " SELECTED";
|
|
}
|
|
print qq{<OPTION VALUE="$n"$sel>$d\n};
|
|
}
|
|
print "</SELECT></TD>
|
|
</tr>
|
|
";
|
|
}
|
|
|
|
print "
|
|
</tr>
|
|
</table>
|
|
|
|
<table border=0>
|
|
";
|
|
|
|
StringSearch("Summary", "short_desc");
|
|
StringSearch("A description entry", "long_desc");
|
|
StringSearch("URL", "bug_file_loc");
|
|
|
|
if (Param("usestatuswhiteboard")) {
|
|
StringSearch("Status whiteboard", "status_whiteboard");
|
|
}
|
|
|
|
if (@::legal_keywords) {
|
|
my $def = value_quote($default{'keywords'});
|
|
print qq{
|
|
<TR>
|
|
<TD ALIGN="right"><A HREF="describekeywords.cgi">Keywords</A>:</TD>
|
|
<TD><INPUT NAME="keywords" SIZE=30 VALUE="$def"></TD>
|
|
<TD>
|
|
};
|
|
my $type = $default{"keywords_type"};
|
|
if ($type eq "or") { # Backward compatability hack.
|
|
$type = "anywords";
|
|
}
|
|
print BuildPulldown("keywords_type",
|
|
[["anywords", "Any of the listed keywords set"],
|
|
["allwords", "All of the listed keywords set"],
|
|
["nowords", "None of the listed keywords set"]],
|
|
$type);
|
|
print qq{</TD></TR>};
|
|
}
|
|
|
|
print "
|
|
</table>
|
|
<p>
|
|
";
|
|
|
|
|
|
my @fields;
|
|
push(@fields, ["noop", "---"]);
|
|
ConnectToDatabase();
|
|
SendSQL("SELECT name, description FROM fielddefs ORDER BY sortkey");
|
|
while (MoreSQLData()) {
|
|
my ($name, $description) = (FetchSQLData());
|
|
push(@fields, [$name, $description]);
|
|
}
|
|
|
|
my @types = (
|
|
["noop", "---"],
|
|
["equals", "equal to"],
|
|
["notequals", "not equal to"],
|
|
["casesubstring", "contains (case-sensitive) substring"],
|
|
["substring", "contains (case-insensitive) substring"],
|
|
["notsubstring", "does not contain (case-insensitive) substring"],
|
|
["allwordssubstr", "all words as (case-insensitive) substrings"],
|
|
["anywordssubstr", "any words as (case-insensitive) substrings"],
|
|
["regexp", "contains regexp"],
|
|
["notregexp", "does not contain regexp"],
|
|
["lessthan", "less than"],
|
|
["greaterthan", "greater than"],
|
|
["anywords", "any words"],
|
|
["allwords", "all words"],
|
|
["nowords", "none of the words"],
|
|
["changedbefore", "changed before"],
|
|
["changedafter", "changed after"],
|
|
["changedto", "changed to"],
|
|
["changedby", "changed by"],
|
|
);
|
|
|
|
|
|
print qq{<A NAME="chart"> </A>\n};
|
|
|
|
foreach my $cmd (grep(/^cmd-/, keys(%::FORM))) {
|
|
if ($cmd =~ /^cmd-add(\d+)-(\d+)-(\d+)$/) {
|
|
$::FORM{"field$1-$2-$3"} = "xyzzy";
|
|
}
|
|
}
|
|
|
|
# foreach my $i (sort(keys(%::FORM))) {
|
|
# print "$i : " . value_quote($::FORM{$i}) . "<BR>\n";
|
|
# }
|
|
|
|
|
|
if (!exists $::FORM{'field0-0-0'}) {
|
|
$::FORM{'field0-0-0'} = "xyzzy";
|
|
}
|
|
|
|
my $jsmagic = qq{ONCLICK="document.forms[0].action='query.cgi#chart' ; document.forms[0].method='POST' ; return 1;"};
|
|
|
|
my $chart;
|
|
for ($chart=0 ; exists $::FORM{"field$chart-0-0"} ; $chart++) {
|
|
my @rows;
|
|
my $row;
|
|
for ($row = 0 ; exists $::FORM{"field$chart-$row-0"} ; $row++) {
|
|
my @cols;
|
|
my $col;
|
|
for ($col = 0 ; exists $::FORM{"field$chart-$row-$col"} ; $col++) {
|
|
my $key = "$chart-$row-$col";
|
|
my $deffield = $::FORM{"field$key"} || "";
|
|
my $deftype = $::FORM{"type$key"} || "";
|
|
my $defvalue = value_quote($::FORM{"value$key"} || "");
|
|
my $line = "";
|
|
$line .= "<TD>";
|
|
$line .= BuildPulldown("field$key", \@fields, $deffield);
|
|
$line .= BuildPulldown("type$key", \@types, $deftype);
|
|
$line .= qq{<INPUT NAME="value$key" VALUE="$defvalue">};
|
|
$line .= "</TD>\n";
|
|
push(@cols, $line);
|
|
}
|
|
push(@rows, "<TR>" . join(qq{<TD ALIGN="center"> or </TD>\n}, @cols) .
|
|
qq{<TD><INPUT TYPE="submit" VALUE="Or" NAME="cmd-add$chart-$row-$col" $jsmagic></TD></TR>});
|
|
}
|
|
print qq{
|
|
<HR>
|
|
<TABLE>
|
|
};
|
|
print join('<TR><TD>And</TD></TR>', @rows);
|
|
print qq{
|
|
<TR><TD><INPUT TYPE="submit" VALUE="And" NAME="cmd-add$chart-$row-0" $jsmagic>
|
|
};
|
|
my $n = $chart + 1;
|
|
if (!exists $::FORM{"field$n-0-0"}) {
|
|
print qq{
|
|
|
|
<INPUT TYPE="submit" VALUE="Add another boolean chart" NAME="cmd-add$n-0-0" $jsmagic>
|
|
|
|
<NOBR><A HREF="queryhelp.cgi#advancedquerying">What is this stuff?</A></NOBR>
|
|
};
|
|
}
|
|
print qq{
|
|
</TD>
|
|
</TR>
|
|
</TABLE>
|
|
};
|
|
}
|
|
print qq{<HR>};
|
|
|
|
|
|
|
|
|
|
if (!$userid) {
|
|
print qq{<INPUT TYPE="hidden" NAME="cmdtype" VALUE="doit">};
|
|
} else {
|
|
print "
|
|
<BR>
|
|
<INPUT TYPE=radio NAME=cmdtype VALUE=doit CHECKED> Run this query
|
|
<BR>
|
|
";
|
|
|
|
my @namedqueries;
|
|
if ($userid) {
|
|
SendSQL("SELECT name FROM namedqueries " .
|
|
"WHERE userid = $userid AND name != '$::defaultqueryname' " .
|
|
"ORDER BY name");
|
|
while (MoreSQLData()) {
|
|
push(@namedqueries, FetchOneColumn());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if (@namedqueries) {
|
|
my $namelist = make_options(\@namedqueries);
|
|
print qq{
|
|
<table cellspacing=0 cellpadding=0><tr>
|
|
<td><INPUT TYPE=radio NAME=cmdtype VALUE=editnamed> Load the remembered query:</td>
|
|
<td rowspan=3><select name=namedcmd>$namelist</select>
|
|
</tr><tr>
|
|
<td><INPUT TYPE=radio NAME=cmdtype VALUE=runnamed> Run the remembered query:</td>
|
|
</tr><tr>
|
|
<td><INPUT TYPE=radio NAME=cmdtype VALUE=forgetnamed> Forget the remembered query:</td>
|
|
</tr></table>};
|
|
}
|
|
|
|
print qq{
|
|
<INPUT TYPE=radio NAME=cmdtype VALUE=asdefault> Remember this as the default query
|
|
<BR>
|
|
<INPUT TYPE=radio NAME=cmdtype VALUE=asnamed> Remember this query, and name it:
|
|
<INPUT TYPE=text NAME=newqueryname>
|
|
<br> <INPUT TYPE="checkbox" NAME="tofooter" VALUE="1">
|
|
and put it in my page footer.
|
|
<BR>
|
|
};
|
|
}
|
|
|
|
print qq{
|
|
<NOBR><B>Sort By:</B>
|
|
<SELECT NAME=\"order\">
|
|
};
|
|
|
|
my $deforder = "'Importance'";
|
|
my @orders = ('Bug Number', $deforder, 'Assignee');
|
|
|
|
if ($::COOKIE{'LASTORDER'}) {
|
|
$deforder = "Reuse same sort as last time";
|
|
unshift(@orders, $deforder);
|
|
}
|
|
|
|
if ($::FORM{'order'}) { $deforder = $::FORM{'order'} }
|
|
|
|
my $defquerytype = $userdefaultquery ? "my" : "the";
|
|
|
|
print make_options(\@orders, $deforder);
|
|
print "</SELECT></NOBR>
|
|
<INPUT TYPE=\"submit\" VALUE=\"Submit query\">
|
|
<INPUT TYPE=\"reset\" VALUE=\"Reset back to $defquerytype default query\">
|
|
";
|
|
|
|
if ($userdefaultquery) {
|
|
print qq{<BR><A HREF="query.cgi?nukedefaultquery=1">Set my default query back to the system default</A>};
|
|
}
|
|
|
|
print "
|
|
</FORM>
|
|
";
|
|
|
|
###
|
|
### I really hate this redudancy, but if somebody for some inexplicable reason doesn't like using
|
|
### the footer for these links, they can uncomment this section.
|
|
###
|
|
|
|
# if (UserInGroup("tweakparams")) {
|
|
# print "<a href=editparams.cgi>Edit Bugzilla operating parameters</a><br>\n";
|
|
# }
|
|
# if (UserInGroup("editcomponents")) {
|
|
# print "<a href=editproducts.cgi>Edit Bugzilla products and components</a><br>\n";
|
|
# }
|
|
# if (UserInGroup("editkeywords")) {
|
|
# print "<a href=editkeywords.cgi>Edit Bugzilla keywords</a><br>\n";
|
|
# }
|
|
# if ($userid) {
|
|
# print "<a href=relogin.cgi>Log in as someone besides <b>$::COOKIE{'Bugzilla_login'}</b></a><br>\n";
|
|
# }
|
|
# print "<a href=userprefs.cgi>Change your password or preferences.</a><br>\n";
|
|
# print "<a href=\"enter_bug.cgi\">Report a new bug.</a><br>\n";
|
|
# print "<a href=\"createaccount.cgi\">Open a new Bugzilla account</a><br>\n";
|
|
# print "<a href=\"reports.cgi\">Bug reports</a><br>\n";
|
|
|
|
|
|
PutFooter();
|