[bugzilla-gnome-org-extensions] initial version
- From: Krzesimir Nowak <krnowak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [bugzilla-gnome-org-extensions] initial version
- Date: Thu, 20 Nov 2014 22:07:24 +0000 (UTC)
commit 1b4fcfca484c012c2867bd9f9e7f5bbf6b4b9254
Author: Olav Vitters <olav vitters nl>
Date: Wed Feb 29 10:12:30 2012 +0100
initial version
Config.pm | 33 ++
Extension.pm | 44 ++
lib/Util.pm | 551 ++++++++++++++++++++++++
template/en/default/browse/README | 16 +
template/en/default/hook/README | 5 +
template/en/default/hook/index-outro.html.tmpl | 12 +
template/en/default/pages/browse.html.tmpl | 473 ++++++++++++++++++++
web/README | 7 +
web/browse.css | 35 ++
web/browse.js | 116 +++++
10 files changed, 1292 insertions(+), 0 deletions(-)
---
diff --git a/Config.pm b/Config.pm
new file mode 100644
index 0000000..71d121e
--- /dev/null
+++ b/Config.pm
@@ -0,0 +1,33 @@
+# -*- 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 Browse Bugzilla Extension.
+#
+# The Initial Developer of the Original Code is Olav Vitters
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Olav Vitters <olav vitters nl>
+
+package Bugzilla::Extension::Browse;
+use strict;
+
+use constant NAME => 'Browse';
+
+use constant REQUIRED_MODULES => [
+];
+
+use constant OPTIONAL_MODULES => [
+];
+
+__PACKAGE__->NAME;
diff --git a/Extension.pm b/Extension.pm
new file mode 100644
index 0000000..db3df42
--- /dev/null
+++ b/Extension.pm
@@ -0,0 +1,44 @@
+# -*- 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 Browse Bugzilla Extension.
+#
+# The Initial Developer of the Original Code is Olav Vitters
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Olav Vitters <olav vitters nl>
+
+package Bugzilla::Extension::Browse;
+use strict;
+use base qw(Bugzilla::Extension);
+
+# This code for this is in ./extensions/Browse/lib/Util.pm
+use Bugzilla::Extension::Browse::Util;
+
+our $VERSION = '0.01';
+
+# See the documentation of Bugzilla::Hook ("perldoc Bugzilla::Hook"
+# in the bugzilla directory) for a list of all available hooks.
+sub install_update_db {
+ my ($self, $args) = @_;
+
+}
+
+sub page_before_template {
+ my ($self, $args) = @_;
+
+ page(%{ $args });
+
+}
+__PACKAGE__->NAME;
diff --git a/lib/Util.pm b/lib/Util.pm
new file mode 100644
index 0000000..6505c69
--- /dev/null
+++ b/lib/Util.pm
@@ -0,0 +1,551 @@
+# -*- 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 Browse Bugzilla Extension.
+#
+# The Initial Developer of the Original Code is YOUR NAME
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# YOUR NAME <YOUR EMAIL ADDRESS>
+
+package Bugzilla::Extension::Browse::Util;
+use strict;
+use base qw(Exporter);
+our @EXPORT = qw(
+ page
+ total_open_bugs
+ what_new_means
+ new_bugs
+ new_patches
+ keyword_bugs
+ no_response_bugs
+ critical_warning_bugs
+ string_bugs
+ by_patch_status
+ by_version
+ needinfo_split
+ by_target
+ by_priority
+ by_severity
+ by_component
+ by_assignee
+ gnome_target_development
+ gnome_target_stable
+ list_blockers
+ browse_bug_link
+);
+
+# This file can be loaded by your extension via
+# "use Bugzilla::Extension::Browse::Util". You can put functions
+# used by your extension in here. (Make sure you also list them in
+# @EXPORT.)
+
+use Bugzilla::Constants;
+use Bugzilla::User;
+use Bugzilla::Search;
+use Bugzilla::Field;
+use Bugzilla::Status;
+use Bugzilla::Util;
+use Bugzilla::Install::Util qw(vers_cmp);
+
+use constant IMPORTANT_PATCH_STATUSES => qw(
+ none
+ accepted-commit_now
+ accepted-commit_after_freeze
+);
+
+sub page {
+ my %params = @_;
+ my ($vars, $page) = @params{qw(vars page_id)};
+ if ($page =~ /^browse\./) {
+ _page_browse($vars);
+ }
+}
+
+sub _page_browse {
+ my $vars = shift;
+
+ my $cgi = Bugzilla->cgi;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $template = Bugzilla->template;
+
+ # All pages point to the same part of the documentation.
+ $vars->{'doc_section'} = 'bugreports.html';
+
+ my $product_name = trim($cgi->param('product') || '');
+ my $product;
+
+ if (!$product_name && $cgi->cookie('DEFAULTPRODUCT')) {
+ $product_name = $cgi->cookie('DEFAULTPRODUCT')
+ if $user->can_enter_product($cgi->cookie('DEFAULTPRODUCT'));
+ }
+
+ my $product_interests = $user->product_interests();
+
+ # If the user didn't select a product and there isn't a default from a cookie,
+ # try getting the first valid product from their interest list.
+ if (!$product_name && scalar @$product_interests) {
+ foreach my $try (@$product_interests) {
+ next if !$user->can_see_product($try->name);
+ $product_name = $try->name;
+ last;
+ }
+ }
+
+ if ($product_name eq '') {
+ # If the user cannot enter bugs in any product, stop here.
+ my @enterable_products = @{$user->get_enterable_products};
+ ThrowUserError('no_products') unless scalar(@enterable_products);
+
+ my $classification = Bugzilla->params->{'useclassification'} ?
+ scalar($cgi->param('classification')) : '__all';
+
+ # Unless a real classification name is given, we sort products
+ # by classification.
+ my @classifications;
+
+ unless ($classification && $classification ne '__all') {
+ if (Bugzilla->params->{'useclassification'}) {
+ my $class;
+ # Get all classifications with at least one enterable product.
+ foreach my $product (@enterable_products) {
+ $class->{$product->classification_id}->{'object'} ||=
+ new Bugzilla::Classification($product->classification_id);
+ # Nice way to group products per classification, without querying
+ # the DB again.
+ push(@{$class->{$product->classification_id}->{'products'}}, $product);
+ }
+ @classifications = sort {$a->{'object'}->sortkey <=> $b->{'object'}->sortkey
+ || lc($a->{'object'}->name) cmp lc($b->{'object'}->name)}
+ (values %$class);
+ }
+ else {
+ @classifications = ({object => undef, products => \ enterable_products});
+ }
+ }
+
+ unless ($classification) {
+ # We know there is at least one classification available,
+ # else we would have stopped earlier.
+ if (scalar(@classifications) > 1) {
+ # We only need classification objects.
+ $vars->{'classifications'} = [map {$_->{'object'}} @classifications];
+
+ $vars->{'target'} = "browse.cgi";
+ $vars->{'format'} = $cgi->param('format');
+
+ print $cgi->header();
+ $template->process("global/choose-classification.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+ # If we come here, then there is only one classification available.
+ $classification = $classifications[0]->{'object'}->name;
+ }
+
+ # Keep only enterable products which are in the specified classification.
+ if ($classification ne "__all") {
+ my $class = new Bugzilla::Classification({'name' => $classification});
+ # If the classification doesn't exist, then there is no product in it.
+ if ($class) {
+ @enterable_products
+ = grep {$_->classification_id == $class->id} @enterable_products;
+ @classifications = ({object => $class, products => \ enterable_products});
+ }
+ else {
+ @enterable_products = ();
+ }
+ }
+
+ if (scalar(@enterable_products) == 0) {
+ ThrowUserError('no_products');
+ }
+ elsif (scalar(@enterable_products) > 1) {
+ $vars->{'classifications'} = \ classifications;
+ $vars->{'target'} = "browse.cgi";
+ $vars->{'format'} = $cgi->param('format');
+
+ print $cgi->header();
+ $template->process("global/choose-product.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ } else {
+ # Only one product exists.
+ $product = $enterable_products[0];
+ }
+ }
+ else {
+ # Do not use Bugzilla::Product::check_product() here, else the user
+ # could know whether the product doesn't exist or is not accessible.
+ $product = new Bugzilla::Product({'name' => $product_name});
+ }
+
+ # We need to check and make sure that the user has permission
+ # to enter a bug against this product.
+ $user->can_enter_product($product ? $product->name : $product_name, THROW_ERROR);
+
+ # Remember selected product
+ $cgi->send_cookie(-name => 'DEFAULTPRODUCT',
+ -value => $product->name,
+ -expires => "Fri, 01-Jan-2038 00:00:00 GMT");
+
+ # Create data structures representing each classification
+ my @classifications = ();
+ if (scalar @$product_interests) {
+ my %watches = (
+ 'name' => 'Watched Products',
+ 'products' => $product_interests
+ );
+ push @classifications, \%watches;
+ }
+
+ if (Bugzilla->params->{'useclassification'}) {
+ foreach my $c (@{$user->get_selectable_classifications}) {
+ # Create hash to hold attributes for each classification.
+ my %classification = (
+ 'name' => $c->name,
+ 'products' => [ @{$user->get_selectable_products($c->id)} ]
+ );
+ # Assign hash back to classification array.
+ push @classifications, \%classification;
+ }
+ }
+
+ $vars->{'classifications'} = \ classifications;
+ $vars->{'product'} = $product;
+ $vars->{'total_open_bugs'} = total_open_bugs($product);
+ $vars->{'what_new_means'} = what_new_means();
+ $vars->{'new_bugs'} = new_bugs($product);
+ $vars->{'new_patches'} = new_patches($product);
+ $vars->{'no_response_bugs'} = scalar(@{no_response_bugs($product)});
+
+ my $keyword = Bugzilla::Keyword->new({ name => 'gnome-love' });
+ if ($keyword) {
+ $vars->{'gnome_love_bugs'} = keyword_bugs($product, $keyword);
+ }
+
+ ######################################################################
+ # Begin temporary searches; If the search will be reused again next
+ # release cycle, please just comment it out instead of deleting it.
+ ######################################################################
+
+ $vars->{'critical_warning_bugs'} = critical_warning_bugs($product);
+ #$vars->{'string_bugs'} = string_bugs($product);
+
+ ######################################################################
+ # End temporary searches
+ ######################################################################
+
+ $vars->{'by_patch_status'} = by_patch_status($product);
+ $vars->{'buglink'} = browse_bug_link($product);
+ $vars->{'by_version'} = by_version($product);
+ $vars->{'by_target'} = by_target($product);
+ $vars->{'by_priority'} = by_priority($product);
+ $vars->{'by_severity'} = by_severity($product);
+ $vars->{'by_component'} = by_component($product);
+ $vars->{'target_development'} = gnome_target_development();
+ $vars->{'target_stable'} = gnome_target_stable();
+ $vars->{'needinfo_split'} = needinfo_split($product);
+
+ ($vars->{'blockers_stable'}, $vars->{'blockers_development'}) = list_blockers($product);
+
+ print Bugzilla->cgi->header();
+
+# my $format = $template->get_format("browse/main",
+# scalar $cgi->param('format'),
+# scalar $cgi->param('ctype'));
+#
+# print $cgi->header($format->{'ctype'});
+# $template->process($format->{'template'}, $vars)
+# || ThrowTemplateError($template->error());
+}
+
+sub browse_open_states {
+ my $dbh = Bugzilla->dbh;
+ return join(",", map { $dbh->quote($_) } grep($_ ne "NEEDINFO", BUG_STATE_OPEN));
+}
+
+sub total_open_bugs {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->selectrow_array("SELECT COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND product_id = ?", undef, $product->id);
+}
+
+sub what_new_means {
+ my $dbh = Bugzilla->dbh;
+ return $dbh->selectrow_array("SELECT " . $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', 7, 'DAY'));
+}
+
+sub new_bugs {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->selectrow_array("SELECT COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND creation_ts >= " . $dbh->sql_date_math('LOCALTIMESTAMP(0)',
'-', 7, 'DAY') . "
+ AND product_id = ?", undef, $product->id);
+}
+
+sub new_patches {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->bz_column_info('attachments', 'status') ?
+ $dbh->selectrow_array("SELECT COUNT(attach_id)
+ FROM bugs, attachments
+ WHERE bugs.bug_id = attachments.bug_id
+ AND bug_status IN (" . browse_open_states() . ")
+ AND attachments.ispatch = 1 AND attachments.isobsolete = 0
+ AND attachments.status = 'none'
+ AND attachments.creation_ts >= " .
$dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', 7, 'DAY') . "
+ AND product_id = ?", undef, $product->id) :
+ "?";
+}
+
+sub keyword_bugs {
+ my ($product, $keyword) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->selectrow_array("SELECT COUNT(bugs.bug_id)
+ FROM bugs, keywords
+ WHERE bugs.bug_id = keywords.bug_id
+ AND bug_status IN (" . browse_open_states() . ")
+ AND keywords.keywordid = ?
+ AND product_id = ?", undef, ($keyword->id, $product->id));
+}
+
+sub no_response_bugs {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+ my @developer_ids = map { $_->id } @{$product->developers};
+
+ if (@developer_ids) {
+ return $dbh->selectcol_arrayref("SELECT bugs.bug_id
+ FROM bugs INNER JOIN longdescs ON longdescs.bug_id = bugs.bug_id
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND bug_severity != 'enhancement'
+ AND product_id = ?
+ AND bugs.reporter NOT IN (" . join(",", @developer_ids) . ")
+ GROUP BY bugs.bug_id
+ HAVING COUNT(distinct longdescs.who) = 1", undef, $product->id);
+ }
+ else {
+ return [];
+ }
+}
+
+sub critical_warning_bugs {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->selectrow_array("SELECT COUNT(bugs.bug_id)
+ FROM bugs INNER JOIN bugs_fulltext ON bugs_fulltext.bug_id = bugs.bug_id
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND " .
$dbh->sql_fulltext_search("bugs_fulltext.comments_noprivate", "'+G_LOG_LEVEL_CRITICAL'") . "
+ AND product_id = ?", undef, $product->id);
+}
+
+sub string_bugs {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->selectrow_array("SELECT COUNT(bugs.bug_id)
+ FROM bugs, keywords, keyworddefs
+ WHERE bugs.bug_id = keywords.bug_id
+ AND keywords.keywordid = keyworddefs.id
+ AND keyworddefs.name = 'string'
+ AND bug_status IN (" . browse_open_states() . ")
+ AND product_id = ?", undef, $product->id);
+}
+
+sub by_patch_status {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->bz_column_info('attachments', 'status') ?
+ $dbh->selectall_arrayref("SELECT attachments.status, COUNT(attach_id)
+ FROM bugs, attachments
+ WHERE attachments.bug_id = bugs.bug_id
+ AND bug_status IN (" . browse_open_states() . ")
+ AND product_id = ?
+ AND attachments.ispatch = 1
+ AND attachments.isobsolete != 1
+ AND attachments.status IN (" . join(",", map { $dbh->quote($_) }
IMPORTANT_PATCH_STATUSES) . ")
+ GROUP BY attachments.status", undef, $product->id) :
+ "?";
+}
+
+sub browse_bug_link {
+ my $product = shift;
+
+ return correct_urlbase() . 'buglist.cgi?product=' . url_quote($product->name) .
+ '&bug_status=' . join(',' ,map { url_quote($_) } grep ($_ ne "NEEDINFO", BUG_STATE_OPEN));
+}
+
+sub by_version {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my @result = sort { vers_cmp($a->[0], $b->[0]) }
+ @{$dbh->selectall_arrayref("SELECT version, COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND product_id = ?
+ GROUP BY version", undef, $product->id)};
+
+ return \ result;
+}
+
+sub needinfo_split {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my $ni_a = Bugzilla::Search::SqlifyDate('-2w');
+ my $ni_b = Bugzilla::Search::SqlifyDate('-4w');
+ my $ni_c = Bugzilla::Search::SqlifyDate('-3m');
+ my $ni_d = Bugzilla::Search::SqlifyDate('-6m');
+ my $ni_e = Bugzilla::Search::SqlifyDate('-1y');
+ my $needinfo_case = "CASE WHEN delta_ts < '$ni_e' THEN 'F'
+ WHEN delta_ts < '$ni_d' THEN 'E'
+ WHEN delta_ts < '$ni_c' THEN 'D'
+ WHEN delta_ts < '$ni_b' THEN 'C'
+ WHEN delta_ts < '$ni_a' THEN 'B'
+ ELSE 'A' END";
+
+ my %results = @{$dbh->selectcol_arrayref("SELECT $needinfo_case age, COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status = 'NEEDINFO'
+ AND product_id = ?
+ GROUP BY $needinfo_case", { Columns=>[1,2] }, $product->id)};
+ return \%results;
+}
+
+sub by_target {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my @result = sort { vers_cmp($a->[0], $b->[0]) }
+ @{$dbh->selectall_arrayref("SELECT target_milestone, COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND target_milestone != '---'
+ AND product_id = ?
+ GROUP BY target_milestone", undef, $product->id)};
+
+ return \ result;
+}
+
+sub by_priority {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my $i = 0;
+ my %order_priority = map { $_ => $i++ } @{get_legal_field_values('priority')};
+
+ my @result = sort { $order_priority{$a->[0]} <=> $order_priority{$b->[0]} }
+ @{$dbh->selectall_arrayref("SELECT priority, COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND product_id = ?
+ GROUP BY priority", undef, $product->id)};
+
+ return \ result;
+}
+
+sub by_severity {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my $i = 0;
+ my %order_severity = map { $_ => $i++ } @{get_legal_field_values('bug_severity')};
+
+ my @result = sort { $order_severity{$a->[0]} <=> $order_severity{$b->[0]} }
+ @{$dbh->selectall_arrayref("SELECT bug_severity, COUNT(bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND product_id = ?
+ GROUP BY bug_severity", undef, $product->id)};
+
+ return \ result;
+}
+
+sub by_component {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ return $dbh->selectall_arrayref("SELECT components.name, COUNT(bugs.bug_id)
+ FROM bugs INNER JOIN components ON bugs.component_id = components.id
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND bugs.product_id = ?
+ GROUP BY components.name", undef, $product->id);
+}
+
+sub by_assignee {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my @result = map { Bugzilla::User->new($_) }
+ @{$dbh->selectall_arrayref("SELECT bugs.assignee AS userid, COUNT(bugs.bug_id)
+ FROM bugs
+ WHERE bug_status IN (" . browse_open_states() . ")
+ AND bugs.product_id = ?
+ GROUP BY components.name", undef, $product->id)};
+
+ return \ result;
+}
+
+sub gnome_target_development {
+ my @legal_gnome_target = @{get_legal_field_values('cf_gnome_target')};
+ return $legal_gnome_target[(scalar @legal_gnome_target) -1];
+}
+
+sub gnome_target_stable {
+ my @legal_gnome_target = @{get_legal_field_values('cf_gnome_target')};
+ return $legal_gnome_target[(scalar @legal_gnome_target) -2];
+}
+
+sub list_blockers {
+ my $product = shift;
+ my $dbh = Bugzilla->dbh;
+
+ my $sth = $dbh->prepare("SELECT bugs.bug_id, products.name AS product, bugs.bug_status,
+ bugs.resolution, bugs.bug_severity, bugs.short_desc
+ FROM bugs INNER JOIN products ON bugs.product_id = products.id
+ WHERE product_id = ?
+ AND bugs.cf_gnome_target = ?
+ AND bug_status IN (" . browse_open_states() . ")
+ ORDER BY bug_id DESC");
+
+ my @list_blockers_development;
+ $sth->execute($product->id, gnome_target_development());
+ while (my $bug = $sth->fetchrow_hashref) {
+ push(@list_blockers_development, $bug);
+ }
+
+ my @list_blockers_stable;
+ $sth->execute($product->id, gnome_target_stable());
+ while (my $bug = $sth->fetchrow_hashref) {
+ push(@list_blockers_stable, $bug);
+ }
+
+ return (\ list_blockers_stable, \ list_blockers_development);
+}
+
+1;
diff --git a/template/en/default/browse/README b/template/en/default/browse/README
new file mode 100644
index 0000000..67d120e
--- /dev/null
+++ b/template/en/default/browse/README
@@ -0,0 +1,16 @@
+Normal templates go in this directory. You can load them in your
+code like this:
+
+use Bugzilla::Error;
+my $template = Bugzilla->template;
+$template->process('browse/some-template.html.tmpl')
+ or ThrowTemplateError($template->error());
+
+That would be how to load a file called some-template.html.tmpl that
+was in this directory.
+
+Note that you have to be careful that the full path of your template
+never conflicts with a template that exists in Bugzilla or in
+another extension, or your template might override that template. That's why
+we created this directory called 'browse' for you, so you
+can put your templates in here to help avoid conflicts.
\ No newline at end of file
diff --git a/template/en/default/hook/README b/template/en/default/hook/README
new file mode 100644
index 0000000..e6c4add
--- /dev/null
+++ b/template/en/default/hook/README
@@ -0,0 +1,5 @@
+Template hooks go in this directory. Template hooks are called in normal
+Bugzilla templates like [% Hook.process('some-hook') %].
+More information about them can be found in the documentation of
+Bugzilla::Extension. (Do "perldoc Bugzilla::Extension" from the main
+Bugzilla directory to see that documentation.)
\ No newline at end of file
diff --git a/template/en/default/hook/index-outro.html.tmpl b/template/en/default/hook/index-outro.html.tmpl
new file mode 100644
index 0000000..30f0a4d
--- /dev/null
+++ b/template/en/default/hook/index-outro.html.tmpl
@@ -0,0 +1,12 @@
+
+ [% IF user.product_interests.size > 0 %]
+ <h3>Interested products</h3>
+
+ <ul>
+ [% FOREACH product = user.product_interests %]
+ <li><a href="browse.cgi?product=[% product.name FILTER uri %]">[% product.name
+ FILTER html %]</a></li>
+ [% END %]
+ </ul>
+ [% END %]
+
diff --git a/template/en/default/pages/browse.html.tmpl b/template/en/default/pages/browse.html.tmpl
new file mode 100644
index 0000000..31bf09e
--- /dev/null
+++ b/template/en/default/pages/browse.html.tmpl
@@ -0,0 +1,473 @@
+[%# The contents of this file are subject to the Mozilla Public
+ # License Version 1.1 (the "License"); you may not use this file
+ # except in compliance with the License. You may obtain a copy of
+ # the License at http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS
+ # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ # implied. See the License for the specific language governing
+ # rights and limitations under the License.
+ #
+ # The Original Code is the Bugzilla Bug Tracking System.
+ #
+ # The Initial Developer of the Original Code is Netscape Communications
+ # Corporation. Portions created by Netscape are
+ # Copyright (C) 1998 Netscape Communications Corporation. All
+ # Rights Reserved.
+ #
+ # Contributor(s): Gervase Markham <gerv gerv net>
+ # Vaskin Kissoyan <vkissoyan yahoo com>
+ # Max Kanat-Alexander <mkanat bugzilla org>
+ # Frédéric Buclin <LpSolit gmail com>
+ # Olav Vitters <olav bkor dhs org>
+ # Guy Pyrzak <guy pyrzak gmail com>
+ # Elliotte Martin <emartin everythingsolved com>
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+[% filtered_product = product.name FILTER html %]
+[% PROCESS global/header.html.tmpl
+ title = "Browse: $filtered_product"
+ h1 = ""
+ style_urls = [ "extensions/Browse/web/browse.css", "skins/standard/buglist.css" ]
+ javascript_urls = [ "extensions/Browse/web/browse.js" ]
+%]
+
+<div id="product_summary">
+ <h3>Project Summary</h3>
+
+ <table border="0" class="figures">
+
+ <tr>
+ <td>Total [% terms.Bugs %]</td>
+ <td align="right">
+ <a href="[% buglink FILTER html %]">
+ [%- total_open_bugs FILTER html %]</a>
+ </td>
+ </tr>
+
+ <tr>
+ <td>New [% terms.Bugs %]</td>
+ <td align="right"><a href="[% buglink FILTER html %]&chfield=[% "[Bug creation]" FILTER uri
%]&chfieldfrom=[% what_new_means FILTER html %]">
+ [% new_bugs FILTER html %]</a></li></td>
+ </tr>
+
+ <tr>
+ <td>New Patches</td>
+ <td align="right">
+ <a href="page.cgi?id=patchreport.html&product=[%- product.name FILTER uri %]&max_days=7">
+ [% new_patches FILTER html %]
+ </a>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ <a class="boogle_edit"
+ href="javascript:addText('keywords:gnome-love')">GNOME-love
+ [%= terms.bugs %]</a>
+ </td>
+ <td align="right">
+ <!--
+ <a href="reports/keyword-search.cgi?product=
+ [%- product.name FILTER uri %]&keyword=gnome-love">
+ -->
+ [%- gnome_love_bugs FILTER html %] <!-- </a> -->
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <!-- <a class="boogle_edit"
+
href="javascript:addText('severity!=enhancement');addText('responders:0');addText('reporter!=developer')"> -->
+ [% terms.Bugs %] without a response <!-- </a> -->
+ </td>
+ <td align="right">
+ <!-- <a href="buglist.cgi?quicksearch=product:
+ [%- product.name FILTER uri %]+responders:0+severity!=enhancement+reporter!=developer"> -->
+ [% no_response_bugs FILTER html %] <!-- </a> -->
+ </td>
+ </tr>
+ [%######################################################################
+ # Temporary time-limited queries; Please just comment these out when
+ # they are not in use if they will be needed again next release
+ # cycle. Also, be sure to leave these in italics so that people can
+ # notice they are different than the standard list.
+ ######################################################################
+ %]
+ <tr>
+ <td>
+ <em><a class="boogle_edit"
+ href="javascript:addText('+G_LOG_LEVEL_CRITICAL')">Critical
+ warning [% terms.bugs %]</a></em>
+ </td>
+ <td align="right">
+ <em><a href="[% buglink FILTER html %]&content=G_LOG_LEVEL_CRITICAL">
+ [%- critical_warning_bugs FILTER html %]</a></em>
+ </td>
+ </tr>
+ <!--
+ <tr>
+ <td><a class="boogle_edit" href="javascript:addText('keyword:string')">String [% terms.bugs %]</a></td>
+ <td align="right"><a href="[% buglink FILTER html %]&keywords=string">[% string_bugs FILTER html
%]</a></td>
+ </tr>
+ -->
+ [%######################################################################
+ # End of temporary, time-limited queries
+ ######################################################################
+ %]
+ </table>
+
+ [% IF by_patch_status.size %]
+ <h3>Patch Status</h3>
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% FOREACH col = by_patch_status %]
+ <tr>
+ <td>
+ <!--
+ <a class="boogle_edit"
+ href="javascript:addText('patch-status:[% col.0 FILTER js %]')"> -->
+ [% col.0 FILTER html %] <!-- </a> -->
+ [% IF col.0 == 'none' %] (unreviewed)[% END %]
+ </td>
+ <td align="right">
+ <a href="page.cgi?id=patchreport.html&product=[%- product.name FILTER uri
%]&patch-status=[% col.0 FILTER uri %]">
+ [% col.1 FILTER html %]
+ </a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ [% END %]
+
+ [% IF by_priority.size %]
+ <h3>Priority</h3>
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% FOREACH col = by_priority %]
+ <tr>
+ <td>
+ <a class="boogle_edit"
+ href="javascript:addText('priority:[% col.0 FILTER js %]')">
+ [%- col.0 FILTER html %]</a>
+ </td>
+ <td align="right">
+ <a href="[% buglink FILTER html %]&priority=
+ [%- col.0 FILTER uri %]">[% col.1 FILTER html %]</a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ [% END %]
+
+ [% IF by_severity.size %]
+ <h3>Severity</h3>
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% FOREACH col = by_severity %]
+ <tr>
+ <td>
+ <a class="boogle_edit"
+ href="javascript:addText('severity:[% col.0 FILTER js %]')">
+ [%- col.0 FILTER html %]</a>
+ </td>
+ <td align="right">
+ <a href="[% buglink FILTER html %]&bug_severity=
+ [%- col.0 FILTER uri %]">[% col.1 FILTER html %]</a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ [% END %]
+
+ <h3>Useful links</h3>
+ <ul>
+ <li>
+ <a href="http://www.gnome.org/start/unstable">Development schedule</a>
+ </li>
+ <li>
+ <a href="http://live.gnome.org/MaintainersCorner">Maintainers corner</a>
+ </li>
+ <li> [% terms.Bugzilla %]
+ <ul>
+ [% IF user.id && user.in_group('editbugs') %]
+ <li>
+ <a href="enter_bug.cgi?product=
+ [%- product.name FILTER uri %]">File a
+ [%= terms.bug %]</a>
+ <!-- (<a href="simple-bug-guide.cgi?product=[% product.name FILTER uri %]">simple form</a>) -->
+ </li>
+ [% ELSE %]
+ <li>
+ <!--
+ <a href="simple-bug-guide.cgi?product=
+ [%- product.name FILTER uri %]">File a
+ [%= terms.bug %]</a> -->
+ <a href="enter_bug.cgi?product=
+ [%- product.name FILTER uri %]">File a
+ [%= terms.bug %]</a>
+ </li>
+ [% END %]
+ [% IF user.in_group('editcomponents', product.id) %]
+ <li>
+ <a href="editproducts.cgi?action=edit&product=
+ [%- product.name FILTER uri %]">Edit this product</a>
+ </li>
+ [% ELSE %]
+ <li>
+ Show <a href="describecomponents.cgi?product=
+ [%- product.name FILTER uri %]">component
+ descriptions</a>
+ </li>
+ [% END %]
+ <li> <a href="http://live.gnome.org/Bugsquad/TriageGuide/ProductSpecificGuidelines">Triaging
guidelines</a></li>
+ <li> <a href="http://live.gnome.org/Bugsquad/ForMaintainers">Contacting bugmasters</a></li>
+ </ul>
+ </li>
+ <li> Product Info
+ <ul>
+ <li><a href="http://git.gnome.org/cgit/
+ [%- product.name FILTER lower FILTER uri %]">GNOME Git</a></li>
+ <li>
+ [% IF product.milestone_url %]
+ <a href="[% product.milestone_url FILTER html %]">
+ [% ELSE %]
+ <a href="http://bugzilla.gnome.org/">
+ [% END %]
+ [% product.name FILTER html %] homepage</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+</div>
+
+<form action="browse.cgi" method="get">
+<h1>
+Browse:
+ <select name="product">
+ [% FOREACH c = classifications %]
+ <optgroup label="[% c.name FILTER html %]">
+ [% FOREACH p = c.products %]
+ <option value="[% p.name FILTER html %]"
+ [% IF p.name == product.name && seen != 1 %]selected="selected"[% seen = 1 %]
+ [% END %]>
+ [% p.name FILTER html %]</option>
+ [% END %]</optgroup>
+ [% END %]
+ </select>
+ <input type="submit" value="Show product">
+</h1>
+</form>
+
+<p><i>[% product.description FILTER none %]</i></p>
+
+[% PROCESS gnomeblocker list = blockers_development
+ target = target_development %]
+[% PROCESS gnomeblocker list = blockers_stable
+ target = target_stable %]
+
+<form class="boogleform" id="boogle_search" action="buglist.cgi"
+ method="get">
+<div>
+ <p>Search for [% terms.bugs %] in [% product.name FILTER html %]: <br />
+ <input id="boogle_search_box" name="quicksearch" type="text"
+ value="product:"[% product.name.replace('\\[', '\[').replace('\\]', '\]').replace(':','\:')
FILTER none %]" "
+ size="50">
+ <input id="show" type="submit" value="Show">
+ <a href="page.cgi?id=quicksearch.html">[Help]</a>
+ </p>
+</div>
+</form>
+
+<script type="text/javascript">
+<!--
+ var search_box = document.getElementById('boogle_search_box');
+ search_box.focus();
+ setCaretToEnd(search_box);
+-->
+</script>
+
+<table cellpadding="3" cellspacing="0">
+<tr>
+ <th>Components</th>
+ <td> </td>
+ <th>Versions</th>
+</tr>
+<tr>
+ <td valign="top">
+
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% FOREACH col = by_component %]
+ <tr>
+ <td>
+ <a class="boogle_edit" href="javascript:addText('component:[% col.0 FILTER js %]')">[% col.0 FILTER
html %]</a>
+ </td>
+ <td align="right">
+ <a href="[% buglink FILTER html %]&component=[% col.0 FILTER uri %]">[% col.1 FILTER html %]</a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+
+ </td>
+ <td> </td>
+ <td valign="top">
+
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% FOREACH col = by_version %]
+ <tr>
+ <td>
+ <a class="boogle_edit" href="javascript:addText('version:[% col.0 FILTER js %]')">[% col.0 FILTER
html %]</a>
+ </td>
+ <td align="right">
+ <a href="[% buglink FILTER html %]&version=[% col.0 FILTER uri %]">[% col.1 FILTER html %]</a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+
+ </td>
+</tr>
+</table>
+
+<table cellpadding="3" cellspacing="0">
+<tr>
+ <th>Milestones</th>
+ <td> </td>
+ <th>NEEDINFO by last changed</th>
+</tr>
+<tr>
+ <td valign="top">
+
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% FOREACH col = by_target %]
+ <tr>
+ <td>
+ <a class="boogle_edit" href="javascript:addText('target:[% col.0 FILTER js %]')">[% col.0 FILTER
html %]</a>
+ </td>
+ <td align="right">
+ <a href="[% buglink FILTER html %]&target_milestone=[% col.0 FILTER uri %]">[% col.1 FILTER html
%]</a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+
+ </td>
+ <td> </td>
+ <td valign="top">
+
+ <table border="0" cellpadding="0" cellspacing="0" class="figures">
+ [% IF needinfo_split.F %]
+ <tr>
+ <td>>= 1 year</a></td>
+ <td align="right"><a href="buglist.cgi?product=[% product.name FILTER uri
%]&bug_status=NEEDINFO&chfieldfrom=&chfieldto=-1y">
+ [% needinfo_split.F FILTER html %]</a></td>
+ </tr>
+ [% END %]
+ [% IF needinfo_split.E %]
+ <tr>
+ <td>6 months - 1 year</a></td>
+ <td align="right"><a href="buglist.cgi?product=[% product.name FILTER uri
%]&bug_status=NEEDINFO&chfieldfrom=-1y&chfieldto=-6m">
+ [% needinfo_split.E FILTER html %]</a></td>
+ </tr>
+ [% END %]
+ [% IF needinfo_split.D %]
+ <tr>
+ <td>3 months - 6 months</a></td>
+ <td align="right"><a href="buglist.cgi?product=[% product.name FILTER uri
%]&bug_status=NEEDINFO&chfieldfrom=-6m&chfieldto=-3m">
+ [% needinfo_split.D FILTER html %]</a></td>
+ </tr>
+ [% END %]
+ [% IF needinfo_split.C %]
+ <tr>
+ <td>4 weeks - 3 months</a></td>
+ <td align="right"><a href="buglist.cgi?product=[% product.name FILTER uri
%]&bug_status=NEEDINFO&chfieldfrom=-3m&chfieldto=-4w">
+ [% needinfo_split.C FILTER html %]</a></td>
+ </tr>
+ [% END %]
+ [% IF needinfo_split.B %]
+ <tr>
+ <td>2 weeks - 4 weeks</a></td>
+ <td align="right"><a href="buglist.cgi?product=[% product.name FILTER uri
%]&bug_status=NEEDINFO&chfieldfrom=-4w&chfieldto=-2w">
+ [% needinfo_split.B FILTER html %]</a></td>
+ </tr>
+ [% END %]
+ [% IF needinfo_split.A %]
+ <tr>
+ <td>< 2 weeks ago</a></td>
+ <td align="right"><a href="buglist.cgi?product=[% product.name FILTER uri
%]&bug_status=NEEDINFO&chfieldfrom=-2w&chfieldto=">
+ [% needinfo_split.A FILTER html %]</a></td>
+ </tr>
+ [% END %]
+ </table>
+</table>
+
+<form class="fixedquery" name="fixedquery" action="buglist.cgi" method="get">
+<div>
+ <p>Find all [% terms.bugs %] marked as fixed since
+ <input name="product" type="hidden" value="[% product.name FILTER html %]">
+ <input name="bug_status" type="hidden" value="RESOLVED">
+ <input name="bug_status" type="hidden" value="VERIFIED">
+ <input name="resolution" type="hidden" value="FIXED">
+ <input name="chfield" type="hidden" value="resolution">
+ <input name="chfieldvalue" type="hidden" value="FIXED">
+ <input name="chfieldfrom" type="text" size="8" value="-7d"> (YYYY-MM-DD)
+ <input id="show" type="submit" value="Show">
+ </p>
+</div>
+</form>
+
+<h3>Developers</h3>
+[% IF product.developers.size > 0 %]
+ <table cellpadding="3" cellspacing="0">
+ [% FOREACH developer = product.developers.sort('name') %]
+ <tr>
+ <td>
+ [% IF user.id %]
+ [%# XXX - describeuser.cgi will come eventually so commenting out for now %]
+ <!-- <a href="describeuser.cgi?login=[% developer.login FILTER uri %]"> -->
+ [% END %]
+ [% PROCESS "global/user.html.tmpl" who = developer %]
+ [% IF user.id %]
+ <!-- </a> -->
+ [% END %]
+ </td>
+ </tr>
+ [% END %]
+ </table>
+[% ELSE %]
+ No users are marked as being developers of this project; please <a
href="enter_bug.cgi?product=bugzilla.gnome.org">contact the bugmasters and let us know who to mark as
such</a>.
+[% END %]
+
+[% BLOCK gnomeblocker %]
+ [% IF list.size %]
+ <table border="0" class="gnomeblocker" cellpadding="0" cellspacing="0">
+ <thead>
+ <tr>
+ <td colspan="5">Blocker [% terms.bugs %]:
+ <b> <a class="boogle_edit"
+ href="javascript:addText('cf_gnome_target:
+ [%- target FILTER js %]')">
+ GNOME [% target FILTER html %]</a></b>
+ (these must be fixed before/in the specified GNOME version)
+ </td>
+ </tr>
+ </thead>
+ [% FOREACH bug = list %]
+ <tr class="[%+ IF loop.count() % 2 == 0 %]bz_row_even[% ELSE %]bz_row_odd[% END %]">
+ <td align="center">
+ <a href="show_bug.cgi?id=[% bug.bug_id FILTER uri %]">[% bug.bug_id FILTER html %]</a>
+ </td>
+ <td>
+ <a href="[% buglink FILTER html %]&gnome_target=[% target FILTER uri %]">[% bug.product
FILTER html %]</a>
+ </td>
+ <td align="center">[% bug.bug_status.truncate(4) FILTER html %]</td>
+ <td align="center">[% bug.bug_severity.truncate(3) FILTER html %]</td>
+ <td>
+ <a href="show_bug.cgi?id=[% bug.bug_id FILTER uri %]">[% bug.short_desc.truncate(70, '...')
FILTER html %]</a>
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ [% END %]
+[% END %]
+
+[% PROCESS global/footer.html.tmpl %]
diff --git a/web/README b/web/README
new file mode 100644
index 0000000..2345641
--- /dev/null
+++ b/web/README
@@ -0,0 +1,7 @@
+Web-accessible files, like JavaScript, CSS, and images go in this
+directory. You can reference them directly in your HTML. For example,
+if you have a file called "style.css" and your extension is called
+"Foo", you would put it in "extensions/Foo/web/style.css", and then
+you could link to it in HTML like:
+
+<link href="extensions/Foo/web/style.css" rel="stylesheet" type="text/css">
\ No newline at end of file
diff --git a/web/browse.css b/web/browse.css
new file mode 100644
index 0000000..693b2cc
--- /dev/null
+++ b/web/browse.css
@@ -0,0 +1,35 @@
+#product_summary {
+ float: right;
+ -moz-border-radius-topleft: 20px;
+ -moz-border-radius-bottomleft: 20px;
+ margin: 0;
+ background-color: #ffeafd;
+ padding: 0 1em 1em 1em;
+}
+
+#product_summary h3 {
+ padding: 1em 0 0 0;
+}
+
+#product_summary ul {
+ padding: 0 0 0 1em;
+ margin: 0;
+}
+
+table.gnomeblocker td {
+ padding: 3px;
+}
+
+table.gnomeblocker thead {
+ background-color: #babdb6;
+}
+
+table.figures td {
+ padding: 0 3px 0 3px;
+ margin: 0;
+}
+
+table.figures tr { margin: 0; }
+
+a.boogle_edit { text-decoration: none; }
+a.boogle_edit:hover { color: #4e9a06 }
diff --git a/web/browse.js b/web/browse.js
new file mode 100644
index 0000000..8d47a38
--- /dev/null
+++ b/web/browse.js
@@ -0,0 +1,116 @@
+function setCaretToEnd (control) {
+ if (control.createTextRange) {
+ var range = control.createTextRange();
+ range.collapse(false);
+ range.select();
+ }
+ else if (control.setSelectionRange) {
+ control.focus();
+ var length = control.value.length;
+ control.setSelectionRange(length, length);
+ }
+}
+
+function addText(text) {
+ /* Get the querytype */
+ var colonloc = text.indexOf(":");
+ var querytype;
+ var searchBox = document.getElementById('boogle_search_box');
+
+ if (colonloc != -1)
+ querytype = text.substring(0,colonloc);
+ else {
+ /* comment or +critical_warning */
+ var oldvalue = searchBox.value;
+ var location = oldvalue.indexOf(text);
+
+ if (location == -1) {
+ /* This is new; just prepend it */
+ searchBox.value = text + " " + oldvalue;
+
+ } else {
+ searchBox.value = oldvalue.substring(0, location)
+ + oldvalue.substring(location + text.length + 1);
+ }
+ return;
+ } /* if (colonloc != -1) */
+
+ /* Quote the value, if needed */
+ var value = text.substring(colonloc+1);
+ if (value.match(/^[A-Za-z0-9+][A-Za-z0-9_\.-]*$/)) {
+ } else {
+ if (colonloc != -1)
+ text = querytype + ':' + '"' + value + '"';
+ else
+ text = '"' + value + '"';
+
+ value = '"' + value + '"';
+ }
+
+ /* Find if this querytype is already in the boogle query */
+ var oldvalue = searchBox.value;
+ var location = oldvalue.search(querytype);
+
+ if (location == -1) {
+ /* This is a new query type; just prepend it */
+ searchBox.value = text + " " + oldvalue;
+ } else {
+ /* This querytype already appears, so doing an and with a different
+ * value does not make any sense. So, just add it as a comma separated
+ * list item if it is not already present; otherwise, remove it.
+ */
+
+ if (oldvalue.search(value) == -1) {
+ /* prepend the new value */
+ searchBox.value = oldvalue.substring(0,location) + text + ","
+ + oldvalue.substring(location + querytype.length + 1);
+
+ } else {
+
+ /* value is already in list, remove it */
+ var vlocation = oldvalue.indexOf(value);
+
+ /* how many values are there for the current querytype? */
+ var queryvalues = oldvalue.substring(location + querytype.length + 1);
+ if (queryvalues.indexOf(":") >= 0) {
+ /* other querytypes follow, discard them */
+ queryvalues = queryvalues.substring(0,queryvalues.indexOf(":") - 1);
+ }
+
+ /* count the commas (up to the next colon) */
+ var numberofvalues = 1;
+ while ( queryvalues.indexOf(",") >= 0 ) {
+ queryvalues = queryvalues.substring(queryvalues.indexOf(",")+1);
+ numberofvalues += 1;
+ }
+
+ if (numberofvalues == 1) {
+ /* remove the querytype name and value */
+ searchBox.value = oldvalue.substring(0,location)
+ + oldvalue.substring(vlocation + value.length + 1);
+ } else {
+ /* only remove one value */
+
+ /* last of all value in querytype?
+ then do not remove the trailing but the preceding character */
+
+ if ( (vlocation + value.length == oldvalue.length) /*last in string? */
+ ||
+ (oldvalue.substring(vlocation + value.length, /*trailing space? */
+ vlocation + value.length + 1) == ' ')
+ )
+ {
+ searchBox.value = oldvalue.substring(0,vlocation-1)
+ + oldvalue.substring(vlocation + value.length);
+
+ } else {
+ searchBox.value = oldvalue.substring(0,vlocation)
+ + oldvalue.substring(vlocation + value.length + 1);
+ }
+ } /* if(numberofvalues == 1) */
+
+ } /* if(oldvalue.search(value) == -1) */
+
+ } /* if (location == -1) */
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]