Bug 286158: Remove GetSelectableProducts() from globals.pl and use Bugzilla::User::get_selectable_products() instead - Patch by Fr�d�ric Buclin <LpSolit@gmail.com> r=joel,kiko a=justdave

This commit is contained in:
lpsolit%gmail.com 2005-09-02 21:12:10 +00:00
Родитель f8ad8c3f24
Коммит 426b5bd478
16 изменённых файлов: 150 добавлений и 273 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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()>

Просмотреть файл

@ -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>
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.
Description: Returns all products the user is allowed to access.
Params: none
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>

Просмотреть файл

@ -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.

Просмотреть файл

@ -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",

Просмотреть файл

@ -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;

Просмотреть файл

@ -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).

Просмотреть файл

@ -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;

Просмотреть файл

@ -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')) {

Просмотреть файл

@ -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;

Просмотреть файл

@ -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

Просмотреть файл

@ -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>

Просмотреть файл

@ -244,7 +244,7 @@
],
'global/choose-classification.html.tmpl' => [
'classdesc.$p',
'class.description',
],
'global/choose-product.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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>