use Bugzilla::Product;
use Bugzilla::Component;
+use List::Util qw(reduce);
+
my $template = Bugzilla->template;
my $vars = {};
################################################################################
sub list {
+ # Restrict the list to the given product and component, if given.
+ $vars = get_products_and_components($vars);
+
+ my $product = validateProduct(scalar $cgi->param('product'));
+ my $component = validateComponent($product, scalar $cgi->param('component'));
+ my $product_id = $product ? $product->id : 0;
+ my $component_id = $component ? $component->id : 0;
+
# Define the variables and functions that will be passed to the UI template.
- $vars->{'bug_types'} =
- Bugzilla::FlagType::match({ 'target_type' => 'bug',
- 'group' => scalar $cgi->param('group') }, 1);
- $vars->{'attachment_types'} =
- Bugzilla::FlagType::match({ 'target_type' => 'attachment',
- 'group' => scalar $cgi->param('group') }, 1);
+ $vars->{'selected_product'} = $cgi->param('product');
+ $vars->{'selected_component'} = $cgi->param('component');
+
+ # If only a product was specified but no component, then we restrict
+ # the list to flag types available in ALL components of that product.
+ my @comp_ids = ($component_id);
+ if ($product_id && !$component_id) {
+ @comp_ids = map {$_->id} @{$product->components};
+ }
+
+ my @bug_flagtypes;
+ my @attach_flagtypes;
+
+ foreach my $comp_id (@comp_ids) {
+ my $bug_types =
+ Bugzilla::FlagType::match({ 'target_type' => 'bug',
+ 'group' => scalar $cgi->param('group'),
+ 'product_id' => $product_id,
+ 'component_id' => $comp_id }, 1);
+ push(@bug_flagtypes, $bug_types);
+
+ my $attach_types =
+ Bugzilla::FlagType::match({ 'target_type' => 'attachment',
+ 'group' => scalar $cgi->param('group'),
+ 'product_id' => $product_id,
+ 'component_id' => $comp_id }, 1);
+ push(@attach_flagtypes, $attach_types);
+ }
+
+ sub intersection {
+ my ($aa, $bb) = @_;
+ my %union;
+ my %isect;
+ foreach my $e (@$aa, @$bb) { $union{$e->{'id'}}++ && ($isect{$e->{'id'}} ||= $e) };
+ return [sort { $a->{'sortkey'} <=> $b->{'sortkey'}
+ || $a->{'name'} cmp $b->{'name'} } values %isect];
+ }
+ $vars->{'bug_types'} = reduce { intersection($a, $b) } @bug_flagtypes;
+ $vars->{'attachment_types'} = reduce { intersection($a, $b) } @attach_flagtypes;
# Users want to see group names, not IDs
# So get the group names
$action eq 'enter' ? validateTargetType() : (my $id = validateID());
my $dbh = Bugzilla->dbh;
- my @products = Bugzilla::Product::get_all_products();
- # We require all unique component names.
- my %components;
- foreach my $product (@products) {
- foreach my $component (@{$product->components}) {
- $components{$component->name} = 1;
- }
- }
- $vars->{'products'} = \@products;
- $vars->{'components'} = [sort(keys %components)];
+ # Fill $vars with products and components data.
+ $vars = get_products_and_components($vars);
$vars->{'last_action'} = $cgi->param('action');
if ($cgi->param('action') eq 'enter' || $cgi->param('action') eq 'copy') {
my %inclusions = clusion_array_to_hash(\@inclusions);
my %exclusions = clusion_array_to_hash(\@exclusions);
- my @products = Bugzilla::Product::get_all_products();
- # We require all unique component names.
- my %components;
- foreach my $product (@products) {
- foreach my $component (@{$product->components}) {
- $components{$component->name} = 1;
- }
- }
- $vars->{'products'} = \@products;
- $vars->{'components'} = [sort(keys %components)];
+ # Fill $vars with products and components data.
+ $vars = get_products_and_components($vars);
my @groups = Bugzilla::Group::get_all_groups();
$vars->{'groups'} = \@groups;
|| ThrowTemplateError($template->error());
}
+sub get_products_and_components {
+ my $vars = shift;
+
+ my @products = Bugzilla::Product::get_all_products();
+ # We require all unique component names.
+ my %components;
+ foreach my $product (@products) {
+ foreach my $component (@{$product->components}) {
+ $components{$component->name} = 1;
+ }
+ }
+ $vars->{'products'} = \@products;
+ $vars->{'components'} = [sort(keys %components)];
+ return $vars;
+}
################################################################################
# Data Validation / Security Authorization
return unless $component_name;
($product && $product->id)
- || ThrowCodeError("flag_type_component_without_product");
+ || ThrowUserError("flag_type_component_without_product");
my $component = Bugzilla::Component::check_component($product, $component_name);
return $component;
[% PROCESS global/variables.none.tmpl %]
+[% PROCESS "global/js-products.html.tmpl" %]
+
[% PROCESS global/header.html.tmpl
title = 'Administer Flag Types'
style = "
.inactive { color: #787878; }
.multiplicable { display: block; }
"
+ onload="selectProduct(document.forms[0], 'product', 'component', '__All__');"
+ javascript_urls=["productmenu.js"]
%]
<p>
depending on whether the patch passed or failed review.
</p>
+<p>
+ You can restrict the list of flag types to those available for a given product
+ and component. If a product is selected with no component, only flag types
+ which are available to ALL components of the product are shown.
+</p>
+
+<form action="editflagtypes.cgi" method="get">
+ <table>
+ <tr>
+ <th><label for="product">Product:</label></th>
+ <td>
+ <select name="product" onchange="selectProduct(this.form, 'product', 'component', '__All__');">
+ <option value="">__All__</option>
+ [% FOREACH prod = products %]
+ <option value="[% prod.name FILTER html %]"
+ [% " selected" IF selected_product == prod.name %]>
+ [% prod.name FILTER html %]</option>
+ [% END %]
+ </select>
+ </td>
+ <th><label for="component">Component:</label></th>
+ <td>
+ <select name="component">
+ <option value="">__All__</option>
+ [% FOREACH comp = components %]
+ <option value="[% comp FILTER html %]"
+ [% " selected" IF selected_component == comp %]>
+ [% comp FILTER html %]</option>
+ [% END %]
+ </select>
+ </td>
+ <td><input type="submit" id="submit" value="Filter"></td>
+ </tr>
+ </table>
+</form>
+
<h3>Flag Types for [% terms.Bugs %]</h3>
[% PROCESS display_flag_types types=bug_types %]
--- /dev/null
+[%# 1.0@bugzilla.org %]
+[%# The contents of this file are subject to the Mozilla Public
+ # License Version 1.1 (the "License"); you may not use this file
+ # except in compliance with the License. You may obtain a copy of
+ # the License at http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS
+ # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ # implied. See the License for the specific language governing
+ # rights and limitations under the License.
+ #
+ # The Original Code is the Bugzilla Bug Tracking System.
+ #
+ # 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): Myk Melez <myk@mozilla.org>
+ # Frédéric Buclin <LpSolit@gmail.com>
+ #%]
+
+[%# The javascript block gets used in header.html.tmpl. %]
+[% javascript = BLOCK %]
+ var usetms = 0; // do we have target milestone?
+ var first_load = 1; // is this the first time we load the page?
+ var last_sel = []; // caches last selection
+ var cpts = new Array();
+ [% FOREACH prod = products %]
+ cpts['[% prod.name FILTER js %]'] = [
+ [%- FOREACH comp = prod.components %]'[% comp.name FILTER js %]'[% ", " UNLESS loop.last %] [%- END -%] ];
+ [% END %]
+[% END %]