Commit f4966aeb authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 70907: QuickSearch: port the JS code to perl (make it server-side) - Patch…

Bug 70907: QuickSearch: port the JS code to perl (make it server-side) - Patch by Marc Schumann <wurblzap@gmail.com> r=wicked a=myk
parent d055246d
# -*- 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.
#
# Contributor(s): C. Begle
# Jesse Ruderman
# Andreas Franke <afranke@mathweb.org>
# Stephen Lee <slee@uk.bnsmc.com>
# Marc Schumann <wurblzap@gmail.com>
package Bugzilla::Search::Quicksearch;
# Make it harder for us to do dangerous things in Perl.
use strict;
use Bugzilla;
use Bugzilla::Config;
use Bugzilla::Error;
use base qw(Exporter);
@Bugzilla::Search::Quicksearch::EXPORT = qw(quicksearch);
my $cgi = Bugzilla->cgi;
# Word renamings
my %mappings = (# Status, Resolution, Platform, OS, Priority, Severity
"status" => "bug_status",
"resolution" => "resolution", # no change
"platform" => "rep_platform",
"os" => "op_sys",
"opsys" => "op_sys",
"priority" => "priority", # no change
"pri" => "priority",
"severity" => "bug_severity",
"sev" => "bug_severity",
# People: AssignedTo, Reporter, QA Contact, CC, Added comment (?)
"owner" => "assigned_to", # deprecated since bug 76507
"assignee" => "assigned_to",
"assignedto" => "assigned_to",
"reporter" => "reporter", # no change
"rep" => "reporter",
"qa" => "qa_contact",
"qacontact" => "qa_contact",
"cc" => "cc", # no change
# Product, Version, Component, Target Milestone
"product" => "product", # no change
"prod" => "product",
"version" => "version", # no change
"ver" => "version",
"component" => "component", # no change
"comp" => "component",
"milestone" => "target_milestone",
"target" => "target_milestone",
"targetmilestone" => "target_milestone",
# Summary, Description, URL, Status whiteboard, Keywords
"summary" => "short_desc",
"shortdesc" => "short_desc",
"desc" => "longdesc",
"description" => "longdesc",
#"comment" => "longdesc", # ???
# reserve "comment" for "added comment" email search?
"longdesc" => "longdesc",
"url" => "bug_file_loc",
"whiteboard" => "status_whiteboard",
"statuswhiteboard" => "status_whiteboard",
"sw" => "status_whiteboard",
"keywords" => "keywords", # no change
"kw" => "keywords",
# Attachments
"attachment" => "attachments.description",
"attachmentdesc" => "attachments.description",
"attachdesc" => "attachments.description",
"attachmentdata" => "attachments.thedata",
"attachdata" => "attachments.thedata",
"attachmentmimetype" => "attachments.mimetype",
"attachmimetype" => "attachments.mimetype");
# We might want to put this into localconfig or somewhere
my @platforms = ('pc', 'sun', 'macintosh', 'mac');
my @productExceptions = ('row' # [Browser]
# ^^^
,'new' # [MailNews]
# ^^^
);
my @componentExceptions = ('hang' # [Bugzilla: Component/Keyword Changes]
# ^^^^
);
# Quicksearch-wide globals for boolean charts.
my $chart = 0;
my $and = 0;
my $or = 0;
sub quicksearch {
my ($searchstring) = (@_);
# Remove leading and trailing commas and whitespace.
$searchstring =~ s/(^[\s,]+|[\s,]+$)//g;
ThrowUserError('buglist_parameters_required') unless ($searchstring);
if ($searchstring =~ m/^[0-9,\s]*$/) {
# Bug number(s) only.
# Allow separation by comma or whitespace.
$searchstring =~ s/[,\s]+/,/g;
if (index($searchstring, ',') < $[) {
# Single bug number; shortcut to show_bug.cgi.
print $cgi->redirect(-uri => Param('urlbase') .
"show_bug.cgi?id=$searchstring");
exit;
}
else {
# List of bug numbers.
$cgi->param('bug_id', $searchstring);
$cgi->param('order', 'bugs.bug_id');
$cgi->param('bugidtype', 'include');
}
}
else {
# It's not just a bug number or a list of bug numbers.
# Maybe it's an alias?
if ($searchstring =~ /^([^,\s]+)$/) {
if (Bugzilla->dbh->selectrow_array(q{SELECT COUNT(*)
FROM bugs
WHERE alias = ?},
undef,
$1)) {
print $cgi->redirect(-uri => Param('urlbase') .
"show_bug.cgi?id=$1");
exit;
}
}
# It's no alias either, so it's a more complex query.
&::GetVersionTable();
# Globally translate " AND ", " OR ", " NOT " to space, pipe, dash.
$searchstring =~ s/\s+AND\s+/ /g;
$searchstring =~ s/\s+OR\s+/|/g;
$searchstring =~ s/\s+NOT\s+/ -/g;
my @words = splitString($searchstring);
my $searchComments = $#words < Param('quicksearch_comment_cutoff');
my @openStates = &::OpenStates();
my @closedStates;
my (%states, %resolutions);
foreach (@::legal_bug_status) {
push(@closedStates, $_) unless &::IsOpenedState($_);
}
foreach (@openStates) { $states{$_} = 1 }
if ($words[0] eq 'ALL') {
foreach (@::legal_bug_status) { $states{$_} = 1 }
shift @words;
}
elsif ($words[0] eq 'OPEN') {
shift @words;
}
elsif ($words[0] =~ /^\+[A-Z]+(,[A-Z]+)*$/) {
# e.g. +DUP,FIX
if (matchPrefixes(\%states,
\%resolutions,
[split(/,/, substr($words[0], 1))],
\@closedStates,
\@::legal_resolution)) {
shift @words;
# Allowing additional resolutions means we need to keep
# the "no resolution" resolution.
$resolutions{'---'} = 1;
}
else {
# Carry on if no match found.
}
}
elsif ($words[0] =~ /^[A-Z]+(,[A-Z]+)*$/) {
# e.g. NEW,ASSI,REOP,FIX
undef %states;
if (matchPrefixes(\%states,
\%resolutions,
[split(/,/, $words[0])],
\@::legal_bug_status,
\@::legal_resolution)) {
shift @words;
}
else {
# Carry on if no match found
foreach (@openStates) { $states{$_} = 1 }
}
}
else {
# Default: search for unresolved bugs only.
# Put custom code here if you would like to change this behaviour.
}
# If we have wanted resolutions, allow closed states
if (keys(%resolutions)) {
foreach (@closedStates) { $states{$_} = 1 }
}
$cgi->param('bug_status', keys(%states));
$cgi->param('resolution', keys(%resolutions));
# Loop over all main-level QuickSearch words.
foreach my $qsword (@words) {
my $negate = substr($qsword, 0, 1) eq '-';
if ($negate) {
$qsword = substr($qsword, 1);
}
my $firstChar = substr($qsword, 0, 1);
my $baseWord = substr($qsword, 1);
my @subWords = split(/[\|,]/, $baseWord);
if ($firstChar eq '+') {
foreach (@subWords) {
addChart('short_desc', 'substring', $qsword, $negate);
}
}
elsif ($firstChar eq '#') {
addChart('short_desc', 'anywords', $baseWord, $negate);
if ($searchComments) {
addChart('longdesc', 'anywords', $baseWord, $negate);
}
}
elsif ($firstChar eq ':') {
foreach (@subWords) {
addChart('product', 'substring', $_, $negate);
addChart('component', 'substring', $_, $negate);
}
}
elsif ($firstChar eq '@') {
foreach (@subWords) {
addChart('assigned_to', 'substring', $_, $negate);
}
}
elsif ($firstChar eq '[') {
addChart('short_desc', 'substring', $baseWord, $negate);
addChart('status_whiteboard', 'substring', $baseWord, $negate);
}
elsif ($firstChar eq '!') {
addChart('keywords', 'anywords', $baseWord, $negate);
}
else { # No special first char
# Split by '|' to get all operands for a boolean OR.
foreach my $or_operand (split(/\|/, $qsword)) {
if ($or_operand =~ /^votes:([0-9]+)$/) {
# votes:xx ("at least xx votes")
addChart('votes', 'greaterthan', $1, $negate);
}
elsif ($or_operand =~ /^([^:]+):([^:]+)$/) {
# generic field1,field2,field3:value1,value2 notation
my @fields = split(/,/, $1);
my @values = split(/,/, $2);
foreach my $field (@fields) {
# Be tolerant about unknown fields
next unless defined($mappings{$field});
$field = $mappings{$field};
foreach (@values) {
addChart($field, 'substring', $_, $negate);
}
}
}
else {
# Having ruled out the special cases, we may now split
# by comma, which is another legal boolean OR indicator.
foreach my $word (split(/,/, $or_operand)) {
# Platform
if (grep({lc($word) eq $_} @platforms)) {
addChart('rep_platform', 'substring',
$word, $negate);
}
# Priority
elsif ($word =~ m/^[pP]([1-5](-[1-5])?)$/) {
addChart('priority', 'regexp',
"[$1]", $negate);
}
# Severity
elsif (grep({lc($word) eq substr($_, 0, 3)}
@::legal_severity)) {
addChart('bug_severity', 'substring',
$word, $negate);
}
# Votes (votes>xx)
elsif ($word =~ m/^votes>([0-9]+)$/) {
addChart('votes', 'greaterthan',
$1, $negate);
}
# Votes (votes>=xx, votes=>xx)
elsif ($word =~ m/^votes(>=|=>)([0-9]+)$/) {
addChart('votes', 'greaterthan',
$2-1, $negate);
}
else { # Default QuickSearch word
if (!grep({lc($word) eq $_}
@productExceptions) &&
length($word)>2
) {
addChart('product', 'substring',
$word, $negate);
}
if (!grep({lc($word) eq $_}
@componentExceptions) &&
length($word)>2
) {
addChart('component', 'substring',
$word, $negate);
}
if (grep({lc($word) eq $_}
@::legal_keywords)) {
addChart('keywords', 'substring',
$word, $negate);
if (length($word)>2) {
addChart('short_desc', 'substring',
$word, $negate);
addChart('status_whiteboard',
'substring',
$word, $negate);
}
}
else {
addChart('short_desc', 'substring',
$word, $negate);
addChart('status_whiteboard', 'substring',
$word, $negate);
}
if ($searchComments) {
addChart('longdesc', 'substring',
$word, $negate);
}
}
# URL field (for IP addrs, host.names,
# scheme://urls)
if ($word =~ m/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/
|| $word =~ /^[A-Za-z]+(\.[A-Za-z]+)+/
|| $word =~ /:[\\\/][\\\/]/
|| $word =~ /localhost/
|| $word =~ /mailto[:]?/
# || $word =~ /[A-Za-z]+[:][0-9]+/ #host:port
) {
addChart('bug_file_loc', 'substring',
$word, $negate);
}
} # foreach my $word (split(/,/, $qsword))
} # votes and generic field detection
} # foreach (split(/\|/, $_))
} # "switch" $firstChar
$chart++;
$and = 0;
$or = 0;
} # foreach (@words)
# We've been very tolerant about invalid queries, so all that's left
# may be an empty query.
scalar($cgi->param())>0 || ThrowUserError("buglist_parameters_required");
}
# List of quicksearch-specific CGI parameters to get rid of.
my @params_to_strip = ('quicksearch', 'load', 'run');
my $modified_query_string = $cgi->canonicalise_query(@params_to_strip);
if ($cgi->param('load')) {
# Param 'load' asks us to display the query in the advanced search form.
print $cgi->redirect(-uri => Param('urlbase') . "query.cgi?" .
"format=advanced&amp;" .
$modified_query_string);
}
# Otherwise, pass the modified query string to the caller.
# We modified $cgi->params, so the caller can choose to look at that, too,
# and disregard the return value.
$cgi->delete(@params_to_strip);
return $modified_query_string;
}
###########################################################################
# Helpers
###########################################################################
# Split string on whitespace, retaining quoted strings as one
sub splitString {
my $string = shift;
my @quoteparts;
my @parts;
my $i = 0;
# Escape backslashes
$string =~ s/\\/\\\//g;
# Now split on quote sign; be tolerant about unclosed quotes
@quoteparts = split(/"/, $string);
foreach (@quoteparts) {
# After every odd quote, escape whitespace
s/(\s)/\\$1/g if $i++ % 2;
}
# Join again
$string = join('"', @quoteparts);
# Now split on unescaped whitespace
@parts = split(/(?<!\\)\s+/, $string);
foreach (@parts) {
# Restore whitespace
s/\\(\s)/$1/g;
# Restore backslashes
s/\\\//\\/g;
# Remove quotes
s/"//g;
}
return @parts;
}
# Expand found prefixes to states or resolutions
sub matchPrefixes {
my $hr_states = shift;
my $hr_resolutions = shift;
my $ar_prefixes = shift;
my $ar_check_states = shift;
my $ar_check_resolutions = shift;
my $foundMatch = 0;
foreach my $prefix (@$ar_prefixes) {
foreach (@$ar_check_states) {
if (/^$prefix/) {
$$hr_states{$_} = 1;
$foundMatch = 1;
}
}
foreach (@$ar_check_resolutions) {
if (/^$prefix/) {
$$hr_resolutions{$_} = 1;
$foundMatch = 1;
}
}
}
return $foundMatch;
}
# Negate comparison type
sub negateComparisonType {
my $comparisonType = shift;
if ($comparisonType eq 'substring') {
return 'notsubstring';
}
elsif ($comparisonType eq 'anywords') {
return 'nowords';
}
elsif ($comparisonType eq 'regexp') {
return 'notregexp';
}
else {
# Don't know how to negate that
ThrowCodeError('unknown_comparison_type');
}
}
# Add a boolean chart
sub addChart {
my ($field, $comparisonType, $value, $negate) = @_;
$negate && ($comparisonType = negateComparisonType($comparisonType));
makeChart("$chart-$and-$or", $field, $comparisonType, $value);
if ($negate) {
$and++;
$or = 0;
}
else {
$or++;
}
}
# Create the CGI parameters for a boolean chart
sub makeChart {
my ($expr, $field, $type, $value) = @_;
$cgi->param("field$expr", $field);
$cgi->param("type$expr", $type);
$cgi->param("value$expr", $value);
}
1;
......@@ -30,10 +30,6 @@ of the Bugzilla Guide in the docs/ directory.
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.
......
......@@ -38,6 +38,7 @@ use vars qw($template $vars);
use Bugzilla;
use Bugzilla::Search;
use Bugzilla::Search::Quicksearch;
use Bugzilla::Constants;
use Bugzilla::User;
......@@ -65,6 +66,15 @@ if (length($buffer) == 0) {
ThrowUserError("buglist_parameters_required");
}
# Determine whether this is a quicksearch query.
my $searchstring = $cgi->param('quicksearch');
if (defined($searchstring)) {
$buffer = quicksearch($searchstring);
# Quicksearch may do a redirect, in which case it does not return.
# If it does return, it has modified $cgi->params so we can use them here
# as if this had been a normal query from the beginning.
}
################################################################################
# Data and Security Validation
################################################################################
......
......@@ -1015,9 +1015,6 @@ if ($my_create_htaccess) {
<FilesMatch ^(.*\.pl|.*localconfig.*)$>
deny from all
</FilesMatch>
<FilesMatch ^(localconfig.js|localconfig.rdf)$>
allow from all
</FilesMatch>
END
close HTACCESS;
chmod $fileperm, ".htaccess";
......@@ -1033,11 +1030,6 @@ END
print "Repairing .htaccess...\n";
open HTACCESS, '>', '.htaccess';
print HTACCESS $oldaccess;
print HTACCESS <<'END';
<FilesMatch ^(localconfig.js|localconfig.rdf)$>
allow from all
</FilesMatch>
END
close HTACCESS;
}
......
......@@ -1424,6 +1424,17 @@ Reason: %reason%
default => 1,
},
{
name => 'quicksearch_comment_cutoff',
desc => q{The maximum number of search terms for a QuickSearch to search
comments.
If the QuickSearch query contains more terms than this value,
QuickSearch will not search comments.},
type => 't',
default => '4',
checker => \&check_numeric
},
# Added for Patch Viewer stuff (attachment.cgi?action=diff)
{
name => 'cvsroot',
......
<!-- <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"> -->
<!-- $Id: security.xml,v 1.6 2005/06/29 23:43:33 zach%zachlipton.com Exp $ -->
<!-- $Id: security.xml,v 1.7 2005/08/21 18:16:41 lpsolit%gmail.com Exp $ -->
<chapter id="security">
<title>Bugzilla Security</title>
......@@ -207,14 +207,6 @@ skip-networking
</simplelist>
</para>
</listitem>
<listitem>
<para>But allow:
<simplelist type="inline">
<member><filename>localconfig.js</filename></member>
<member><filename>localconfig.rdf</filename></member>
</simplelist>
</para>
</listitem>
</itemizedlist>
</listitem>
......
......@@ -725,11 +725,12 @@
summary and status whiteboard of a bug; adding
"<filename>:BazProduct</filename>" would
search only in that product.
You can use it to find a bug by its number or its alias, too.
</para>
<para>You'll find the Quicksearch box on Bugzilla's
front page, along with a
<ulink url="../../quicksearch.html">Help</ulink>
<para>You'll find the Quicksearch box in Bugzilla's footer area.
On Bugzilla's front page, there is an additional
<ulink url="../../page.cgi?id=quicksearch.html">Help</ulink>
link which details how to use it.</para>
</section>
......
//
// This file contains the installation specific values for QuickSearch.
// See quicksearch.js for more details.
//
// the global bugzilla url
var bugzilla = "";
//var bugzilla = "http://bugzilla.mozilla.org/";
// Status and Resolution
// ---------------------
var statuses_open = new Array("UNCONFIRMED","NEW","ASSIGNED","REOPENED");
var statuses_resolved = new Array("RESOLVED","VERIFIED","CLOSED");
var resolutions = new Array("FIXED","INVALID","WONTFIX","LATER",
"REMIND","DUPLICATE","WORKSFORME","MOVED");
// Keywords
// --------
//
// Enumerate all your keywords here. This is necessary to avoid
// "foo is not a legal keyword" errors. This makes it possible
// to include the keywords field in the search by default.
var keywords = new Array(
// "foo", "bar", "baz"
);
// Platforms
// ---------
//
// A list of words <w> (substrings of platform values)
// that will automatically be translated to "platform:<w>"
// E.g. if "mac" is defined as a platform, then searching
// for it will find all bugs with platform="Macintosh",
// but no other bugs with e.g. "mac" in the summary.
var platforms = new Array(
"pc","sun","macintosh","mac" //shortcut added
//,"dec","hp","sgi"
//,"all" //this is a legal value for OpSys, too :(
//,"other"
);
// Severities
// ----------
//
// A list of words <w> (substrings of severity values)
// that will automatically be translated to "severity:<w>"
// E.g with this default set of severities, searching for
// "blo,cri,maj" will find all severe bugs.
var severities = new Array("blo","cri","maj","nor","min","tri","enh");
// Products and Components
// -----------------------
//
// It is not necessary to list all products and components here.
// Instead, you can define a "blacklist" for some commonly used
// words or word fragments that occur in a product or component name
// but should _not_ trigger product/component search.
var product_exceptions = new Array(
"row" // [Browser]
// ^^^
,"new" // [MailNews]
// ^^^
);
var component_exceptions = new Array(
"hang" // [mozilla.org] Bugzilla: Component/Keyword Changes
// ^^^^
);
<!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>Bugzilla QuickSearch</title>
</head>
<body bgcolor="#ffffff">
<p>
<small>If you are already familiar with the original
<a href="query.cgi">Bugzilla Query Form</a>,
you may prefer <a href="quicksearchhack.html">this form</a>.
</small>
</p>
<script src="localconfig.js" type="text/javascript"></script>
<script src="quicksearch.js" type="text/javascript"></script>
<h1>Bugzilla QuickSearch</h1>
<p>
Type in one or more words (or word fragments) to search for:
</p>
<form name="f" action="show_bug.cgi" method="get"
onsubmit="QuickSearch(f.id.value); return false;">
<table>
<tr>
<td><input type="text" size="40" name="id"></td>
<td align="left"><input type="submit" value="Search"></td>
<!-- <td><a href="javascript:QuickSearch_Help();">[Help]</a></td> -->
</tr>
</table>
</form>
<script type="text/javascript">
<!--
document.forms['f'].id.focus();
//-->
</script>
<h2>Getting Started</h2>
<ul>
<li> This is <b>case-insensitive</b> search.
<ul>
<li> &nbsp;<tt>table</tt>&nbsp;, &nbsp;<tt>Table</tt>&nbsp;
and &nbsp;<tt>TABLE</tt>&nbsp; are all the same.</li>
</ul>
</li>
<li> This is <b>all words as substrings</b> search.<br>
Therefore you should <b>use stems</b> to get better results:
<ul>
<li> Use <tt>localiz</tt> instead of <tt>localize</tt> or
<tt>localization</tt>.</li>
<li> Use <tt>bookmark</tt> instead of <tt>bookmarks</tt> or
<tt>bookmarking</tt>.</li>
</ul>
</li>
</ul>
<h2><a name="features">Features</a></h2>
<ul>
<li> Boolean operations: ``<tt>-foo</tt>''(NOT), ``<tt>foo bar</tt>''(AND),
``<tt>foo|bar</tt>''(OR).
<ul>
<li> <b>NOT</b>: Use &nbsp;<tt><b>-</b><i>foo</i></tt>&nbsp; to exclude bugs
with &nbsp;<tt><i>foo</i></tt>&nbsp; in the summary.</li>
<li> <b>AND</b>: Space-separated words are treated as a conjunction.</li>
<li> <b>OR</b>: Within a word, "|"-separated parts denote alternatives. </li>
<li> Besides "|", a comma can be used to separate alternatives.</li>
<li> OR has higher precedence than AND; AND is the top level operation</li>
</ul>
<i>Example:</i> &nbsp;<tt>url,location bar,field -focus</tt>&nbsp;
means
(<tt>url</tt> OR <tt>location</tt>) AND (<tt>bar</tt> OR <tt>field</tt>) AND (NOT <tt>focus</tt>)
<p>
</li>
<li>Use &nbsp;<tt>+foo</tt>&nbsp; to search for bugs where the <b>summary</b> contains &nbsp;<tt>foo</tt>&nbsp; as a <b>substring</b>.<br>
Use &nbsp;<tt>#foo</tt>&nbsp; to search for bugs where the <b>summary</b> contains the <b>word</b> &nbsp;<tt>foo</tt>&nbsp;
<ul>
<li> &nbsp;<tt>+brow</tt>&nbsp; does not find all bugs in the &nbsp;<tt>Browser</tt>&nbsp; product</li>
<li> &nbsp;<tt>#title</tt>&nbsp; does not find bugs bugs with &nbsp;<tt>titlebar</tt>&nbsp; or &nbsp;<tt>titled</tt>&nbsp;</li>
</ul>
Phrases with special chars (space, comma, +, -, #, ...) can be <b>quoted</b>:
<ul>
<li> &nbsp;<tt>"lock icon"</tt>&nbsp;</li>
</ul>
<p>
</li>
<li> <b>Open vs. Resolved Bugs</b>:<br>
By default, only open (i.e. unresolved) bugs are shown.
Use &nbsp;<tt>+DUP</tt>&nbsp; as first word in your query
to include duplicate bugs in your search,
&nbsp;<tt>FIXED</tt>&nbsp; to search for fixed bugs only,
or &nbsp;<tt>ALL</tt>&nbsp; to search all bugs,
regardless of status or resolution. Searching for duplicates is
recommended if you can't find an open bug directly.
<ul>
<li> &nbsp;<tt>+DUP,FIXED table border</tt>&nbsp;</li>
<li> &nbsp;<tt>ALL mouse wheel</tt>&nbsp;</li>
</ul>
<p></li>
<li> <b>Focus the Search with Products &amp; Components</b>:<br>
To search for bugs in product "Foo Bar" only, add
&nbsp;<tt>:foo</tt>&nbsp; or &nbsp;<tt>:bar</tt>&nbsp; or both
to your query.
You can do this with any substring of a
<a href="describecomponents.cgi">product or component</a>
to focus the search.
</li>
</ul>
<h2>More Tips</h2>
<ul>
<li> You can also use this tool to <b>lookup</b> a bug by its number.
<ul>
<li> &nbsp;<tt>12345</tt>&nbsp;</li>
</ul>
</li>
<li> A comma-separated list of bug numbers gives you a list of these bugs.
<ul>
<li> &nbsp;<tt>12345,23456,34567</tt>&nbsp;</li>
</ul>
</li>
</ul>
<p>
By default, the following fields are searched: Summary, Keywords, Product,
Component, Status Whiteboard. If a word looks like a part of a URL, that field
is included in the search, too.
</p>
<!--
<small>For further details, see
<a href="http://bugzilla.mozilla.org/show_bug.cgi?id=61561">Bug 61561</a> and
<a href="http://bugzilla.mozilla.org/show_bug.cgi?id=69793">Bug 69793</a>.
</small>
-->
<hr>
<p>
Use the powerful
<a href="query.cgi">Bugzilla Query Form</a>
for advanced queries.
</p>
</body>
</html>
//
// This is the main JS file for QuickSearch.
//
// Derived from:
//
// * C. Begle's SimpleSearch tool:
// http://www.mozilla.org/quality/help/simplesearch.html
// http://www.mozilla.org/quality/help/bugreport.js
//
// * Jesse Ruderman's bugzilla search page:
// http://www.cs.hmc.edu/~jruderma/s/bugz.html
//
// Created by
// Andreas Franke <afranke@mathweb.org>
//
// Contributors:
// Stephen Lee <slee@uk.bnsmc.com>
// Use no_result variable to avoid problems with "undefined" on some browsers
var no_result="---";
// do_unshift(l, s) is equivalent to l.unshift(s), but some browsers do not
// support the built-in function.
function do_unshift(l, s) {
l.length = l.length + 1;
for (var i=l.length-1; i>0; i--) {
l[i] = l[i-1];
}
l[0] = s;
return l.length;
}
// do_shift(l) is equivalent to l.shift(s), but some browsers do not
// support the built-in function.
function do_shift(l) {
var l0=l[0];
for (var i=0; i<l.length-1; i++) {
l[i] = l[i+1];
}
l.length = l.length - 1;
return l0;
}
function go_to (url) {
// XXX specifying "sidebar" here indicates you want to use a
// function to do the actual loading instead of using the specified
// url directly. bug 236025 covers clarifying this. Pages that specify
// sidebar=1 *must* specify a load_absolute_url function meanwhile.
if ( typeof sidebar != "undefined" && sidebar == 1 ) {
load_absolute_url(url);
} else {
document.location.href = url;
}
}
function map(l, f) {
var l1 = new Array();
for (var i=0; i<l.length; i++) {
l1[i] = f(l[i]);
}
return l1;
}
function isPrefix(s1, s2) {
return (s1.length <= s2.length) &&
(s1 == s2.substring(0,s1.length))
}
function member(s, l) {
for (var i=0; i<l.length; i++) {
if (l[i] == s) return true;
}
return false;
}
function add(s, l) {
if (! member(s, l)) {
do_unshift(l,s);
}
}
function addAll(l1, l2) {
for (var i=0; i<l1.length; i++) {
add(l1[i],l2);
}
}
function isSubset (l1, l2) {
return (l1.length == 0)
|| (member(l1[0],l2) && subset(l1.slice(1),l2));
}
// fields
var f1 = new Array();
var f2 = new Array();
function add_mapping(from,to) {
f1[f1.length] = from;
f2[f2.length] = to;
}
// Status, Resolution, Platform, OS, Priority, Severity
add_mapping("status", "bug_status");
add_mapping("resolution", "resolution"); // no change
add_mapping("platform", "rep_platform");
add_mapping("os", "op_sys");
add_mapping("opsys", "op_sys");
add_mapping("priority", "priority"); // no change
add_mapping("pri", "priority");
add_mapping("severity", "bug_severity");
add_mapping("sev", "bug_severity");
// People: AssignedTo, Reporter, QA Contact, CC, Added comment (?)
add_mapping("owner", "assigned_to");
add_mapping("assignee", "assigned_to");
add_mapping("assignedto", "assigned_to");
add_mapping("reporter", "reporter"); // no change
add_mapping("rep", "reporter");
add_mapping("qa", "qa_contact");
add_mapping("qacontact", "qa_contact");
add_mapping("cc", "cc"); // no change
// Product, Version, Component, Target Milestone
add_mapping("product", "product"); // no change
add_mapping("prod", "product");
add_mapping("version", "version"); // no change
add_mapping("ver", "version");
add_mapping("component", "component"); // no change
add_mapping("comp", "component");
add_mapping("milestone", "target_milestone");
add_mapping("target", "target_milestone");
add_mapping("targetmilestone", "target_milestone");
// Summary, Description, URL, Status whiteboard, Keywords
add_mapping("summary", "short_desc");
add_mapping("shortdesc", "short_desc");
add_mapping("desc", "longdesc");
add_mapping("description", "longdesc");
//add_mapping("comment", "longdesc"); // ???
// reserve "comment" for "added comment" email search?
add_mapping("longdesc", "longdesc");
add_mapping("url", "bug_file_loc");
add_mapping("whiteboard", "status_whiteboard");
add_mapping("statuswhiteboard", "status_whiteboard");
add_mapping("sw", "status_whiteboard");
add_mapping("keywords", "keywords"); // no change
add_mapping("kw", "keywords");
// Attachments
add_mapping("attachment", "attachments.description");
add_mapping("attachmentdesc", "attachments.description");
add_mapping("attachdesc", "attachments.description");
add_mapping("attachmentdata", "attachments.thedata");
add_mapping("attachdata", "attachments.thedata");
add_mapping("attachmentmimetype", "attachments.mimetype");
add_mapping("attachmimetype", "attachments.mimetype");
// disabled because of bug 30823:
// "BugsThisDependsOn" --> "dependson"
// "OtherBugsDependingOnThis"--> "blocked"
//add_mapping("dependson", "dependson");
//add_mapping("blocked", "blocked");
// Substring search doesn't make much sense for the following fields:
// "Attachment is patch" --> "attachments.ispatch"
// "Last changed date" --> "delta_ts"
// "Days since bug changed" --> "(to_days(now()) - to_days(bugs.delta_ts))"
//"groupset"
//"everconfirmed"
//"bug","bugid","bugno" --> "bug_id"
// "votes" --> "votes"
// "votes>5", "votes>=5", "votes=>5" works now, see below
// "votes:5" is interpreted as "votes>=5"
function findIndex(array,value) {
for (var i=0; i<array.length; i++)
if (array[i] == value) return i;
return -1;
}
function mapField(fieldname) {
var i = findIndex(f1,fieldname);
if (i >= 0) return f2[i];
return no_result;
}
// `keywords' is defined externally
function is_keyword(s) {
return member(s, keywords);
}
// `platforms' is defined externally
function is_platform(str) {
return member (str.toLowerCase(),platforms);
}
// `severities' is defined externally
function is_severity(str) {
return member(str.toLowerCase(),severities);
}
// `product_exceptions' is defined externally
function match_product(str) {
var s = str.toLowerCase();
return (s.length > 2) && (! member(s,product_exceptions));
}
// `component_exceptions are defined externally
function match_component(str) {
var s = str.toLowerCase();
return (s.length > 2) && (! member(s,component_exceptions));
}
var status_and_resolution = ""; // for pretty debug output only; these vars
var charts = ""; // always hold the data from the last query
// derived from http://www.mozilla.org/quality/help/bugreport.js
function make_chart(expr, field, type, value) {
charts += "<tr>" +
"<td><tt>" + expr + "</tt></td>" +
"<td><tt>" + field + "</tt></td>" +
"<td><tt>" + type + "</tt></td>" +
"<td><tt>" + value + "</tt></td>" +
"</tr>";
return "&field" + expr + "=" + field +
"&type" + expr + "=" + type +
"&value" + expr + "=" + escape(value).replace(/[+]/g,"%2B");
}
// returns true if at least one of comparelist had the prefix, false otherwise
function addPrefixMatches(prefix, comparelist, resultlist) {
var foundMatch = false;
for (var i=0; i<comparelist.length; i++) {
if (isPrefix(prefix,comparelist[i])) {
foundMatch = true;
add(comparelist[i],resultlist);
}
}
return foundMatch;
}
function prefixesNotFoundError(prefixes,statusValues,resolutionValues) {
var txt;
if (prefixes.length == 1) {
txt = "is not a prefix ";
} else {
txt = "are not prefixes ";
}
alert(prefixes + "\n" + txt +
"of one of these status or resolution values:\n" +
statusValues + "\n" + resolutionValues + "\n");
}
function make_query_URL(url, input, searchLong) {
status_and_resolution = "";
charts = "";
// declare all variables used in this function
var searchURL = url; // bugzilla + "buglist.cgi" (or "query.cgi")
var abort = false; // global flag, checked upon return
var i,j,k,l; // index counters used in 'for' loops
var parts,input2; // escape "quoted" parts of input
var word; // array of words
// (space-separated parts of input2)
var alternative; // array of parts of an element of 'word'
// (separated by '|', sometimes by comma)
var comma_separated_words; // array of parts of an element of 'alternative'
var w; // current element of one of these arrays:
// word, alternative, comma_separated_words
var w0; // first element of 'word'
var prefixes; // comma-separated parts of w0
// (prefixes of status/resolution values)
var expr; // used for 'priority' support
var n,separator; // used for 'votes' support
var colon_separated_parts, fields,values,field;
// used for generic fields:values notation
var chart,and,or; // counters used in add_chart
var negation; // boolean flag used in add_chart
// `statuses_open' and `statuses_resolved' are defined externally
var statusOpen = statuses_open;
var statusResolved = statuses_resolved;
var statusAll = statusOpen.concat(statusResolved);
// `resolutions' is defined externally
var bug_status = statusOpen.slice().reverse(); //reverse is just cosmetic
var resolution = new Array();
// escape everything between quotes: "foo bar" --> "foo%20bar"
parts = input.split('"');
if ((parts.length % 2) != 1) {
alert('Unterminated quote');
abort = true;
return no_result;
}
for (i=1; i<parts.length; i+=2) {
parts[i] = escape(parts[i]);
}
input2 = parts.join('"');
// abort if there are still brackets
if (input2.match(/[(]|[\)]/)) {
alert('Brackets (...) are not supported.\n' +
'Use quotes "..." for values that contain special characters.');
abort = true;
return no_result;
}
// translate " AND "," OR "," NOT " to space,comma,dash
input2 = input2.replace(/[\s]+AND[\s]+/g," ");
input2 = input2.replace(/[\s]+OR[\s]+/g,"|");
input2 = input2.replace(/[\s]+NOT[\s]+/g," -");
// now split into words at space positions
word = input2.split(/[\s]+/);
// determine bug_status and resolution
// the first word may contain relevant info
// This function matches the given prefixes against the given statuses and
// resolutions. Matched statuses are added to bug_status, matched
// resolutions are added to resolution. Returns true if and only if
// some matches were found for at least one of the given prefixes.
function matchPrefixes(prefixes,statuses,resolutions) {
var failedPrefixes = new Array();
var foundMatch = false;
for (var j=0; j<prefixes.length; j++) {
var ok1 = addPrefixMatches(prefixes[j],statuses,bug_status);
var ok2 = addPrefixMatches(prefixes[j],resolutions,resolution);
if ((! ok1) && (! ok2)) {
add(prefixes[j],failedPrefixes);
} else {
foundMatch = true;
}
}
//report an error if some (but not all) prefixes didn't match anything
if (foundMatch && (failedPrefixes.length > 0)) {
prefixesNotFoundError(failedPrefixes,statuses,resolutions);
abort = true;
}
return foundMatch;
}
if (word[0] == "ALL") {
// special case: search for bugs regardless of status
addAll(statusResolved,bug_status);
do_shift(word);
} else if (word[0] == "OPEN") {
// special case: search for open bugs only
do_shift(word);
} else if (word[0].match("^[+][A-Z]+(,[A-Z]+)*$")) {
// e.g. +DUP,FIX
w0 = do_shift(word);
prefixes = w0.substring(1).split(",");
if (! matchPrefixes(prefixes,statusResolved,resolutions)) {
do_unshift(word,w0);
}
} else if (word[0].match("^[A-Z]+(,[A-Z]+)*$")) {
// e.g. NEW,ASSI,REOP,FIX
bug_status = new Array(); // reset
w0 = do_shift(word);
prefixes = w0.split(",");
if (! matchPrefixes(prefixes,statusAll,resolutions)) {
do_unshift(word,w0);
bug_status = statusOpen.reverse(); //reset to default bug_status
}
} else {
// default case:
// search for unresolved bugs only
// uncomment this to include duplicate bugs in the search
// add("DUPLICATE",resolution);
}
if (resolution.length > 0) {
resolution = resolution.reverse();
do_unshift(resolution,"---");
addAll(statusResolved,bug_status);
}
bug_status = bug_status.reverse();
bug_status = map(bug_status,escape);
searchURL += "?bug_status=" + bug_status.join("&bug_status=");
status_and_resolution += 'Status: <tt>'+bug_status+'</tt>';
if (resolution.length > 0) {
resolution = map(resolution,escape);
searchURL += "&resolution=" + resolution.join("&resolution=");
status_and_resolution += '<br>'+'Resolution: <tt>'+resolution+'</tt>';
}
// end of bug_status & resolution stuff
chart = 0;
and = 0;
or = 0;
negation = false;
function negate_comparison_type(type) {
switch(type) {
case "substring": return "notsubstring";
case "anywords": return "nowords";
case "regexp": return "notregexp";
default:
// e.g. "greaterthan"
alert("Can't negate comparison type: `" + type + "'");
abort = true;
return "dummy";
}
}
function add_chart(field,type,value) {
// undo escaping for value: '"foo%20bar"' --> 'foo bar'
var parts = value.split('"');
if ((parts.length % 2) != 1) {
alert('Internal error: unescaping failure');
abort = true;
}
for (var i=1; i<parts.length; i+=2) {
parts[i] = unescape(parts[i]);
}
var value2 = parts.join('');
// negate type if negation is set
var type2 = type;
if (negation) {
type2 = negate_comparison_type(type2);
}
searchURL += make_chart(chart+"-"+and+"-"+or,field,type2,value2);
or++;
if (negation) {
and++;
or=0;
}
}
for (i=0; i<word.length; i++, chart++) {
w = word[i];
negation = false;
if (w.charAt(0) == "-") {
negation = true;
w = w.substring(1);
}
switch (w.charAt(0)) {
case "+":
alternative = w.substring(1).split(/[|,]/);
for (j=0; j<alternative.length; j++)
add_chart("short_desc","substring",alternative[j]);
break;
case "#":
alternative = w.substring(1).replace(/[|,]/g," ");
add_chart("short_desc","anywords",alternative);
if (searchLong)
add_chart("longdesc","anywords",alternative);
break;
case ":":
alternative = w.substring(1).split(",");
for (j=0; j<alternative.length; j++) {
add_chart("product","substring",alternative[j]);
add_chart("component","substring",alternative[j]);
}
break;
case "@":
alternative = w.substring(1).split(",");
for (j=0; j<alternative.length; j++)
add_chart("assigned_to","substring",alternative[j]);
break;
case "[":
add_chart("short_desc","substring",w);
add_chart("status_whiteboard","substring",w);
break;
case "!":
add_chart("keywords","anywords",w.substring(1));
break;
default:
alternative=w.split("|");
for (j=0; j<alternative.length; j++) {
w=alternative[j];
// votes:xx ("at least xx votes")
if (w.match("^votes[:][0-9]+$")) {
n = w.split(/[:]/)[1];
add_chart("votes","greaterthan",String(n-1));
continue;
}
// generic field1,field2,field3:value1,value2 notation
if (w.match("^[^:]+[:][^:\/][^:]*$")) {
colon_separated_parts = w.split(":");
fields = colon_separated_parts[0].split(/[,]+/);
values = colon_separated_parts[1].split(/[,]+/);
for (k=0; k<fields.length; k++) {
field = mapField(fields[k]);
if (field == no_result) {
alert("`"+fields[k]+"'"+
" is not a valid field name.");
abort = true;
return no_result;
} else {
for (l=0; l<values.length; l++) {
add_chart(field,"substring",values[l]);
}
}
}
continue;
}
comma_separated_words=w.split(/[,]+/);
for (k=0; k<comma_separated_words.length; k++) {
w=comma_separated_words[k];
// platform
if (is_platform(w)) {
add_chart("rep_platform","substring",w);
continue;
}
// priority
if (w.match("^[pP][1-5](,[pP]?[1-5])*$")) {
expr = "["+w.replace(/[p,]/g,"")+"]";
add_chart("priority","regexp",expr);
continue;
}
if (w.match("^[pP][1-5]-[1-5]$")) {
expr = "["+w.substring(1)+"]";
add_chart("priority","regexp",expr);
continue;
}
// severity
if (is_severity(w)) {
add_chart("bug_severity","substring",w);
continue;
}
// votes>xx
if (w.match("^votes>[0-9]+$")) {
n = w.split(">")[1];
add_chart("votes","greaterthan",n);
continue;
}
// votes>=xx, votes=>xx
if (w.match("^votes(>=|=>)[0-9]+$")) {
separator = w.match("^votes(>=|=>)[0-9]+$")[1];
n = w.split(separator)[1];
add_chart("votes","greaterthan",String(n-1));
continue;
}
// really default case
if (match_product(w)) {
add_chart("product","substring",w);
}
if (match_component(w)) {
add_chart("component","substring",w);
}
if (is_keyword(w)) {
add_chart("keywords","substring",w);
if (w.length > 2) {
add_chart("short_desc","substring",w);
add_chart("status_whiteboard","substring",w);
}
} else {
add_chart("short_desc","substring",w);
add_chart("status_whiteboard","substring",w);
}
if (searchLong)
add_chart("longdesc","substring",w);
// URL field (for IP addrs, host.names, scheme://urls)
if (w.match(/[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+/)
|| w.match(/^[A-Za-z]+([.][A-Za-z]+)+/)
|| w.match(/[:][\/][\/]/)
|| w.match(/localhost/)
|| w.match(/mailto[:]?/)
// || w.match(/[A-Za-z]+[:][0-9]+/) //host:port
)
add_chart("bug_file_loc","substring",w);
}
}
}
and = 0;
or = 0;
}
//searchURL += "&cmdtype=doit";
if (abort == false) {
return searchURL;
} else {
return no_result;
}
}
function unique_id () {
return (new Date()).getTime();
}
function ShowURL(mode,input) {
var searchURL = make_query_URL(bugzilla+"buglist.cgi", input, false);
if (searchURL != no_result) {
var pieces = searchURL.replace(/[\?]/g,"\n?").replace(/[\&]/g,"\n&");
if (mode == "alert") {
alert(pieces);
} else {
var table = "<table border=1>" +
"<thead>" +
"<tr>" +
"<th>Chart-And-Or</th>" +
"<th>Field</th>" +
"<th>Type</th>" +
"<th>Value</th>" +
"</tr>" +
"</thead>" +
"<tbody>" + charts + "</tbody>" +
"</table>";
var html = '<html>' +
'<head>' +
'<title>' + input + '</title>' +
'</head>' +
'<body>' +
'<a href="' + searchURL + '">' +
'Submit Query' +
'</a>' +
'<p>' + status_and_resolution +
'<p>' + table +
'<pre>' +
pieces.replace(/[\n]/g,"<br>") +
'</pre>' +
'</body>' +
'</html>';
var w = window.open("","preview_"+unique_id());
w.document.write(html);
w.document.close();
}
}
}
//
// new interface:
// searchLong is a boolean now (not a checkbox/radiobutton)
//
function Search(url, input, searchLong) {
var inputstring = new String(input);
var word = inputstring.split(/[\s]+/);
// Check for empty input
if ( word.length == 1 && word[0] == "" )
return;
// Check for potential Bugzilla-busting intensive queries
if ((searchLong!=false) && word.length > 4) {
var message = "Searching Descriptions for more than four words " +
"will take a very long time indeed. Please choose " +
"no more than four keywords for your query.";
alert(message);
return;
}
var searchURL = make_query_URL(url, inputstring, searchLong);
if (searchURL != no_result) {
go_to(searchURL);
//window.open(searchURL, "other" );
} else {
return;
}
}
//
// original interface, untested
//
//function SearchForBugs (input, searchRadio) {
// if (searchRadio[0].checked) {
// return Search(bugzilla + "buglist.cgi", input, false);
// } else {
// return Search(bugzilla + "buglist.cgi", input, true);
// }
//}
// derived from http://www.cs.hmc.edu/~jruderma/s/bugz.html
// QuickSearch combines lookup-by-bug-number and search
// in a single textbox.
//
// type nothing:
// --> go to bugzilla front page
// type a number:
// --> go to that bug number
// type several numbers, separated by commas:
// --> go to a buglist of just those bug numbers
// type anything else:
// --> search summary, product, component, keywords, status whiteboard
// (and URL if it's an IP address, a host.name, or an absolute://URL)
function QuickSearch (input)
{
//remove leading and trailing whitespace
input = input.replace(/^[\s]+/,"").replace(/[\s]+$/,"");
if (input == "")
{
//once this _is_ on http://bugzilla.mozilla.org, it should just return;
go_to(bugzilla);
}
else if (input.match(/^[0-9, ]*$/))
{
if (input.indexOf(",") == -1) {
// only _one_ bug number --> show_bug
go_to(bugzilla+"show_bug.cgi?id="+escape(input));
} else {
// comma-separated bug numbers --> buglist
go_to(bugzilla+"buglist.cgi?bug_id="+escape(input)
+ "&bugidtype=include&order=bugs.bug_id");
}
}
else
{
Search(bugzilla+"buglist.cgi",input,false);
}
return;
}
function LoadQuery(input) {
//remove leading and trailing whitespace
input = input.replace(/^[\s]+/,"").replace(/[\s]+$/,"");
Search(bugzilla+"query.cgi",input,false);
return;
}
......@@ -176,13 +176,14 @@ body
margin-top: 5px;
width: 100%;
font-family: sans-serif;
background: #edf2f2;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
#footer form
{
background: #edf2f2;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
display: inline;
}
#footer span
......
......@@ -19,8 +19,7 @@
# Contributor(s): Myk Melez <myk@mozilla.org>
#%]
//
// This file contains the installation specific values for QuickSearch
// and other Bugzilla clients. See quicksearch.js for more details.
// This file contains installation specific values for third-party clients.
//
// Note: this interface is experimental and under development.
// We may and probably will make breaking changes to it in the future.
......
......@@ -25,7 +25,6 @@
[% PROCESS global/variables.none.tmpl %]
<form method="get" action="show_bug.cgi">
<div id="useful-links">
<div id="links-actions">
<div class="label">Actions:</div>
......@@ -34,8 +33,13 @@
<a href="enter_bug.cgi">New</a> |
<a href="query.cgi">Search</a> |
[% terms.bug %] # <input class="txt" name="id" size="6">
<input class="btn" type="submit" value="Find"> |
<form action="buglist.cgi" method="get"
onsubmit="if (this.quicksearch.value == '')
{ alert('Please enter one or more search terms first.');
return false; } return true;">
<input class="txt" type="text" name="quicksearch">
<input class="btn" type="submit" value="Find">
</form> |
<a href="report.cgi">Reports</a>
......@@ -127,4 +131,3 @@
[%# Sections of links to more things users can do on this installation. %]
[% Hook.process("end") %]
</div>
</form>
......@@ -28,11 +28,12 @@
[% PROCESS global/variables.none.tmpl %]
[% title = BLOCK %]
[% terms.Bugzilla %] Main Page
[% END %]
[% style_urls = [ "skins/standard/index.css" ] %]
[% PROCESS global/header.html.tmpl %]
[% PROCESS global/header.html.tmpl
title = "$terms.Bugzilla Main Page"
style_urls = [ 'skins/standard/index.css' ]
onload = 'document.forms[\'f\'].quicksearch.focus();'
%]
<script type="text/javascript">
......@@ -84,25 +85,19 @@ function addSidebar() {
</ul>
<form id="show-bug" name="f" action="show_bug.cgi" method="get"
onsubmit="QuickSearch(f.id.value); return false;">
<form id="show-bug" name="f" action="buglist.cgi" method="get"
onsubmit="if (this.quicksearch.value == '')
{ alert('Please enter one or more search terms first.');
return false; } return true;">
<div>
<p>Enter [% terms.abug %] # or some search terms:</p>
<input id="text" type="text" name="id">
<input id="show" type="submit" value="Show">
<a href="quicksearch.html">[Help]</a>
<input id="quicksearch" type="text" name="quicksearch">
<input id="find" type="submit" value="Find">
<a href="page.cgi?id=quicksearch.html">[Help]</a>
</div>
</form>
<div class="outro"></div>
</div>
<script type="text/javascript" src="localconfig.js"></script>
<script type="text/javascript" src="quicksearch.js"></script>
<script type="text/javascript">
<!--
document.forms['f'].id.focus();
//-->
</script>
[% PROCESS global/footer.html.tmpl %]
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): N.N.
# Marc Schumann <wurblzap@gmail.com>
#%]
[% PROCESS global/variables.none.tmpl %]
[% INCLUDE global/header.html.tmpl
title = "$terms.Bugzilla QuickSearch",
style = 'ul {margin-bottom: 2ex}
ul li {margin-top: 2ex}
ul li ul li {margin-top: 0}'
onload = 'document.forms[\'f\'].quicksearch.focus()'
%]
<p style="font-size: 80%">
If you are already familiar with the original
<a href="query.cgi">[% terms.Bugzilla %] Search Form</a>,
you may prefer <a href="page.cgi?id=quicksearchhack.html">this form</a>.
</p>
<p>
Type in one or more words (or word fragments) to search for:
</p>
<form name="f" action="buglist.cgi" method="get"
onsubmit="if (this.quicksearch.value == '')
{ alert('Please enter one or more search terms first.');
return false; } return true;">
<input type="text" size="40" name="quicksearch">
<input type="submit" value="Find">
</form>
<h2>Getting Started</h2>
<ul>
<li>
This is <strong>case-insensitive</strong> search:<br />
<ul>
<li><tt>table</tt>, <tt>Table</tt> and <tt>TABLE</tt> are all the same.</li>
</ul>
</li>
<li>
This is <strong>all words as substrings</strong>
search.<br />
Therefore you should <strong>use stems</strong> to get better results:
<ul>
<li>
Use <tt>localiz</tt> instead of <tt>localize</tt> or
<tt>localization</tt>.
</li>
<li>
Use <tt>bookmark</tt> instead of <tt>bookmarks</tt> or
<tt>bookmarking</tt>.
</li>
</ul>
</li>
</ul>
<h2><a name="features">Features</a></h2>
<ul>
<li>
Boolean operations: &ldquo;<tt>-foo</tt>&rdquo;&nbsp;(NOT),
&ldquo;<tt>foo&nbsp;bar</tt>&rdquo;&nbsp;(AND),
&ldquo;<tt>foo|bar</tt>&rdquo;&nbsp;(OR).
<ul>
<li>
<strong>NOT</strong>:<br />
Use <tt><b>-</b><i>foo</i></tt> to exclude [% terms.bugs %]
with <tt><i>foo</i></tt> in the summary.
</li>
<li>
<strong>AND</strong>:<br />
Space-separated words are treated as a conjunction.
</li>
<li>
<strong>OR</strong>:<br />
Within a word, "|"-separated parts denote alternatives.
</li>
<li>
Besides "|", a comma can be used to separate alternatives.
</li>
<li>
OR has higher precedence than AND; AND is the top level operation.
</li>
</ul>
<i>Example:</i>
<tt>url,location bar,field -focus</tt> means
(<tt>url</tt> OR <tt>location</tt>) AND (<tt>bar</tt> OR <tt>field</tt>)
AND (NOT <tt>focus</tt>)
</li>
<li>
Use <tt>+foo</tt> to search for [% terms.bugs %] where the
<strong>summary</strong> contains <tt>foo</tt> as a
<strong>substring</strong>.<br/>
Use <tt>#foo</tt> to search for [% terms.bugs %] where the
<strong>summary</strong> contains the <strong>word</strong> <tt>foo</tt>.
<ul>
<li>
<tt>+brow</tt> does not find all [% terms.bugs %] in the
<tt>Browser</tt> product.
</li>
<li>
<tt>#title</tt> does not find [% terms.bugs %] with <tt>titlebar</tt>
or <tt>titled</tt>.
</li>
</ul>
Phrases with special chars (space, comma, +, -, #, &hellip;) can be
<strong>quoted</strong>:
<ul>
<li>
<tt>"lock icon"</tt>
</li>
</ul>
</li>
<li>
<strong>Open vs. Resolved [% terms.Bugs %]</strong>:<br />
By default, only open (i.e. unresolved) [% terms.bugs %] are shown.
Use <tt>+DUP</tt> as first word in your search to include duplicate
[%+ terms.bugs %] in your search,
<tt>FIXED</tt> to search for fixed [%+ terms.bugs %] only,
or <tt>ALL</tt> to search all [% terms.bugs %],
regardless of status or resolution.
Searching for duplicates is recommended if you can't find an open
[%+ terms.bug %] directly.
<ul>
<li>
<tt>+DUP,FIXED table border</tt>
</li>
<li>
<tt>ALL mouse wheel</tt>
</li>
</ul>
</li>
<li>
<strong>Focus the Search with Products &amp;
Components</strong>:<br />
To search for [% terms.bugs %] in product "Foo Bar" only, add
<tt>:foo</tt> or <tt>:bar</tt> or both to your search.
You can do this with any substring of a
<a href="describecomponents.cgi">product or component</a> to focus the
search.
</li>
</ul>
<h2>More Tips</h2>
<ul>
<li>
You can also use this tool to <strong>lookup</strong> a [% terms.bug %] by
its number:<br />
<ul>
<li><tt>12345</tt></li>
</ul>
</li>
<li>
A comma-separated list of [% terms.bug %] numbers gives you a list of these
[%+ terms.bugs %]:<br />
<ul>
<li><tt>12345,23456,34567</tt></li>
</ul>
</li>
</ul>
<p>
By default, the following fields are searched: Summary, Keywords, Product,
Component, Status Whiteboard. If a word looks like a part of a URL, that field
is included in the search, too.
</p>
<hr>
<p>
Use the powerful <a href="query.cgi">[% terms.Bugzilla %] Search Form</a>
for advanced queries.
</p>
[% PROCESS global/footer.html.tmpl %]
<!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>Bugzilla QuickSearch (for Hackers)</title>
</head>
[%# 1.0@bugzilla.org %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): N.N.
# Marc Schumann <wurblzap@gmail.com>
#%]
<body bgcolor="#ffffff">
[% PROCESS global/variables.none.tmpl %]
<script src="localconfig.js" type="text/javascript"></script>
<script src="quicksearch.js" type="text/javascript"></script>
<h1>Bugzilla QuickSearch (for Hackers)</h1>
[% INCLUDE global/header.html.tmpl
title = "$terms.Bugzilla QuickSearch (for Hackers)",
style = 'th {text-align: left}'
onload = 'document.forms[\'f\'].quicksearch.focus()'
%]
<p>
Type in one or more words (or word fragments) to search for:
Type in one or more words (or word fragments) to search for:
</p>
<form name="f" action="show_bug.cgi" method="get"
onsubmit="QuickSearch(f.id.value); return false;">
<table>
<tr>
<td><input type="text" size="40" name="id"></td>
<td align="left"><input type="submit" name="run" value="Search"></td>
<td align="left"><input type="button" name="load" value="Load Query"
onclick="LoadQuery(f.id.value);">
</td>
</tr>
</table>
<form name="f" action="buglist.cgi" method="get"
onsubmit="if (this.quicksearch.value == '')
{ alert('Please enter one or more search terms first.');
return false; } return true;">
<input type="text" size="40" name="quicksearch">
<input type="submit" value="Find">
<input type="submit" name="load" value="Load Search Form">
</form>
<script type="text/javascript">
<!--
document.forms['f'].id.focus();
//-->
</script>
<p>
This is a case-insensitive ``all words as substrings'' search;
words are separated by spaces.
By default, the following fields are relevant: Summary, Keywords,
Product, Component, Status Whiteboard. If a word looks like a part of a
URL, that field is included in the search, too.
This is a case-insensitive &ldquo;all words as substrings&rdquo; search;
words are separated by spaces.
By default, the following fields are relevant: Summary, Keywords,
Product, Component, Status Whiteboard.
If a word looks like a part of a URL, that field is included in the search,
too.
</p>
<p>
The generic format for a ``word'' is
&nbsp;<tt>field1,...,fieldN:value1,...,valueM</tt>&nbsp;.
A bug qualifies if at least one of the values occurs as a substring in
at least one of the fields.
For example, &nbsp;<tt>assignee,reporter,qa:ibm,sun</tt>&nbsp;
will give you bugs where the assignee, reporter, or qa contact
has an email address that contains
&nbsp;<tt>ibm</tt>&nbsp; or &nbsp;<tt>sun</tt>&nbsp;.
If only &nbsp;<tt>value1,...,valueM</tt>&nbsp; is given,
the prefix (roughly) defaults to &nbsp;<tt>summary,keywords,product,component,statuswhiteboard:</tt>&nbsp; as noted above.
You can use &nbsp;<tt>-<i>word</i></tt>&nbsp; to express the logical negation
of &nbsp;<tt><i>word</i></tt>.&nbsp;
The generic format for a &ldquo;word&rdquo; is
<tt>field1,&hellip;,fieldN:value1,&hellip;,valueM</tt>.
A [% terms.bug %] qualifies if at least one of the values occurs as a
substring in at least one of the fields.
For example, <tt>assignee,reporter,qa:ibm,sun</tt> will give you
[%+ terms.bugs %] where the assignee, reporter, or qa contact has a login
that contains <tt>ibm</tt> or <tt>sun</tt>.
If only <tt>value1,&hellip;,valueM</tt> is given, the prefix (roughly) defaults to
<tt>summary,keywords,product,component,statuswhiteboard:</tt> as noted above.
You can use <tt>-<i>word</i></tt> to express the logical negation of
<tt><i>word</i></tt>.
</p>
<p>
Here is a complete listing of available fields (the Shortcut column is just
for access speed):
Here is a complete listing of available fields (the Shortcut column is just
for access speed):
</p>
<table border="1">
<thead>
<tr>
<td><b>Searched by default</b></td>
<td><b>Shortcut</b></td>
<td><b>Field Name</b></td>
<td><b>Aliases</b></td>
<td><b>Description</b></td>
<th>Searched by default</th>
<th>Shortcut</th>
<th>Field Name</th>
<th>Aliases</th>
<th>Description</th>
</tr>
</thead>
......@@ -76,11 +77,14 @@ for access speed):
<tr>
<td>&nbsp;</td>
<td rowspan="2"><tt>UNCO,NEW,...,CLOS,<br>FIX,DUP,...<i>(as first word)</i></tt></td>
<td rowspan="2">
<tt>UNCO,NEW,&hellip;,CLOS,<br>FIX,DUP,&hellip;<i>(as first word)</i></tt>
</td>
<td><tt>status</tt></td>
<td>&nbsp;</td>
<td><a href="page.cgi?id=fields.html#status">Status</a>
<i>("bug_status")</i>
<td>
<a href="page.cgi?id=fields.html#status">Status</a>
<i>(&ldquo;bug_status&rdquo;)</i>
</td>
</tr>
<tr>
......@@ -94,14 +98,20 @@ for access speed):
<td><i>as-is</i></td>
<td><tt>platform</tt></td>
<td>&nbsp;</td>
<td><a href="page.cgi?id=fields.html#rep_platform">Platform</a> <i>("rep_platform")</i></td>
<td>
<a href="page.cgi?id=fields.html#rep_platform">Platform</a>
<i>(&ldquo;rep_platform&rdquo;)</i>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>os</tt></td>
<td><tt>opsys</tt></td>
<td><a href="page.cgi?id=fields.html#op_sys">OS</a> <i>("op_sys")</i></td>
<td>
<a href="page.cgi?id=fields.html#op_sys">OS</a>
<i>(&ldquo;op_sys&rdquo;)</i>
</td>
</tr>
<tr>
<td>&nbsp;</td>
......@@ -112,10 +122,13 @@ for access speed):
</tr>
<tr>
<td>&nbsp;</td>
<td><tt>blo,cri,...,enh</tt></td>
<td><tt>blo,cri,&hellip;,enh</tt></td>
<td><tt>severity</tt></td>
<td><tt>sev</tt></td>
<td><a href="page.cgi?id=fields.html#bug_severity">Severity</a> <i>("bug_severity")</i></td>
<td>
<a href="page.cgi?id=fields.html#bug_severity">Severity</a>
<i>(&ldquo;bug_severity&rdquo;)</i>
</td>
</tr>
<!-- People: AssignedTo, Reporter, QA Contact, CC, Added comment -->
......@@ -125,29 +138,32 @@ for access speed):
<td>&nbsp;</td>
<td><b>@</b><i>assignee</i></td>
<td><tt>assignedto</tt></td>
<td><tt>assignee, owner</tt></td>
<td><a href="page.cgi?id=fields.html#assigned_to">Assignee</a> <i>("assigned_to")</i></td>
<td><tt>assignee</tt></td>
<td>
<a href="page.cgi?id=fields.html#assigned_to">Assignee</a>
<i>(&ldquo;assigned_to&rdquo;)</i>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>reporter</tt></td>
<td><tt>rep</tt></td>
<td>Reporter (email)</td>
<td>Reporter (login)</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>qa</tt></td>
<td><tt>qacontact</tt></td>
<td>QA Contact (email) <i>("qa_contact")</i></td>
<td>QA Contact (login) <i>(&ldquo;qa_contact&rdquo;)</i></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>cc</tt></td>
<td>&nbsp;</td>
<td>CC (email)</td>
<td>CC (login)</td>
</tr>
<!-- Product, Version, Component, Target Milestone -->
......@@ -177,7 +193,7 @@ for access speed):
<td>&nbsp;</td>
<td><tt>milestone</tt></td>
<td><tt>target, targetmilestone</tt></td>
<td>Target Milestone <i>("target_milestone")</i></td>
<td>Target Milestone <i>(&ldquo;target_milestone&rdquo;)</i></td>
</tr>
<!-- Summary, Description, URL, Status whiteboard, Keywords -->
......@@ -187,29 +203,32 @@ for access speed):
<td>&nbsp;</td>
<td><tt>summary</tt></td>
<td><tt>shortdesc</tt></td>
<td>Bug Summary (short text)<i>("short_desc")</i></td>
<td>
[% terms.Bug %] Summary (short text)
<i>(&ldquo;short_desc&rdquo;)</i>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>description</tt></td>
<td><tt>desc, longdesc<!--, comment--></tt></td>
<!-- reserve "comment" for "added comment" email search?! -->
<td>Bug Description / Comments (long text)</td>
<!-- reserve "comment" for "added comment" login search?! -->
<td>[% terms.Bug %] Description / Comments (long text)</td>
</tr>
<tr>
<td><i>depends</i></td>
<td>&nbsp;</td>
<td><tt>url</tt></td>
<td>&nbsp;</td>
<td>URL <i>("bug_file_loc")</i></td>
<td>URL <i>(&ldquo;bug_file_loc&rdquo;)</i></td>
</tr>
<tr>
<td><i>yes</i></td>
<td>&nbsp;</td>
<td><tt>statuswhiteboard</tt></td>
<td><tt>sw, whiteboard</tt></td>
<td>Status Whiteboard <i>("status_whiteboard")</i></td>
<td>Status Whiteboard <i>(&ldquo;status_whiteboard&rdquo;)</i></td>
</tr>
<tr>
<td><i>yes</i></td>
......@@ -226,40 +245,43 @@ for access speed):
<td>&nbsp;</td>
<td><tt>attachmentdesc</tt></td>
<td><tt>attachdesc</tt></td>
<td>Attachment Description <i>("attachments.description")</i></td>
<td>
Attachment Description
<i>(&ldquo;attachments.description&rdquo;)</i>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>attachmentdata</tt></td>
<td><tt>attachdata</tt></td>
<td>Attachment Data <i>("attachments.thedata")</i></td>
<td>Attachment Data <i>(&ldquo;attachments.thedata&rdquo;)</i></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><tt>attachmentmimetype</tt></td>
<td><tt>attachmimetype</tt></td>
<td>Attachment mime-type <i>("attachments.mimetype")</i></td>
<td>Attachment mime-type <i>(&ldquo;attachments.mimetype&rdquo;)</i></td>
</tr>
</table>
<p>
Examples for some useful abbreviations:
Examples for some useful abbreviations:
</p>
<table border="1">
<thead>
<tr>
<td><b>Syntax</b></td>
<td><b>Semantics and Examples</b></td>
<th>Syntax</th>
<th>Semantics and Examples</th>
</tr>
</thead>
<!--
<tr>
<td><i>STAT</i> <i>(as first word)</i></td>
<td><b>status,resolution:</b><i>STAT</i></td>
<td><b>status,resolution:</b> <i>STAT</i></td>
</tr>
<tr>
<td></td>
......@@ -267,11 +289,11 @@ Examples for some useful abbreviations:
</tr>
<tr>
<td><tt>ALL</tt> <i>(as first word)</i></td>
<td><i>include all resolved bugs in your query</i></td>
<td><i>include all resolved [% terms.bugs %] in your search</i></td>
</tr>
<tr>
<td><tt>+DUP,FIXED</tt> <i>(as first word)</i></td>
<td><i>include DUPLICATE and FIXED bugs in your search</i></td>
<td><i>include DUPLICATE and FIXED [% terms.bugs %] in your search</i></td>
</tr>
-->
......@@ -282,14 +304,14 @@ Examples for some useful abbreviations:
<!--
<tr>
<td><tt>:browser</tt></td>
<td><i>bugs in the Browser product</i></td>
<td><i>[% terms.bugs %] in the Browser product</i></td>
</tr>
<td><tt>:mail</tt></td>
<td><i>bugs in the MailNews product</td>
<td><i>[% terms.bugs %] in the MailNews product</td>
</tr>
<tr>
<td><tt>:xbl</tt></td>
<td><i>bugs in the XBL component</i></td>
<td><i>[% terms.bugs %] in the XBL component</i></td>
</tr>
-->
<tr>
......@@ -298,7 +320,7 @@ Examples for some useful abbreviations:
</tr>
<tr>
<td><tt>blo,cri,maj</tt></td>
<td><i>severe bugs</i></td>
<td><i>severe [% terms.bugs %]</i></td>
</tr>
<tr>
<td><tt>enh</tt></td>
......@@ -310,11 +332,11 @@ Examples for some useful abbreviations:
</tr>
<tr>
<td><tt>p1</tt></td>
<td><i>very high-priority bugs</i></td>
<td><i>very high-priority [% terms.bugs %]</i></td>
</tr>
<tr>
<td><tt>p1-2</tt></td>
<td><i>high-priority bugs</i></td>
<td><i>high-priority [% terms.bugs %]</i></td>
</tr>
<tr>
<td><b>@</b><i>assignee</i></td>
......@@ -323,11 +345,11 @@ Examples for some useful abbreviations:
<!--
<tr>
<td><tt>@nobody</tt></td>
<td><i>ownerless bugs</i></td>
<td><i>assigneeless [% terms.bugs %]</i></td>
</tr>
<tr>
<td><tt>@mozilla.org</tt></td>
<td><i>bugs assigned to mozilla.org members</i></td>
<td><i>[% terms.bugs %] assigned to mozilla.org members</i></td>
</tr>
-->
<tr>
......@@ -337,21 +359,19 @@ Examples for some useful abbreviations:
<!--
<tr>
<td><tt>!crash</tt></td>
<td><i>crasher bugs</i></td>
<td><i>crasher [% terms.bugs %]</i></td>
</tr>
<tr>
<td><tt>!helpwanted</tt></td>
<td><i>bugs waiting for your help</i></td>
<td><i>[% terms.bugs %] waiting for your help</i></td>
</tr>
-->
</table>
<p>
More information can be found in the
<a href="quicksearch.html#features">&quot;Features&quot;</a> section
on the <a href="quicksearch.html">introductory page</a>.
More information can be found in the
<a href="page.cgi?id=quicksearch.html#features">&ldquo;Features&rdquo;</a>
section on the <a href="page.cgi?id=quicksearch.html">introductory page</a>.
</p>
</body>
</html>
[% PROCESS global/footer.html.tmpl %]
......@@ -34,14 +34,8 @@
orient="vertical"
onload="document.getElementById('query-field').addEventListener('keypress', initial_keypress_handler, true)">
<!-- Load QuickSearch libraries -->
<script type="application/x-javascript" src="localconfig.js"/>
<script type="application/x-javascript" src="quicksearch.js"/>
<script type="application/x-javascript"><![CDATA[
// Tell QuickSearch that the source of this is the sidebar
var sidebar = 1;
function load_absolute_url( aAbsoluteURL ) {
content.location = aAbsoluteURL;
}
......@@ -59,7 +53,7 @@ function initial_keypress_handler( aEvent ) {
function normal_keypress_handler( aEvent ) {
if ( aEvent.keyCode == 13 )
QuickSearch(this.value);
load_relative_url('buglist.cgi?quicksearch=' + this.value);
}
]]></script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment