aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Component.pm15
-rw-r--r--Bugzilla/Product.pm17
-rw-r--r--Bugzilla/User.pm71
-rwxr-xr-xconfig.cgi7
-rwxr-xr-xduplicates.cgi3
-rwxr-xr-xenter_bug.cgi18
-rw-r--r--globals.pl107
-rwxr-xr-xquery.cgi31
-rwxr-xr-xreports.cgi5
-rwxr-xr-xrequest.cgi34
-rw-r--r--template/en/default/config.js.tmpl8
-rw-r--r--template/en/default/config.rdf.tmpl60
-rw-r--r--template/en/default/filterexceptions.pl2
-rw-r--r--template/en/default/global/choose-classification.html.tmpl16
-rw-r--r--template/en/default/reports/duplicates.html.tmpl8
-rw-r--r--template/en/default/request/queue.html.tmpl21
16 files changed, 150 insertions, 273 deletions
diff --git a/Bugzilla/Component.pm b/Bugzilla/Component.pm
index a3278dea9..dfbcf00a8 100644
--- a/Bugzilla/Component.pm
+++ b/Bugzilla/Component.pm
@@ -121,11 +121,11 @@ sub get_components_by_product {
SELECT id FROM components
WHERE product_id = ?}, undef, $product_id);
- my $components;
+ my @components;
foreach my $id (@$ids) {
- $components->{$id} = new Bugzilla::Component($id);
+ push @components, new Bugzilla::Component($id);
}
- return $components;
+ return @components;
}
1;
@@ -151,8 +151,7 @@ Bugzilla::Component - Bugzilla product component class.
my $default_assignee = $component->default_assignee;
my $default_qa_contact = $component->default_qa_contact;
- my $hash_ref = Bugzilla::Component::get_components_by_product(1);
- my $component = $hash_ref->{1};
+ my @components = Bugzilla::Component::get_components_by_product($id);
=head1 DESCRIPTION
@@ -184,13 +183,11 @@ Component.pm represents a Product Component object.
=item C<get_components_by_product($product_id)>
- Description: Returns all Bugzilla components that belong to the
- supplied product.
+ Description: Returns all components that belong to the supplied product.
Params: $product_id - Integer with a Bugzilla product id.
- Returns: A hash with component id as key and Bugzilla::Component
- object as value.
+ Returns: An array of Bugzilla::Component objects.
=back
diff --git a/Bugzilla/Product.pm b/Bugzilla/Product.pm
index c257bd4ce..514620258 100644
--- a/Bugzilla/Product.pm
+++ b/Bugzilla/Product.pm
@@ -98,8 +98,9 @@ sub components {
my $self = shift;
if (!defined $self->{components}) {
- $self->{components} =
+ my @components =
Bugzilla::Component::get_components_by_product($self->id);
+ $self->{components} = \@components;
}
return $self->{components};
}
@@ -247,11 +248,11 @@ Bugzilla::Product - Bugzilla product class.
my $product = new Bugzilla::Product(1);
my $product = new Bugzilla::Product('AcmeProduct');
- my $components = $product->components();
+ my @components = $product->components();
my $classification = $product->classification();
- my $hash_ref = $product->group_controls();
- my @array_ref = $product->milestones();
- my @array_ref = $product->versions();
+ my $groups_controls = $product->group_controls();
+ my @milestones = $product->milestones();
+ my @versions = $product->versions();
my $bugcount = $product->bug_count();
my $id = $product->id;
@@ -290,12 +291,12 @@ Product.pm represents a product object.
=item C<components()>
- Description: Returns a hash with all product components.
+ Description: Returns an array of component objects belonging to
+ the product.
Params: none.
- Returns: A hash where component id is the hash key and
- Bugzilla::Component object is the hash value.
+ Returns: An array of Bugzilla::Component object.
=item C<classification()>
diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm
index 87f894752..2c6c6b0b5 100644
--- a/Bugzilla/User.pm
+++ b/Bugzilla/User.pm
@@ -61,8 +61,6 @@ use constant USER_MATCH_SUCCESS => 1;
use constant MATCH_SKIP_CONFIRM => 1;
-use constant GET_PRODUCTS_BY_ID => 1;
-
################################################################################
# Functions
################################################################################
@@ -420,13 +418,12 @@ sub can_see_bug {
sub get_selectable_products {
my ($self, $by_id) = @_;
- if (defined $self->{SelectableProducts}) {
- my %list = @{$self->{SelectableProducts}};
- return \%list if $by_id;
- return values(%list);
+ if (defined $self->{selectable_products}) {
+ return $self->{selectable_products};
}
- my $query = "SELECT id, name " .
+ my $dbh = Bugzilla->dbh;
+ my $query = "SELECT id " .
"FROM products " .
"LEFT JOIN group_control_map " .
"ON group_control_map.product_id = products.id ";
@@ -439,38 +436,31 @@ sub get_selectable_products {
$query .= "AND group_id NOT IN(" .
$self->groups_as_string . ") " .
"WHERE group_id IS NULL ORDER BY name";
- my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare($query);
- $sth->execute();
- my @products = ();
- while (my @row = $sth->fetchrow_array) {
- push(@products, @row);
+
+ my $prod_ids = $dbh->selectcol_arrayref($query);
+ my @products;
+ foreach my $prod_id (@$prod_ids) {
+ push(@products, new Bugzilla::Product($prod_id));
}
- $self->{SelectableProducts} = \@products;
- my %list = @products;
- return \%list if $by_id;
- return values(%list);
+ $self->{selectable_products} = \@products;
+ return $self->{selectable_products};
}
-sub get_selectable_classifications ($) {
+sub get_selectable_classifications {
my ($self) = @_;
if (defined $self->{selectable_classifications}) {
return $self->{selectable_classifications};
}
-
- my $products = $self->get_selectable_products(GET_PRODUCTS_BY_ID);
-
- my $selectable_classifications;
-
- foreach my $prod_id (keys %$products) {
- my $product = new Bugzilla::Product($prod_id);
-
- $selectable_classifications->{$product->classification_id} =
- $product->classification;
+
+ my $products = $self->get_selectable_products;
+
+ my $class;
+ foreach my $product (@$products) {
+ $class->{$product->classification_id} ||= $product->classification;
}
- $self->{selectable_classifications} =
- [values %$selectable_classifications];
+ my @sorted_class = sort {lc($a->name) cmp lc($b->name)} (values %$class);
+ $self->{selectable_classifications} = \@sorted_class;
return $self->{selectable_classifications};
}
@@ -1450,22 +1440,23 @@ care of by the constructor. However, when updating the email address, the
user may be placed into different groups, based on a new email regexp. This
method should be called in such a case to force reresolution of these groups.
-=item C<get_selectable_products(by_id)>
+=item C<get_selectable_products>
+
+ Description: Returns all products the user is allowed to access.
+
+ Params: none
-Returns an alphabetical list of product names from which
-the user can select bugs. If the $by_id parameter is true, it returns
-a hash where the keys are the product ids and the values are the
-product names.
+ Returns: An array of product objects, sorted by the product name.
=item C<get_selectable_classifications>
- Description: Returns the classifications that a user, according his
- groups ownership, can select to entering, serch, view or
- edit a bug.
+ Description: Returns all classifications containing at least one product
+ the user is allowed to view.
- Params: none.
+ Params: none
- Returns: Bugzilla::Classification objects values.
+ Returns: An array of Bugzilla::Classification objects, sorted by
+ the classification name.
=item C<get_userlist>
diff --git a/config.cgi b/config.cgi
index e3ecef3ff..a21fc7843 100755
--- a/config.cgi
+++ b/config.cgi
@@ -64,11 +64,8 @@ $vars->{'keyword'} = \@::legal_keywords;
$vars->{'resolution'} = \@::legal_resolution;
$vars->{'status'} = \@::legal_bug_status;
-# Include lists of products, components, versions, and target milestones.
-my $selectables = GetSelectableProductHash();
-foreach my $selectable (keys %$selectables) {
- $vars->{$selectable} = $selectables->{$selectable};
-}
+# Include a list of product objects.
+$vars->{'products'} = Bugzilla->user->get_selectable_products;
# Create separate lists of open versus resolved statuses. This should really
# be made part of the configuration.
diff --git a/duplicates.cgi b/duplicates.cgi
index 2aa0df263..6348748bf 100755
--- a/duplicates.cgi
+++ b/duplicates.cgi
@@ -265,8 +265,7 @@ $vars->{'openonly'} = $openonly;
$vars->{'reverse'} = $reverse;
$vars->{'format'} = $cgi->param('format');
$vars->{'query_products'} = \@query_products;
-my @selectable_products = GetSelectableProducts();
-$vars->{'products'} = \@selectable_products;
+$vars->{'products'} = Bugzilla->user->get_selectable_products;
my $format = $template->get_format("reports/duplicates",
diff --git a/enter_bug.cgi b/enter_bug.cgi
index 4a32a32e7..66e182423 100755
--- a/enter_bug.cgi
+++ b/enter_bug.cgi
@@ -80,21 +80,13 @@ if (!defined $product || $product eq "") {
}
if (!$cgi->param('classification')) {
- my %classdesc;
- my %classifications;
-
- foreach my $c (GetSelectableClassifications()) {
- $classdesc{$c} = $::classdesc{$c};
- $classifications{$c} = $::classifications{$c};
- }
+ my $classifications = Bugzilla->user->get_selectable_classifications();
- my $classification_size = scalar(keys %classdesc);
- if ($classification_size == 0) {
+ if (scalar(@$classifications) == 0) {
ThrowUserError("no_products");
}
- elsif ($classification_size > 1) {
- $vars->{'classdesc'} = \%classdesc;
- $vars->{'classifications'} = \%classifications;
+ elsif (scalar(@$classifications) > 1) {
+ $vars->{'classifications'} = $classifications;
$vars->{'target'} = "enter_bug.cgi";
$vars->{'format'} = $cgi->param('format');
@@ -106,7 +98,7 @@ if (!defined $product || $product eq "") {
|| ThrowTemplateError($template->error());
exit;
}
- $cgi->param(-name => 'classification', -value => (keys %classdesc)[0]);
+ $cgi->param(-name => 'classification', -value => @$classifications[0]->name);
}
my %products;
diff --git a/globals.pl b/globals.pl
index 7f3d9dd13..46ceebc74 100644
--- a/globals.pl
+++ b/globals.pl
@@ -518,113 +518,6 @@ sub GetEnterableProducts {
return (@products);
}
-
-#
-# This function returns an alphabetical list of product names to which
-# the user can enter bugs. If the $by_id parameter is true, also retrieves IDs
-# and pushes them onto the list as id, name [, id, name...] for easy slurping
-# into a hash by the calling code.
-sub GetSelectableProducts {
- my ($by_id,$by_classification) = @_;
-
- my $extra_sql = $by_id ? "id, " : "";
-
- my $extra_from_sql = $by_classification ? " INNER JOIN classifications"
- . " ON classifications.id = products.classification_id" : "";
-
- my $query = "SELECT $extra_sql products.name " .
- "FROM products $extra_from_sql " .
- "LEFT JOIN group_control_map " .
- "ON group_control_map.product_id = products.id ";
- if (Param('useentrygroupdefault')) {
- $query .= "AND group_control_map.entry != 0 ";
- } else {
- $query .= "AND group_control_map.membercontrol = " .
- CONTROLMAPMANDATORY . " ";
- }
- if (%{Bugzilla->user->groups}) {
- $query .= "AND group_id NOT IN(" .
- join(',', values(%{Bugzilla->user->groups})) . ") ";
- }
- $query .= "WHERE group_id IS NULL ";
- if ($by_classification) {
- $query .= "AND classifications.name = ";
- $query .= SqlQuote($by_classification) . " ";
- }
- $query .= "ORDER BY name";
- PushGlobalSQLState();
- SendSQL($query);
- my @products = ();
- push(@products, FetchSQLData()) while MoreSQLData();
- PopGlobalSQLState();
- return (@products);
-}
-
-# GetSelectableProductHash
-# returns a hash containing
-# legal_products => an enterable product list
-# legal_(components|versions|milestones) =>
-# the list of components, versions, and milestones of enterable products
-# (components|versions|milestones)_by_product
-# => a hash of component lists for each enterable product
-# Milestones only get returned if the usetargetmilestones parameter is set.
-sub GetSelectableProductHash {
- # The hash of selectable products and their attributes that gets returned
- # at the end of this function.
- my $selectables = {};
-
- my %products = GetSelectableProducts(1);
-
- $selectables->{legal_products} = [sort values %products];
-
- # Run queries that retrieve the list of components, versions,
- # and target milestones (if used) for the selectable products.
- my @tables = qw(components versions);
- push(@tables, 'milestones') if Param('usetargetmilestone');
-
- PushGlobalSQLState();
- foreach my $table (@tables) {
- my %values;
- my %values_by_product;
-
- if (scalar(keys %products)) {
- # Why oh why can't we standardize on these names?!?
- my $fld = ($table eq "components" ? "name" : "value");
-
- my $query = "SELECT $fld, product_id FROM $table WHERE product_id " .
- "IN (" . join(",", keys %products) . ") ORDER BY $fld";
- SendSQL($query);
-
- while (MoreSQLData()) {
- my ($name, $product_id) = FetchSQLData();
- next unless $name;
- $values{$name} = 1;
- push @{$values_by_product{$products{$product_id}}}, $name;
- }
- }
-
- $selectables->{"legal_$table"} = [sort keys %values];
- $selectables->{"${table}_by_product"} = \%values_by_product;
- }
- PopGlobalSQLState();
-
- return $selectables;
-}
-
-#
-# This function returns an alphabetical list of classifications that has products the user can enter bugs.
-sub GetSelectableClassifications {
- my @selectable_classes = ();
-
- foreach my $c (sort keys %::classdesc) {
- if ( scalar(GetSelectableProducts(0,$c)) > 0) {
- push(@selectable_classes,$c);
- }
- }
- return (@selectable_classes);
-}
-
-
sub ValidatePassword {
# Determines whether or not a password is valid (i.e. meets Bugzilla's
# requirements for length and content).
diff --git a/query.cgi b/query.cgi
index 2f73f0602..4a414d46c 100755
--- a/query.cgi
+++ b/query.cgi
@@ -66,7 +66,8 @@ if ($cgi->param("GoAheadAndLogIn")) {
Bugzilla->login();
}
-my $userid = Bugzilla->user->id;
+my $user = Bugzilla->user;
+my $userid = $user->id;
# Backwards compatibility hack -- if there are any of the old QUERY_*
# cookies around, and we are logged in, then move them into the database
@@ -219,23 +220,26 @@ GetVersionTable();
# if using groups for entry, then we don't want people to see products they
# don't have access to. Remove them from the list.
-my @products = ();
+my @selectable_product_objects = @{$user->get_selectable_products};
+
my %component_set;
my %version_set;
my %milestone_set;
-foreach my $p (GetSelectableProducts()) {
+# extract product names
+my @products = map { $_->name } @selectable_product_objects;
+
+foreach my $prod_name (@products) {
# We build up boolean hashes in the "-set" hashes for each of these things
# before making a list because there may be duplicates names across products.
- push @products, $p;
- if ($::components{$p}) {
- foreach my $c (@{$::components{$p}}) {
+ if ($::components{$prod_name}) {
+ foreach my $c (@{$::components{$prod_name}}) {
$component_set{$c} = 1;
}
}
- foreach my $v (@{$::versions{$p}}) {
+ foreach my $v (@{$::versions{$prod_name}}) {
$version_set{$v} = 1;
}
- foreach my $m (@{$::target_milestone{$p}}) {
+ foreach my $m (@{$::target_milestone{$prod_name}}) {
$milestone_set{$m} = 1;
}
}
@@ -296,11 +300,16 @@ $vars->{'product'} = \@products;
if (Param('useclassification')) {
my @classifications = ();
- foreach my $c (GetSelectableClassifications()) {
+ my $class = $user->get_selectable_classifications;
+ foreach my $c (@$class) {
+ # Extract the name of products being in this classification.
+ my @prod_in_class
+ = grep { $_->classification_id == $c->id } @selectable_product_objects;
+ @prod_in_class = map { $_->name } @prod_in_class;
# Create hash to hold attributes for each classification.
my %classification = (
- 'name' => $c,
- 'products' => [ GetSelectableProducts(0,$c) ]
+ 'name' => $c->name,
+ 'products' => \@prod_in_class
);
# Assign hash back to classification array.
push @classifications, \%classification;
diff --git a/reports.cgi b/reports.cgi
index 7274ea8e2..c060045eb 100755
--- a/reports.cgi
+++ b/reports.cgi
@@ -54,7 +54,7 @@ use Bugzilla;
# If we're using bug groups for products, we should apply those restrictions
# to viewing reports, as well. Time to check the login in that case.
-Bugzilla->login();
+my $user = Bugzilla->login();
GetVersionTable();
@@ -66,7 +66,8 @@ my $template = Bugzilla->template;
# We only want those products that the user has permissions for.
my @myproducts;
push( @myproducts, "-All-");
-push( @myproducts, GetSelectableProducts());
+# Extract product names from objects and add them to the list.
+push( @myproducts, map { $_->name } @{$user->get_selectable_products} );
if (! defined $cgi->param('product')) {
diff --git a/request.cgi b/request.cgi
index b0f45b1cc..c7705f38d 100755
--- a/request.cgi
+++ b/request.cgi
@@ -27,28 +27,26 @@
# Make it harder for us to do dangerous things in Perl.
use strict;
-# Include the Bugzilla CGI and general utility library.
use lib qw(.);
require "globals.pl";
use Bugzilla;
-# Use Bugzilla's Request module which contains utilities for handling requests.
use Bugzilla::Flag;
use Bugzilla::FlagType;
-
-# use Bugzilla's User module which contains utilities for handling users.
use Bugzilla::User;
-use vars qw($template $vars @legal_product @legal_components %components);
+use vars qw($template $vars);
# Make sure the user is logged in.
-Bugzilla->login();
+my $user = Bugzilla->login();
+my $userid = $user->id;
+
+my $cgi = Bugzilla->cgi;
+
################################################################################
# Main Body Execution
################################################################################
-my $cgi = Bugzilla->cgi;
-
my $fields;
$fields->{'requester'}->{'type'} = 'single';
# If the user doesn't restrict his search to requests from the wind
@@ -116,17 +114,17 @@ sub queue {
LEFT JOIN bug_group_map AS bgmap
ON bgmap.bug_id = bugs.bug_id
AND bgmap.group_id NOT IN (" .
- join(', ', (-1, values(%{Bugzilla->user->groups}))) . ")
+ join(', ', (-1, values(%{$user->groups}))) . ")
LEFT JOIN cc AS ccmap
- ON ccmap.who = $::userid
- AND ccmap.bug_id = bugs.bug_id
+ ON ccmap.who = $userid
+ AND ccmap.bug_id = bugs.bug_id
" .
# Weed out bug the user does not have access to
" WHERE ((bgmap.group_id IS NULL) OR
(ccmap.who IS NOT NULL AND cclist_accessible = 1) OR
- (bugs.reporter = $::userid AND bugs.reporter_accessible = 1) OR
- (bugs.assigned_to = $::userid))";
+ (bugs.reporter = $userid AND bugs.reporter_accessible = 1) OR
+ (bugs.assigned_to = $userid))";
# Non-deleted flags only
$query .= " AND flags.is_active = 1 ";
@@ -279,15 +277,7 @@ sub queue {
SendSQL("SELECT DISTINCT(name) FROM flagtypes ORDER BY name");
push(@types, FetchOneColumn()) while MoreSQLData();
- # products and components and the function used to modify the components
- # menu when the products menu changes; used by the template to populate
- # the menus and keep the components menu consistent with the products menu
- GetVersionTable();
- my $selectable = GetSelectableProductHash();
- $vars->{'products'} = $selectable->{legal_products};
- $vars->{'components'} = $selectable->{legal_components};
- $vars->{'components_by_product'} = $selectable->{components_by_product};
-
+ $vars->{'products'} = $user->get_selectable_products;
$vars->{'excluded_columns'} = \@excluded_columns;
$vars->{'group_field'} = $form_group;
$vars->{'requests'} = \@requests;
diff --git a/template/en/default/config.js.tmpl b/template/en/default/config.js.tmpl
index e3ec91435..00ba58983 100644
--- a/template/en/default/config.js.tmpl
+++ b/template/en/default/config.js.tmpl
@@ -73,10 +73,10 @@ var component = new Object();
var version = new Object();
var target_milestone = new Object();
-[% FOREACH p = legal_products %]
- component['[% p FILTER js %]'] = [ [% FOREACH x = components_by_product.$p %]'[% x FILTER js %]', [% END %] ];
- version['[% p FILTER js %]'] = [ [% FOREACH x = versions_by_product.$p %]'[% x FILTER js %]', [% END %] ];
- target_milestone['[% p FILTER js %]'] = [ [% FOREACH x = milestones_by_product.$p %]'[% x FILTER js %]', [% END %] ];
+[% FOREACH p = products %]
+ component['[% p.name FILTER js %]'] = [ [% FOREACH x = p.components %]'[% x.name FILTER js %]', [% END %] ];
+ version['[% p.name FILTER js %]'] = [ [% FOREACH x = p.versions %]'[% x.name FILTER js %]', [% END %] ];
+ target_milestone['[% p.name FILTER js %]'] = [ [% FOREACH x = p.milestones %]'[% x.name FILTER js %]', [% END %] ];
[% END %]
// Product and Component Exceptions
diff --git a/template/en/default/config.rdf.tmpl b/template/en/default/config.rdf.tmpl
index 27a7ba3a0..4c1047f50 100644
--- a/template/en/default/config.rdf.tmpl
+++ b/template/en/default/config.rdf.tmpl
@@ -105,23 +105,23 @@
<bz:products>
<Seq>
- [% FOREACH product = legal_products %]
+ [% FOREACH product = products %]
<li>
- <bz:product rdf:about="[% Param('urlbase') %]product.cgi?name=[% product FILTER uri %]">
- <bz:name>[% product FILTER html %]</bz:name>
+ <bz:product rdf:about="[% Param('urlbase') %]product.cgi?name=[% product.name FILTER uri %]">
+ <bz:name>[% product.name FILTER html %]</bz:name>
<bz:components>
<Seq>
- [% FOREACH component = components_by_product.$product %]
- <li resource="[% Param('urlbase') %]component.cgi?name=[% component FILTER uri %]"/>
+ [% FOREACH component = product.components %]
+ <li resource="[% Param('urlbase') %]component.cgi?name=[% component.name FILTER uri %]"/>
[% END %]
</Seq>
</bz:components>
<bz:versions>
<Seq>
- [% FOREACH version = versions_by_product.$product %]
- <li resource="[% Param('urlbase') %]version.cgi?name=[% version FILTER uri %]"/>
+ [% FOREACH version = product.versions %]
+ <li resource="[% Param('urlbase') %]version.cgi?name=[% version.name FILTER uri %]"/>
[% END %]
</Seq>
</bz:versions>
@@ -129,8 +129,8 @@
[% IF Param('usetargetmilestone') %]
<bz:target_milestones>
<Seq>
- [% FOREACH milestone = milestones_by_product.$product %]
- <li resource="[% Param('urlbase') %]milestone.cgi?name=[% milestone FILTER uri %]"/>
+ [% FOREACH milestone = product.milestones %]
+ <li resource="[% Param('urlbase') %]milestone.cgi?name=[% milestone.name FILTER uri %]"/>
[% END %]
</Seq>
</bz:target_milestones>
@@ -144,24 +144,28 @@
<bz:components>
<Seq>
- [% FOREACH item = legal_components %]
- <li>
- <bz:component rdf:about="[% Param('urlbase') %]component.cgi?name=[% item FILTER uri %]">
- <bz:name>[% item FILTER html %]</bz:name>
- </bz:component>
- </li>
+ [% FOREACH product = products %]
+ [% FOREACH component = product.components %]
+ <li>
+ <bz:component rdf:about="[% Param('urlbase') %]component.cgi?name=[% component.name FILTER uri %]">
+ <bz:name>[% component.name FILTER html %]</bz:name>
+ </bz:component>
+ </li>
+ [% END %]
[% END %]
</Seq>
</bz:components>
<bz:versions>
<Seq>
- [% FOREACH item = legal_versions %]
- <li>
- <bz:version rdf:about="[% Param('urlbase') %]version.cgi?name=[% item FILTER uri %]">
- <bz:name>[% item FILTER html %]</bz:name>
- </bz:version>
- </li>
+ [% FOREACH product = products %]
+ [% FOREACH version = product.versions %]
+ <li>
+ <bz:version rdf:about="[% Param('urlbase') %]version.cgi?name=[% version.name FILTER uri %]">
+ <bz:name>[% version.name FILTER html %]</bz:name>
+ </bz:version>
+ </li>
+ [% END %]
[% END %]
</Seq>
</bz:versions>
@@ -169,12 +173,14 @@
[% IF Param('usetargetmilestone') %]
<bz:target_milestones>
<Seq>
- [% FOREACH item = legal_milestones %]
- <li>
- <bz:target_milestone rdf:about="[% Param('urlbase') %]milestone.cgi?name=[% item FILTER uri %]">
- <bz:name>[% item FILTER html %]</bz:name>
- </bz:target_milestone>
- </li>
+ [% FOREACH product = products %]
+ [% FOREACH milestone = product.milestones %]
+ <li>
+ <bz:target_milestone rdf:about="[% Param('urlbase') %]milestone.cgi?name=[% milestone.name FILTER uri %]">
+ <bz:name>[% milestone.name FILTER html %]</bz:name>
+ </bz:target_milestone>
+ </li>
+ [% END %]
[% END %]
</Seq>
</bz:target_milestones>
diff --git a/template/en/default/filterexceptions.pl b/template/en/default/filterexceptions.pl
index 8ed71f008..28ea2c7ba 100644
--- a/template/en/default/filterexceptions.pl
+++ b/template/en/default/filterexceptions.pl
@@ -244,7 +244,7 @@
],
'global/choose-classification.html.tmpl' => [
- 'classdesc.$p',
+ 'class.description',
],
'global/choose-product.html.tmpl' => [
diff --git a/template/en/default/global/choose-classification.html.tmpl b/template/en/default/global/choose-classification.html.tmpl
index b2021f234..627ac860f 100644
--- a/template/en/default/global/choose-classification.html.tmpl
+++ b/template/en/default/global/choose-classification.html.tmpl
@@ -18,8 +18,8 @@
#%]
[%# INTERFACE:
- # classdesc: hash. May be empty. The hash keys are the classifications, and the values
- # are their descriptions.
+ # classifications: an array of classification objects containing
+ # at least one product accessible by the user.
#%]
[% IF target == "enter_bug.cgi" %]
@@ -45,21 +45,19 @@
</tr>
[% END %]
-[% FOREACH p = classdesc.keys.sort %]
- [% IF classifications.$p.size > 0 %]
+[% FOREACH class = classifications %]
<tr>
<th align="right" valign="top">
- <a href="[% target FILTER url_quote %]?classification=[% p FILTER url_quote -%]
+ <a href="[% target FILTER url_quote %]?classification=[% class.name FILTER url_quote -%]
[%- IF cloned_bug_id %]&amp;cloned_bug_id=[% cloned_bug_id FILTER url_quote %][% END -%]
[%- IF format %]&amp;format=[% format FILTER url_quote %][% END %]">
- [% p FILTER html %]</a>:
+ [% class.name FILTER html %]</a>:
</th>
- [% IF classdesc.$p %]
- <td valign="top">&nbsp;[% classdesc.$p %]</td>
+ [% IF class.description %]
+ <td valign="top">&nbsp;[% class.description %]</td>
[% END %]
</tr>
- [% END %]
[% END %]
</table>
diff --git a/template/en/default/reports/duplicates.html.tmpl b/template/en/default/reports/duplicates.html.tmpl
index 5cbf84fe2..897bbf17f 100644
--- a/template/en/default/reports/duplicates.html.tmpl
+++ b/template/en/default/reports/duplicates.html.tmpl
@@ -20,7 +20,7 @@
#%]
[%# INTERFACE:
- # products: list of strings. The products this user can see.
+ # products: an array of product objects this user can see.
#
# sortby: string. the column on which we are sorting the buglist.
# reverse: boolean. True if we are reversing the current sort.
@@ -84,9 +84,9 @@
<td rowspan="4" valign="top">
<select name="product" size="5" multiple="multiple">
[% FOREACH p = products %]
- <option name="[% p FILTER html %]"
- [% " selected" IF lsearch(query_products, p) != -1 %]
- >[% p FILTER html %]</option>
+ <option name="[% p.name FILTER html %]"
+ [% " selected" IF lsearch(query_products, p.name) != -1 %]
+ >[% p.name FILTER html %]</option>
[% END %]
</select>
</td>
diff --git a/template/en/default/request/queue.html.tmpl b/template/en/default/request/queue.html.tmpl
index a6edcd8a3..73bbd8195 100644
--- a/template/en/default/request/queue.html.tmpl
+++ b/template/en/default/request/queue.html.tmpl
@@ -30,9 +30,9 @@
var first_load = 1; // is this the first time we load the page?
var last_sel = []; // caches last selection
var cpts = new Array();
- [% FOREACH p = products %]
- cpts['[% p FILTER js %]'] = [
- [%- FOREACH item = components_by_product.$p %]'[% item FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
+ [% FOREACH prod = products %]
+ cpts['[% prod.name FILTER js %]'] = [
+ [%- FOREACH comp = prod.components %]'[% comp.name FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
[% END %]
[% END %]
@@ -58,9 +58,10 @@
<td>
<select name="product" onchange="selectProduct(this.form, 'product', 'component', 'Any');">
<option value="">Any</option>
- [% FOREACH item = products %]
- <option value="[% item FILTER html %]"
- [% "selected" IF cgi.param('product') == item %]>[% item FILTER html %]</option>
+ [% FOREACH prod = products %]
+ <option value="[% prod.name FILTER html %]"
+ [% "selected" IF cgi.param('product') == prod.name %]>
+ [% prod.name FILTER html %]</option>
[% END %]
</select>
</td>
@@ -92,9 +93,11 @@
<td>
<select name="component">
<option value="">Any</option>
- [% FOREACH item = components %]
- <option value="[% item FILTER html %]" [% "selected" IF cgi.param('component') == item %]>
- [% item FILTER html %]</option>
+ [% FOREACH prod = products %]
+ [% FOREACH comp = prod.components %]
+ <option value="[% comp.name FILTER html %]" [% "selected" IF cgi.param('component') == comp.name %]>
+ [% comp.name FILTER html %]</option>
+ [% END %]
[% END %]
</select>
</td>