Bug 173622 - Move template handling into a module. r=justdave, joel, a=justdave

parent 1a3c26e6
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
#
package Bugzilla;
use strict;
use Bugzilla::CGI;
use Bugzilla::Template;
sub create {
my $class = shift;
my $B = $class->instance;
# And set up the vars for this request
$B->_init_transient;
return $B;
}
# We don't use Class::Singleton, because theres no need. However, I'm keeping
# the same interface in case we do change in the future
my $_instance;
sub instance {
my $class = shift;
$_instance = $class->_new_instance unless ($_instance);
return $_instance;
}
sub template { return $_[0]->{_template}; }
sub cgi { return $_[0]->{_cgi}; }
# PRIVATE methods below here
# Called from instance
sub _new_instance {
my $class = shift;
my $self = { };
bless($self, $class);
$self->_init_persistent;
return $self;
}
# Initialise persistent items
sub _init_persistent {
my $self = shift;
# Set up the template
$self->{_template} = Bugzilla::Template->create();
}
# Initialise transient (per-request) items
sub _init_transient {
my $self = shift;
$self->{_cgi} = new Bugzilla::CGI if exists $::ENV{'GATEWAY_INTERFACE'};
}
# Clean up transient items such as database handles
sub _cleanup {
my $self = shift;
delete $self->{_cgi};
}
sub DESTROY {
my $self = shift;
# Clean up transient items. We can't just let perl handle removing
# stuff from the $self hash because some stuff (eg database handles)
# may need special casing
# under a persistent environment (ie mod_perl)
$self->_cleanup;
}
1;
__END__
=head1 NAME
Bugzilla - Semi-persistent collection of various objects used by scripts
and modules
=head1 SYNOPSIS
use Bugzilla;
Bugzilla->create;
sub someModulesSub {
my $B = Bugzilla->instance;
$B->template->process(...);
}
=head1 DESCRIPTION
Several Bugzilla 'things' are used by a variety of modules and scripts. This
includes database handles, template objects, and so on.
This module is a singleton intended as a central place to store these objects.
This approach has several advantages:
=over 4
=item *
They're not global variables, so we don't have issues with them staying arround
with mod_perl
=item *
Everything is in one central place, so its easy to access, modify, and maintain
=item *
Code in modules can get access to these objects without having to have them
all passed from the caller, and the caller's caller, and....
=item *
We can reuse objects across requests using mod_perl where appropriate (eg
templates), whilst destroying those which are only valid for a single request
(such as the current user)
=back
Note that items accessible via this object may be loaded when the Bugzilla
object is created, or may be demand-loaded when requested.
For something to be added to this object, it should either be able to benefit
from persistence when run under mod_perl (such as the a C<template> object),
or should be something which is globally required by a large ammount of code
(such as the current C<user> object).
=head1 CREATION
=over 4
=item C<create>
Creates the C<Bugzilla> object, and initialises any per-request data
=item C<instance>
Returns the current C<Bugzilla> instance. If one doesn't exist, then it will
be created, but no per-request data will be set. The only use this method has
for creating the object is from a mod_perl init script. (Its also what
L<Class::Singleton> does, and I'm trying to keep that interface for this)
=back
=head1 FUNCTIONS
=over 4
=item C<template>
The current C<Template> object, to be used for output
=item C<cgi>
The current C<cgi> object. Note that modules should B<not> be using this in
general. Not all Bugzilla actions are cgi requests. Its useful as a convenience
method for those scripts/templates which are only use via CGI, though.
=back
......@@ -111,7 +111,7 @@ __END__
=head1 NAME
Bugzilla::CGI - CGI handling for Bugzilla
Bugzilla::CGI - CGI handling for Bugzilla
=head1 SYNOPSIS
......
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Terry Weissman <terry@mozilla.org>
# Dan Mosedale <dmose@mozilla.org>
# Jacob Steenhagen <jake@bugzilla.org>
# Bradley Baetz <bbaetz@student.usyd.edu.au>
# Christopher Aillon <christopher@aillon.com>
package Bugzilla::Template;
use strict;
use Bugzilla::Config;
use Bugzilla::Util;
# for time2str - replace by TT Date plugin??
use Date::Format ();
use base qw(Template);
###############################################################################
# Templatization Code
# Use the Toolkit Template's Stash module to add utility pseudo-methods
# to template variables.
use Template::Stash;
# Add "contains***" methods to list variables that search for one or more
# items in a list and return boolean values representing whether or not
# one/all/any item(s) were found.
$Template::Stash::LIST_OPS->{ contains } =
sub {
my ($list, $item) = @_;
return grep($_ eq $item, @$list);
};
$Template::Stash::LIST_OPS->{ containsany } =
sub {
my ($list, $items) = @_;
foreach my $item (@$items) {
return 1 if grep($_ eq $item, @$list);
}
return 0;
};
# Add a "substr" method to the Template Toolkit's "scalar" object
# that returns a substring of a string.
$Template::Stash::SCALAR_OPS->{ substr } =
sub {
my ($scalar, $offset, $length) = @_;
return substr($scalar, $offset, $length);
};
# Add a "truncate" method to the Template Toolkit's "scalar" object
# that truncates a string to a certain length.
$Template::Stash::SCALAR_OPS->{ truncate } =
sub {
my ($string, $length, $ellipsis) = @_;
$ellipsis ||= "";
return $string if !$length || length($string) <= $length;
my $strlen = $length - length($ellipsis);
my $newstr = substr($string, 0, $strlen) . $ellipsis;
return $newstr;
};
# Create the template object that processes templates and specify
# configuration parameters that apply to all templates.
###############################################################################
# Construct the Template object
# Note that all of the failure cases here can't use templateable errors,
# since we won't have a template to use...
sub create {
my $class = shift;
# IMPORTANT - If you make any configuration changes here, make sure to
# make them in t/004.template.t and checksetup.pl.
return $class->new({
# Colon-separated list of directories containing templates.
INCLUDE_PATH => "template/en/custom:template/en/default",
# Remove white-space before template directives (PRE_CHOMP) and at the
# beginning and end of templates and template blocks (TRIM) for better
# looking, more compact content. Use the plus sign at the beginning
# of directives to maintain white space (i.e. [%+ DIRECTIVE %]).
PRE_CHOMP => 1,
TRIM => 1,
COMPILE_DIR => 'data/',
# Functions for processing text within templates in various ways.
# IMPORTANT! When adding a filter here that does not override a
# built-in filter, please also add a stub filter to checksetup.pl
# and t/004template.t.
FILTERS => {
# Render text in strike-through style.
strike => sub { return "<strike>" . $_[0] . "</strike>" },
# Returns the text with backslashes, single/double quotes,
# and newlines/carriage returns escaped for use in JS strings.
js => sub {
my ($var) = @_;
$var =~ s/([\\\'\"])/\\$1/g;
$var =~ s/\n/\\n/g;
$var =~ s/\r/\\r/g;
return $var;
},
# HTML collapses newlines in element attributes to a single space,
# so form elements which may have whitespace (ie comments) need
# to be encoded using &#013;
# See bugs 4928, 22983 and 32000 for more details
html_linebreak => sub {
my ($var) = @_;
$var =~ s/\r\n/\&#013;/g;
$var =~ s/\n\r/\&#013;/g;
$var =~ s/\r/\&#013;/g;
$var =~ s/\n/\&#013;/g;
return $var;
},
xml => \&Bugzilla::Util::xml_quote ,
# This filter escapes characters in a variable or value string for
# use in a query string. It escapes all characters NOT in the
# regex set: [a-zA-Z0-9_\-.]. The 'uri' filter should be used for
# a full URL that may have characters that need encoding.
url_quote => \&Bugzilla::Util::url_quote ,
quoteUrls => \&::quoteUrls ,
bug_link => [ sub {
my ($context, $bug) = @_;
return sub {
my $text = shift;
return &::GetBugLink($text, $bug);
};
},
1
],
# In CSV, quotes are doubled, and any value containing a quote or a
# comma is enclosed in quotes.
csv => sub
{
my ($var) = @_;
$var =~ s/\"/\"\"/g;
if ($var !~ /^-?(\d+\.)?\d*$/) {
$var = "\"$var\"";
}
return $var;
} ,
# Format a time for display (more info in Bugzilla::Util)
time => \&Bugzilla::Util::format_time,
},
PLUGIN_BASE => 'Bugzilla::Template::Plugin',
# Default variables for all templates
VARIABLES => {
# Function for retrieving global parameters.
'Param' => \&Bugzilla::Config::Param,
# Function to create date strings
'time2str' => \&Date::Format::time2str,
# Function for processing global parameters that contain references
# to other global parameters.
'PerformSubsts' => \&::PerformSubsts ,
# Generic linear search function
'lsearch' => \&Bugzilla::Util::lsearch,
# UserInGroup - you probably want to cache this
'UserInGroup' => \&::UserInGroup,
# SyncAnyPendingShadowChanges
# - called in the footer to sync the shadowdb
'SyncAnyPendingShadowChanges' => \&::SyncAnyPendingShadowChanges,
# Bugzilla version
# This could be made a ref, or even a CONSTANT with TT2.08
'VERSION' => $Bugzilla::Config::VERSION ,
},
}) || die("Template creation failed: " . $class->error());
}
1;
__END__
=head1 NAME
Bugzilla::Template - Wrapper arround the Template Toolkit C<Template> object
=head1 SYNOPSYS
my $template = Bugzilla::Template->create;
=head1 DESCRIPTION
This is basically a wrapper so that the correct arguments get passed into
the C<Template> constructor.
It should not be used directly by scripts or modules - instead, use
C<Bugzilla-E<gt>instance-E<gt>template> to get an already created module.
=head1 SEE ALSO
L<Bugzilla>, L<Template>
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
#
package Bugzilla::Template::Plugin::Bugzilla;
use strict;
use base qw(Template::Plugin);
use Bugzilla;
sub new {
my ($class, $context) = @_;
return Bugzilla->instance;
}
1;
__END__
=head1 NAME
Bugzilla::Template::Plugin::Bugzilla
=head1 DESCRIPTION
Template Toolkit plugin to allow access to the persistent C<Bugzilla>
object.
=head1 SEE ALSO
L<Bugzilla>, L<Template::Plugin>
......@@ -893,12 +893,10 @@ sub GetBugActivity {
############# Live code below here (that is, not subroutine defs) #############
use Bugzilla::CGI();
use Bugzilla;
# XXX - mod_perl, this needs to move into all the scripts individually
# Once we do that, look into setting DISABLE_UPLOADS, and overriding
# on a per-script basis
$::cgi = new Bugzilla::CGI();
# XXX - mod_perl - reset this between runs
$::cgi = Bugzilla->instance->cgi;
# Set up stuff for compatibility with the old CGI.pl code
# This code will be removed as soon as possible, in favour of
......
......@@ -54,7 +54,7 @@ foreach (@myproducts) {
&calculate_dupes();
# Generate a static RDF file containing the default view of the duplicates data.
open(CGI, "REQUEST_METHOD=GET QUERY_STRING=ctype=rdf ./duplicates.cgi |")
open(CGI, "GATEWAY_INTERFACE=cmdline REQUEST_METHOD=GET QUERY_STRING=ctype=rdf ./duplicates.cgi |")
|| die "can't fork duplicates.cgi: $!";
open(RDF, ">data/duplicates.tmp")
|| die "can't write to data/duplicates.tmp: $!";
......
......@@ -1725,150 +1725,7 @@ sub FormatTimeUnit {
return $newtime;
}
###############################################################################
# Global Templatization Code
# Use the template toolkit (http://www.template-toolkit.org/) to generate
# the user interface using templates in the "template/" subdirectory.
use Template;
# Create the global template object that processes templates and specify
# configuration parameters that apply to all templates processed in this script.
# IMPORTANT - If you make any configuration changes here, make sure to make
# them in t/004.template.t and checksetup.pl. You may also need to change the
# date settings were last changed - see the comments in checksetup.pl for
# details
$::template ||= Template->new(
{
# Colon-separated list of directories containing templates.
INCLUDE_PATH => "template/en/custom:template/en/default" ,
# Remove white-space before template directives (PRE_CHOMP) and at the
# beginning and end of templates and template blocks (TRIM) for better
# looking, more compact content. Use the plus sign at the beginning
# of directives to maintain white space (i.e. [%+ DIRECTIVE %]).
PRE_CHOMP => 1 ,
TRIM => 1 ,
COMPILE_DIR => 'data/',
# Functions for processing text within templates in various ways.
# IMPORTANT! When adding a filter here that does not override a
# built-in filter, please also add a stub filter to checksetup.pl
# and t/004template.t.
FILTERS =>
{
# Render text in strike-through style.
strike => sub { return "<strike>" . $_[0] . "</strike>" } ,
# Returns the text with backslashes, single/double quotes,
# and newlines/carriage returns escaped for use in JS strings.
js => sub
{
my ($var) = @_;
$var =~ s/([\\\'\"])/\\$1/g;
$var =~ s/\n/\\n/g;
$var =~ s/\r/\\r/g;
return $var;
} ,
# HTML collapses newlines in element attributes to a single space,
# so form elements which may have whitespace (ie comments) need
# to be encoded using &#013;
# See bugs 4928, 22983 and 32000 for more details
html_linebreak => sub
{
my ($var) = @_;
$var =~ s/\r\n/\&#013;/g;
$var =~ s/\n\r/\&#013;/g;
$var =~ s/\r/\&#013;/g;
$var =~ s/\n/\&#013;/g;
return $var;
} ,
# This subroutine in CGI.pl escapes characters in a variable
# or value string for use in a query string. It escapes all
# characters NOT in the regex set: [a-zA-Z0-9_\-.]. The 'uri'
# filter should be used for a full URL that may have
# characters that need encoding.
url_quote => \&Bugzilla::Util::url_quote ,
xml => \&Bugzilla::Util::xml_quote ,
quoteUrls => \&quoteUrls ,
bug_link => [ sub {
my ($context, $bug) = @_;
return sub {
my $text = shift;
return GetBugLink($text, $bug);
};
},
1
],
# In CSV, quotes are doubled, and we enclose the whole value in quotes
csv => sub
{
my ($var) = @_;
$var =~ s/"/""/g;
if ($var !~ /^-?(\d+\.)?\d*$/) {
$var = "\"$var\"";
}
return $var;
} ,
# Format a time for display (more info in Bugzilla::Util)
time => \&Bugzilla::Util::format_time,
} ,
}
) || die("Template creation failed: " . Template->error());
# Use the Toolkit Template's Stash module to add utility pseudo-methods
# to template variables.
use Template::Stash;
# Add "contains***" methods to list variables that search for one or more
# items in a list and return boolean values representing whether or not
# one/all/any item(s) were found.
$Template::Stash::LIST_OPS->{ contains } =
sub {
my ($list, $item) = @_;
return grep($_ eq $item, @$list);
};
$Template::Stash::LIST_OPS->{ containsany } =
sub {
my ($list, $items) = @_;
foreach my $item (@$items) {
return 1 if grep($_ eq $item, @$list);
}
return 0;
};
# Add a "substr" method to the Template Toolkit's "scalar" object
# that returns a substring of a string.
$Template::Stash::SCALAR_OPS->{ substr } =
sub {
my ($scalar, $offset, $length) = @_;
return substr($scalar, $offset, $length);
};
# Add a "truncate" method to the Template Toolkit's "scalar" object
# that truncates a string to a certain length.
$Template::Stash::SCALAR_OPS->{ truncate } =
sub {
my ($string, $length, $ellipsis) = @_;
$ellipsis ||= "";
return $string if !$length || length($string) <= $length;
my $strlen = $length - length($ellipsis);
my $newstr = substr($string, 0, $strlen) . $ellipsis;
return $newstr;
};
###############################################################################
# Constructs a format object from URL parameters. You most commonly call it
......@@ -1876,59 +1733,35 @@ $Template::Stash::SCALAR_OPS->{ truncate } =
# my $format = GetFormat("foo/bar", $::FORM{'format'}, $::FORM{'ctype'});
sub GetFormat {
my ($template, $format, $ctype) = @_;
$ctype ||= "html";
$format ||= "";
# Security - allow letters and a hyphen only
$ctype =~ s/[^a-zA-Z\-]//g;
$format =~ s/[^a-zA-Z\-]//g;
trick_taint($ctype);
trick_taint($format);
$template .= ($format ? "-$format" : "");
$template .= ".$ctype.tmpl";
return
{
'template' => $template ,
'extension' => $ctype ,
'ctype' => $::contenttypes->{$ctype} || "text/plain" ,
return
{
'template' => $template ,
'extension' => $ctype ,
'ctype' => $::contenttypes->{$ctype} || "text/plain" ,
};
}
###############################################################################
# Define the global variables and functions that will be passed to the UI
# template. Additional values may be added to this hash before templates
# are processed.
$::vars =
{
# Function for retrieving global parameters.
'Param' => \&Param ,
############# Live code below here (that is, not subroutine defs) #############
# Function to create date strings
'time2str' => \&time2str ,
use Bugzilla;
# Function for processing global parameters that contain references
# to other global parameters.
'PerformSubsts' => \&PerformSubsts ,
$::BZ = Bugzilla->create();
# Generic linear search function
'lsearch' => \&Bugzilla::Util::lsearch ,
$::template = $::BZ->template();
# UserInGroup - you probably want to cache this
'UserInGroup' => \&UserInGroup ,
# SyncAnyPendingShadowChanges - called in the footer to sync the shadowdb
'SyncAnyPendingShadowChanges' => \&SyncAnyPendingShadowChanges ,
# User Agent - useful for detecting in templates
'user_agent' => $ENV{'HTTP_USER_AGENT'} ,
# Bugzilla version
'VERSION' => $Bugzilla::Config::VERSION,
};
$::vars = {};
1;
......@@ -23,7 +23,10 @@
[%# INTERFACE:
# This template has the same interface as create.html.tmpl
#%]
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
[% PROCESS global/header.html.tmpl
title = "Enter A Bug"
onload = "PutDescription()"
......@@ -62,7 +65,7 @@ function PutDescription() {
[%# Browser sniff to try and reduce the incidence of 4.x and NS 6/7 bugs %]
[% IF user_agent.search("Mozilla/4") AND NOT user_agent.search("compatible") %]
[% IF cgi.user_agent("Mozilla/4") AND NOT cgi.user_agent("compatible") %]
<div style="background-color: lightgrey;
border: 1px solid black;
padding: 2px">
......@@ -79,7 +82,8 @@ function PutDescription() {
</div>
[% END %]
[% IF (matches = user_agent.match('Netscape(\d)')) %]
[% IF cgi.user_agent('Netscape(\d)') %]
[% matches = cgi.user_agent().match('Netscape(\d)') %]
<div style="background-color: lightgrey;
border: 1px solid black;
padding: 2px">
......@@ -123,9 +127,9 @@ function PutDescription() {
</p>
[%# Stop NS 4.x and all v.3 browsers from getting <iframe> code %]
[% IF (user_agent.search("Mozilla/4")
AND NOT user_agent.search("compatible"))
OR (user_agent.search("Mozilla/[123]")) %]
[% IF (cgi.user_agent("Mozilla/4")
AND NOT cgi.user_agent("compatible"))
OR (cgi.user_agent("Mozilla/[123]")) %]
<p>
Visit the <a href="duplicates.cgi">most-frequently-reported bugs page</a>
and see if your bug is there. If not, go to the
......@@ -245,7 +249,7 @@ function PutDescription() {
</select>
</td>
<td valign="top" width="100%">
[% IF user_agent.search("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
<div id="description" style="color: green; margin-left: 10px;
height: 5em; overflow: auto;">
Select a component to see its description here.
......@@ -283,8 +287,8 @@ function PutDescription() {
</td>
</tr>
[% matches = user_agent.match('Gecko/(\d+)') %]
[% buildid = user_agent IF matches %]
[% matches = cgi.user_agent('Gecko/(\d+)') %]
[% buildid = cgi.user_agent() IF matches %]
<tr bgcolor="[% tablecolour %]">
<td align="right" valign="middle">
......
......@@ -19,8 +19,11 @@
# Contributor(s): Gervase Markham <gerv@gerv.net>
#%]
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
[% IF help %]
[% IF user_agent.search("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
<style type="text/css">
.help {
border-style: solid;
......
......@@ -18,9 +18,12 @@
#
# Contributor(s): Gervase Markham <gerv@gerv.net>
#%]
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
[% IF help %]
[% IF user_agent.search("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
[% FOREACH h = help_html %]
<div id="[% h.id %]_help" class="help" style="display: none;">
[%- h.html -%]
......
......@@ -24,8 +24,11 @@
# bug_list: list of integers. List of bug numbers of current query (if any).
# bug.bug_id: integer. Number of current bug (for navigation purposes)
#%]
[% IF NOT (user_agent.match("MSIE [1-6]") OR user_agent.match("Mozilla/4")) %]
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
[% IF NOT (cgi.user_agent("MSIE [1-6]") OR cgi.user_agent("Mozilla/4")) %]
<link rel="Top" href="[% Param('urlbase') %]">
[%# *** Bug List Navigation *** %]
......
......@@ -25,6 +25,9 @@
# search/boolean-charts.html.tmpl.
#%]
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
[% PROCESS global/header.html.tmpl
title = "Search for bugs"
onload = "selectProduct(document.forms['queryform']);initHelp();"
......@@ -35,7 +38,7 @@
[%# The decent help requires Javascript %]
[% IF NOT help %]
<p>
[% IF user_agent.search("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
<script> <!--
document.write("<a href='query.cgi?help=1'>Give me some help</a> (reloads page.)");
// -->
......@@ -51,7 +54,7 @@
<p>
For help, mouse over the page elements.
<font color="red">
[% IF user_agent.match("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
Note that if the help popups are hidden by form element scroll bars,
this is a bug in your browser, not in Bugzilla.
[% END %]
......
......@@ -25,6 +25,9 @@
# search/boolean-charts.html.tmpl.
#%]
[% USE Bugzilla %]
[% cgi = Bugzilla.cgi %]
[% PROCESS global/header.html.tmpl
title = "Search for bugs"
onload = "selectProduct(document.forms['queryform']);initHelp();"
......@@ -35,7 +38,7 @@
[%# The decent help requires Javascript %]
[% IF NOT help %]
<p>
[% IF user_agent.search("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
<script> <!--
document.write("<a href='query.cgi?help=1'>Give me some help</a> (reloads page.)");
// -->
......@@ -51,7 +54,7 @@
<p>
For help, mouse over the page elements.
<font color="red">
[% IF user_agent.match("Mozilla/5") %]
[% IF cgi.user_agent("Mozilla/5") %]
Note that if the help popups are hidden by form element scroll bars,
this is a bug in your browser, not in Bugzilla.
[% END %]
......
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