From 6fac08b94b6cd67d8dd7ae8f7eeb5de5233c59d7 Mon Sep 17 00:00:00 2001 From: Frédéric Buclin Date: Sat, 4 Dec 2010 02:22:49 +0100 Subject: Bug 529974: Let users with local editcomponents privs manage flags for products they can administer a=LpSolit (module owner) --- Bugzilla/FlagType.pm | 63 +++--- Bugzilla/User.pm | 58 ++++++ editflagtypes.cgi | 215 +++++++++++++++------ skins/standard/attachment.css | 4 - skins/standard/global.css | 4 + template/en/default/admin/admin.html.tmpl | 3 +- template/en/default/admin/flag-type/edit.html.tmpl | 66 ++++--- template/en/default/global/user-error.html.tmpl | 25 +++ 8 files changed, 334 insertions(+), 104 deletions(-) diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index d6a122609..bd3f7b054 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -112,6 +112,9 @@ use constant UPDATE_VALIDATORS => { sub create { my $class = shift; + my $dbh = Bugzilla->dbh; + + $dbh->bz_start_transaction(); $class->check_required_create_fields(@_); my $params = $class->run_create_validators(@_); @@ -126,6 +129,9 @@ sub create { $flagtype->set_clusions({ inclusions => $inclusions, exclusions => $exclusions }); + $flagtype->update(); + + $dbh->bz_commit_transaction(); return $flagtype; } @@ -161,7 +167,7 @@ sub update { FROM flags INNER JOIN bugs ON flags.bug_id = bugs.bug_id - LEFT OUTER JOIN flaginclusions AS i + LEFT JOIN flaginclusions AS i ON (flags.type_id = i.type_id AND (bugs.product_id = i.product_id OR i.product_id IS NULL) @@ -351,7 +357,6 @@ sub set_request_group { $_[0]->set('request_group_id', $_[1]); } sub set_clusions { my ($self, $list) = @_; - my $dbh = Bugzilla->dbh; my %products; foreach my $category (keys %$list) { @@ -360,22 +365,33 @@ sub set_clusions { foreach my $prod_comp (@{$list->{$category} || []}) { my ($prod_id, $comp_id) = split(':', $prod_comp); - my $component; + my $prod_name = '__Any__'; + my $comp_name = '__Any__'; # Does the product exist? - if ($prod_id && detaint_natural($prod_id)) { - $products{$prod_id} ||= new Bugzilla::Product($prod_id); - next unless defined $products{$prod_id}; + if ($prod_id) { + $products{$prod_id} ||= Bugzilla::Product->check({ id => $prod_id }); + detaint_natural($prod_id); + $prod_name = $products{$prod_id}->name; # Does the component belong to this product? - if ($comp_id && detaint_natural($comp_id)) { - ($component) = grep { $_->id == $comp_id } @{$products{$prod_id}->components}; - next unless $component; + if ($comp_id) { + detaint_natural($comp_id) + || ThrowCodeError('param_must_be_numeric', + { function => 'Bugzilla::FlagType::set_clusions' }); + + my ($component) = grep { $_->id == $comp_id } @{$products{$prod_id}->components} + or ThrowUserError('product_unknown_component', + { product => $prod_name, comp_id => $comp_id }); + $comp_name = $component->name; } + else { + $comp_id = 0; + } + } + else { + $prod_id = 0; + $comp_id = 0; } - $prod_id ||= 0; - $comp_id ||= 0; - my $prod_name = $prod_id ? $products{$prod_id}->name : '__Any__'; - my $comp_name = $comp_id ? $component->name : '__Any__'; $clusions{"$prod_name:$comp_name"} = "$prod_id:$comp_id"; $clusions_as_hash{$prod_id}->{$comp_id} = 1; } @@ -520,15 +536,16 @@ sub get_clusions { my $dbh = Bugzilla->dbh; my $list = - $dbh->selectall_arrayref("SELECT products.id, products.name, " . - " components.id, components.name " . - "FROM flagtypes, flag${type}clusions " . - "LEFT OUTER JOIN products " . - " ON flag${type}clusions.product_id = products.id " . - "LEFT OUTER JOIN components " . - " ON flag${type}clusions.component_id = components.id " . - "WHERE flagtypes.id = ? " . - " AND flag${type}clusions.type_id = flagtypes.id", + $dbh->selectall_arrayref("SELECT products.id, products.name, + components.id, components.name + FROM flagtypes + INNER JOIN flag${type}clusions + ON flag${type}clusions.type_id = flagtypes.id + LEFT JOIN products + ON flag${type}clusions.product_id = products.id + LEFT JOIN components + ON flag${type}clusions.component_id = components.id + WHERE flagtypes.id = ?", undef, $id); my (%clusions, %clusions_as_hash); foreach my $data (@$list) { @@ -667,7 +684,7 @@ sub sqlify_criteria { $join_clause .= "AND (e.component_id = $component_id OR e.component_id IS NULL) "; } else { - $addl_join_clause = "AND e.component_id IS NULL OR (i.component_id != e.component_id) "; + $addl_join_clause = "AND e.component_id IS NULL OR (i.component_id = e.component_id) "; } $join_clause .= "AND ((e.product_id = $product_id $addl_join_clause) OR e.product_id IS NULL)"; diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index de2d0dcc7..47f923f20 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -1006,6 +1006,49 @@ sub check_can_admin_product { return $product; } +sub check_can_admin_flagtype { + my ($self, $flagtype_id) = @_; + + my $flagtype = Bugzilla::FlagType->check({ id => $flagtype_id }); + my $can_fully_edit = 1; + + if (!$self->in_group('editcomponents')) { + my $products = $self->get_products_by_permission('editcomponents'); + # You need editcomponents privs for at least one product to have + # a chance to edit the flagtype. + scalar(@$products) + || ThrowUserError('auth_failure', {group => 'editcomponents', + action => 'edit', + object => 'flagtypes'}); + my $can_admin = 0; + my $i = $flagtype->inclusions_as_hash; + my $e = $flagtype->exclusions_as_hash; + + # If there is at least one product for which the user doesn't have + # editcomponents privs, then don't allow him to do everything with + # this flagtype, independently of whether this product is in the + # exclusion list or not. + my %product_ids; + map { $product_ids{$_->id} = 1 } @$products; + $can_fully_edit = 0 if grep { !$product_ids{$_} } keys %$i; + + unless ($e->{0}->{0}) { + foreach my $product (@$products) { + my $id = $product->id; + next if $e->{$id}->{0}; + # If we are here, the product has not been explicitly excluded. + # Check whether it's explicitly included, or at least one of + # its components. + $can_admin = ($i->{0}->{0} || $i->{$id}->{0} + || scalar(grep { !$e->{$id}->{$_} } keys %{$i->{$id}})); + last if $can_admin; + } + } + $can_admin || ThrowUserError('flag_type_not_editable', { flagtype => $flagtype }); + } + return wantarray ? ($flagtype, $can_fully_edit) : $flagtype; +} + sub can_request_flag { my ($self, $flag_type) = @_; @@ -2261,6 +2304,21 @@ not be aware of the existence of the product. Returns: On success, a product object. On failure, an error is thrown. +=item C + + Description: Checks whether the user is allowed to edit properties of the flag type. + If the flag type is also used by some products for which the user + hasn't editcomponents privs, then the user is only allowed to edit + the inclusion and exclusion lists for products he can administrate. + + Params: $flagtype_id - a flag type ID. + + Returns: On success, a flag type object. On failure, an error is thrown. + In list context, a boolean indicating whether the user can edit + all properties of the flag type is also returned. The boolean + is false if the user can only edit the inclusion and exclusions + lists. + =item C Description: Checks whether the user can request flags of the given type. diff --git a/editflagtypes.cgi b/editflagtypes.cgi index ecfa32ca8..4d11eecdb 100755 --- a/editflagtypes.cgi +++ b/editflagtypes.cgi @@ -38,7 +38,6 @@ use Bugzilla::Group; use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::Product; -use Bugzilla::Component; use Bugzilla::Token; # Make sure the user is logged in and has the right privileges. @@ -46,16 +45,18 @@ my $user = Bugzilla->login(LOGIN_REQUIRED); my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; -# We need this everywhere. -my $vars = get_products_and_components(); - print $cgi->header(); $user->in_group('editcomponents') + || scalar(@{$user->get_products_by_permission('editcomponents')}) || ThrowUserError("auth_failure", {group => "editcomponents", action => "edit", object => "flagtypes"}); +# We need this everywhere. +my $vars = get_products_and_components(); +my @products = @{$vars->{products}}; + my $action = $cgi->param('action') || 'list'; my $token = $cgi->param('token'); my $product = $cgi->param('product'); @@ -63,13 +64,18 @@ my $component = $cgi->param('component'); my $flag_id = $cgi->param('id'); if ($product) { - $product = Bugzilla::Product->check({ name => $product, allow_inaccessible => 1 }); + # Make sure the user is allowed to view this product name. + # Users with global editcomponents privs can see all product names. + ($product) = grep { lc($_->name) eq lc($product) } @products; + $product || ThrowUserError('product_access_denied', { name => $cgi->param('product') }); } if ($component) { ($product && $product->id) || ThrowUserError('flag_type_component_without_product'); - $component = Bugzilla::Component->check({ product => $product, name => $component }); + ($component) = grep { lc($_->name) eq lc($component) } @{$product->components}; + $component || ThrowUserError('product_unknown_component', { product => $product->name, + comp => $cgi->param('component') }); } # If 'categoryAction' is set, it has priority over 'action'. @@ -78,15 +84,30 @@ if (my ($category_action) = grep { $_ =~ /^categoryAction-(?:\w+)$/ } $cgi->para my @inclusions = $cgi->param('inclusions'); my @exclusions = $cgi->param('exclusions'); + my @categories; + if ($category_action =~ /^(in|ex)clude$/) { + if (!$user->in_group('editcomponents') && !$product) { + # The user can only add the flag type to products he can administrate. + foreach my $prod (@products) { + push(@categories, $prod->id . ':0') + } + } + else { + my $category = ($product ? $product->id : 0) . ':' . + ($component ? $component->id : 0); + push(@categories, $category); + } + } + if ($category_action eq 'include') { - my $category = ($product ? $product->id : 0) . ":" . - ($component ? $component->id : 0); - push(@inclusions, $category) unless grep($_ eq $category, @inclusions); + foreach my $category (@categories) { + push(@inclusions, $category) unless grep($_ eq $category, @inclusions); + } } elsif ($category_action eq 'exclude') { - my $category = ($product ? $product->id : 0) . ":" . - ($component ? $component->id : 0); - push(@exclusions, $category) unless grep($_ eq $category, @exclusions); + foreach my $category (@categories) { + push(@exclusions, $category) unless grep($_ eq $category, @exclusions); + } } elsif ($category_action eq 'removeInclusion') { my @inclusion_to_remove = $cgi->param('inclusion_to_remove'); @@ -101,11 +122,6 @@ if (my ($category_action) = grep { $_ =~ /^categoryAction-(?:\w+)$/ } $cgi->para } } - # Convert the array @clusions('prod_ID:comp_ID') back to a hash of - # the form %clusions{'prod_name:comp_name'} = 'prod_ID:comp_ID' - my %inclusions = clusion_array_to_hash(\@inclusions); - my %exclusions = clusion_array_to_hash(\@exclusions); - $vars->{'groups'} = [Bugzilla::Group->get_all]; $vars->{'action'} = $action; @@ -122,11 +138,13 @@ if (my ($category_action) = grep { $_ =~ /^categoryAction-(?:\w+)$/ } $cgi->para $type->{'request_group'} = {}; $type->{'request_group'}->{'name'} = $cgi->param('request_group'); - $type->{'inclusions'} = \%inclusions; - $type->{'exclusions'} = \%exclusions; + $vars->{'inclusions'} = clusion_array_to_hash(\@inclusions, \@products); + $vars->{'exclusions'} = clusion_array_to_hash(\@exclusions, \@products); + $vars->{'type'} = $type; $vars->{'token'} = $token; $vars->{'check_clusions'} = 1; + $vars->{'can_fully_edit'} = $cgi->param('can_fully_edit'); $template->process("admin/flag-type/edit.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -165,8 +183,7 @@ if ($action eq 'list') { } # If no product is given, then show all flag types available. else { - my $flagtypes = Bugzilla::FlagType::match({ group => $group_id }); - + my $flagtypes = get_editable_flagtypes(\@products, $group_id); $bug_flagtypes = [grep { $_->target_type eq 'bug' } @$flagtypes]; $attach_flagtypes = [grep { $_->target_type eq 'attachment' } @$flagtypes]; } @@ -202,8 +219,12 @@ if ($action eq 'enter') { $vars->{'action'} = 'insert'; $vars->{'token'} = issue_session_token('add_flagtype'); - $vars->{'type'} = { 'target_type' => $type, - 'inclusions' => { '__Any__:__Any__' => '0:0' } }; + $vars->{'type'} = { 'target_type' => $type }; + # Only users with global editcomponents privs can add a flagtype + # to all products. + $vars->{'inclusions'} = { '__Any__:__Any__' => '0:0' } + if $user->in_group('editcomponents'); + $vars->{'can_fully_edit'} = 1; # Get a list of groups available to restrict this flag type against. $vars->{'groups'} = [Bugzilla::Group->get_all]; @@ -213,7 +234,19 @@ if ($action eq 'enter') { } if ($action eq 'edit' || $action eq 'copy') { - $vars->{'type'} = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + $vars->{'type'} = $flagtype; + $vars->{'can_fully_edit'} = $can_fully_edit; + + if ($user->in_group('editcomponents')) { + $vars->{'inclusions'} = $flagtype->inclusions; + $vars->{'exclusions'} = $flagtype->exclusions; + } + else { + # Filter products the user shouldn't know about. + $vars->{'inclusions'} = clusion_array_to_hash([values %{$flagtype->inclusions}], \@products); + $vars->{'exclusions'} = clusion_array_to_hash([values %{$flagtype->exclusions}], \@products); + } if ($action eq 'copy') { $vars->{'action'} = "insert"; @@ -249,6 +282,12 @@ if ($action eq 'insert') { my @inclusions = $cgi->param('inclusions'); my @exclusions = $cgi->param('exclusions'); + # Filter inclusion and exclusion lists to products the user can see. + unless ($user->in_group('editcomponents')) { + @inclusions = values %{clusion_array_to_hash(\@inclusions, \@products)}; + @exclusions = values %{clusion_array_to_hash(\@exclusions, \@products)}; + } + my $flagtype = Bugzilla::FlagType->create({ name => $name, description => $description, @@ -270,9 +309,9 @@ if ($action eq 'insert') { $vars->{'name'} = $flagtype->name; $vars->{'message'} = "flag_type_created"; - my @flagtypes = Bugzilla::FlagType->get_all; - $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @flagtypes]; - $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @flagtypes]; + my $flagtypes = get_editable_flagtypes(\@products); + $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @$flagtypes]; + $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @$flagtypes]; $template->process("admin/flag-type/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -295,17 +334,34 @@ if ($action eq 'update') { my @inclusions = $cgi->param('inclusions'); my @exclusions = $cgi->param('exclusions'); - my $flagtype = Bugzilla::FlagType->check({ id => $flag_id }); - $flagtype->set_name($name); - $flagtype->set_description($description); - $flagtype->set_cc_list($cc_list); - $flagtype->set_sortkey($sortkey); - $flagtype->set_is_active($is_active); - $flagtype->set_is_requestable($is_requestable); - $flagtype->set_is_specifically_requestable($is_specifically); - $flagtype->set_is_multiplicable($is_multiplicable); - $flagtype->set_grant_group($grant_group); - $flagtype->set_request_group($request_group); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + if ($cgi->param('check_clusions') && !$user->in_group('editcomponents')) { + # Filter inclusion and exclusion lists to products the user can edit. + @inclusions = values %{clusion_array_to_hash(\@inclusions, \@products)}; + @exclusions = values %{clusion_array_to_hash(\@exclusions, \@products)}; + # Bring back the products the user cannot edit. + foreach my $item (values %{$flagtype->inclusions}) { + my ($prod_id, $comp_id) = split(':', $item); + push(@inclusions, $item) unless grep { $_->id == $prod_id } @products; + } + foreach my $item (values %{$flagtype->exclusions}) { + my ($prod_id, $comp_id) = split(':', $item); + push(@exclusions, $item) unless grep { $_->id == $prod_id } @products; + } + } + + if ($can_fully_edit) { + $flagtype->set_name($name); + $flagtype->set_description($description); + $flagtype->set_cc_list($cc_list); + $flagtype->set_sortkey($sortkey); + $flagtype->set_is_active($is_active); + $flagtype->set_is_requestable($is_requestable); + $flagtype->set_is_specifically_requestable($is_specifically); + $flagtype->set_is_multiplicable($is_multiplicable); + $flagtype->set_grant_group($grant_group); + $flagtype->set_request_group($request_group); + } $flagtype->set_clusions({ inclusions => \@inclusions, exclusions => \@exclusions}) if $cgi->param('check_clusions'); my $changes = $flagtype->update(); @@ -316,9 +372,9 @@ if ($action eq 'update') { $vars->{'changes'} = $changes; $vars->{'message'} = 'flag_type_updated'; - my @flagtypes = Bugzilla::FlagType->get_all; - $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @flagtypes]; - $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @flagtypes]; + my $flagtypes = get_editable_flagtypes(\@products); + $vars->{'bug_types'} = [grep { $_->target_type eq 'bug' } @$flagtypes]; + $vars->{'attachment_types'} = [grep { $_->target_type eq 'attachment' } @$flagtypes]; $template->process("admin/flag-type/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); @@ -326,7 +382,10 @@ if ($action eq 'update') { } if ($action eq 'confirmdelete') { - $vars->{'flag_type'} = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + ThrowUserError('flag_type_cannot_delete', { flagtype => $flagtype }) unless $can_fully_edit; + + $vars->{'flag_type'} = $flagtype; $vars->{'token'} = issue_session_token('delete_flagtype'); $template->process("admin/flag-type/confirm-delete.html.tmpl", $vars) @@ -337,7 +396,9 @@ if ($action eq 'confirmdelete') { if ($action eq 'delete') { check_token_data($token, 'delete_flagtype'); - my $flagtype = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + ThrowUserError('flag_type_cannot_delete', { flagtype => $flagtype }) unless $can_fully_edit; + $flagtype->remove_from_db(); delete_token($token); @@ -357,7 +418,9 @@ if ($action eq 'delete') { if ($action eq 'deactivate') { check_token_data($token, 'delete_flagtype'); - my $flagtype = Bugzilla::FlagType->check({ id => $flag_id }); + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_id); + ThrowUserError('flag_type_cannot_deactivate', { flagtype => $flagtype }) unless $can_fully_edit; + $flagtype->set_is_active(0); $flagtype->update(); @@ -383,8 +446,15 @@ ThrowUserError('unknown_action', {action => $action}); sub get_products_and_components { my $vars = {}; + my $user = Bugzilla->user; - my @products = Bugzilla::Product->get_all; + my @products; + if ($user->in_group('editcomponents')) { + @products = Bugzilla::Product->get_all; + } + else { + @products = @{$user->get_products_by_permission('editcomponents')}; + } # We require all unique component names. my %components; foreach my $product (@products) { @@ -397,6 +467,29 @@ sub get_products_and_components { return $vars; } +sub get_editable_flagtypes { + my ($products, $group_id) = @_; + my $flagtypes; + + if (Bugzilla->user->in_group('editcomponents')) { + $flagtypes = Bugzilla::FlagType::match({ group => $group_id }); + return $flagtypes; + } + + my %visible_flagtypes; + foreach my $product (@$products) { + foreach my $target ('bug', 'attachment') { + my $prod_flagtypes = $product->flag_types->{$target}; + $visible_flagtypes{$_->id} ||= $_ foreach @$prod_flagtypes; + } + } + @$flagtypes = sort { $a->sortkey <=> $b->sortkey || $a->name cmp $b->name } + values %visible_flagtypes; + # Filter flag types if a group ID is given. + $flagtypes = filter_group($flagtypes, $group_id); + return $flagtypes; +} + sub filter_group { my ($flag_types, $gid) = @_; return $flag_types unless $gid; @@ -410,24 +503,38 @@ sub filter_group { # Convert the array @clusions('prod_ID:comp_ID') back to a hash of # the form %clusions{'prod_name:comp_name'} = 'prod_ID:comp_ID' sub clusion_array_to_hash { - my $array = shift; + my ($array, $visible_products) = @_; + my $user = Bugzilla->user; + my $has_privs = $user->in_group('editcomponents'); + my %hash; my %products; my %components; + foreach my $ids (@$array) { - trick_taint($ids); my ($product_id, $component_id) = split(":", $ids); my $product_name = "__Any__"; + my $component_name = "__Any__"; + if ($product_id) { - $products{$product_id} ||= new Bugzilla::Product($product_id); - $product_name = $products{$product_id}->name if $products{$product_id}; + ($products{$product_id}) = grep { $_->id == $product_id } @$visible_products; + next unless $products{$product_id}; + $product_name = $products{$product_id}->name; + + if ($component_id) { + ($components{$component_id}) = + grep { $_->id == $component_id } @{$products{$product_id}->components}; + next unless $components{$component_id}; + $component_name = $components{$component_id}->name; + } } - my $component_name = "__Any__"; - if ($component_id) { - $components{$component_id} ||= new Bugzilla::Component($component_id); - $component_name = $components{$component_id}->name if $components{$component_id}; + else { + # Users with local editcomponents privs cannot use __Any__:__Any__. + next unless $has_privs; + # It's illegal to select a component without a product. + next if $component_id; } $hash{"$product_name:$component_name"} = $ids; } - return %hash; + return \%hash; } diff --git a/skins/standard/attachment.css b/skins/standard/attachment.css index b42b2224c..3c93ec6fb 100644 --- a/skins/standard/attachment.css +++ b/skins/standard/attachment.css @@ -99,10 +99,6 @@ tbody.file pre:empty { width: 3em; } -.warning { - color: red -} - table.attachment_info th { text-align: right; vertical-align: top; diff --git a/skins/standard/global.css b/skins/standard/global.css index 0deb4b94a..50f5d442b 100644 --- a/skins/standard/global.css +++ b/skins/standard/global.css @@ -385,6 +385,10 @@ input.requestee { font-size: x-large; } +.warning { + color: red; +} + .throw_error { background-color: #ff0000; color: black; diff --git a/template/en/default/admin/admin.html.tmpl b/template/en/default/admin/admin.html.tmpl index 145360bfa..98f729b02 100644 --- a/template/en/default/admin/admin.html.tmpl +++ b/template/en/default/admin/admin.html.tmpl @@ -76,7 +76,8 @@ components, versions and milestones directly. - [% class = user.in_group('editcomponents') ? "" : "forbidden" %] + [% class = (user.in_group('editcomponents') + || user.get_products_by_permission('editcomponents').size) ? "" : "forbidden" %]
Flags
A flag is a custom 4-states attribute of [% terms.bugs %] and/or attachments. These states are: granted, denied, requested and undefined. diff --git a/template/en/default/admin/flag-type/edit.html.tmpl b/template/en/default/admin/flag-type/edit.html.tmpl index eb0211377..13db9fab1 100644 --- a/template/en/default/admin/flag-type/edit.html.tmpl +++ b/template/en/default/admin/flag-type/edit.html.tmpl @@ -17,6 +17,7 @@ # # Contributor(s): Myk Melez # Mark Bickford + # Frédéric Buclin #%] [% PROCESS global/variables.none.tmpl %] @@ -42,22 +43,24 @@ table#form th { text-align: right; vertical-align: baseline; white-space: nowrap; } table#form td { text-align: left; vertical-align: baseline; } " - onload="var f = document.forms[0]; selectProduct(f.product, f.component, null, null, '__Any__');" + onload="var f = document.forms['flagtype_properties']; + selectProduct(f.product, f.component, null, null, '__Any__');" javascript_urls=["js/productform.js"] doc_section = doc_section %] -
+ + - [% FOREACH category = type.inclusions %] - + [% FOREACH category = inclusions.values %] + [% END %] - [% FOREACH category = type.exclusions %] - + [% FOREACH category = exclusions.values %] + [% END %] [%# Add a hidden button at the top of the form so that the user pressing "return" @@ -69,8 +72,8 @@ Name: a short name identifying this type.
- + @@ -83,6 +86,7 @@ minrows = 4 cols = 80 defaultcontent = type.description + disabled = !can_fully_edit %] @@ -94,6 +98,12 @@ the products/components to which [% type.target_type == "bug" ? terms.bugs : "attachments" %] must (inclusions) or must not (exclusions) belong in order for users to be able to set flags of this type for them. + [% UNLESS can_fully_edit %] +

This flagtype also applies to some products you are not allowed + to edit (and so which are not displayed in the lists below). Your limited privileges + means you are only allowed to add and remove this flagtype to/from products you can + edit, but not to edit other properties of the flagtype.

+ [% END %] @@ -139,7 +145,8 @@ this type will be sorted when displayed to users in a list; ignore if you don't care what order the types appear in or if you want them to appear in alphabetical order.
- + @@ -147,6 +154,7 @@ @@ -156,6 +164,7 @@ @@ -172,7 +181,8 @@ [% Param('emailsuffix') %] will not be appended to these addresses, so you should add it explicitly if so desired. [% END %]
- + @@ -180,6 +190,7 @@ @@ -211,7 +223,7 @@ if flags of this type are requestable, the group allowed to request them (to allow all users to request these flags, select no group).
Note that the request group alone has no effect if the grant group is not defined!
- [% PROCESS select selname = "request_group" %] + [% PROCESS group_select selname = "request_group" %] @@ -233,8 +245,8 @@ [%# Block for SELECT fields #%] [%############################################################################%] -[% BLOCK select %] - [% FOREACH group = groups %]
@@ -101,17 +111,13 @@

@@ -119,12 +125,12 @@
Inclusions:
- [% PROCESS "global/select-menu.html.tmpl" name="inclusion_to_remove" multiple="1" size="7" options=type.inclusions %]
+ [% PROCESS category_select name="inclusion_to_remove" categories = inclusions %]
Exclusions:
- [% PROCESS "global/select-menu.html.tmpl" name="exclusion_to_remove" multiple="1" size="7" options=type.exclusions %]
+ [% PROCESS category_select name="exclusion_to_remove" categories = exclusions %]
   
  @@ -190,6 +201,7 @@   @@ -201,7 +213,7 @@ the group allowed to grant/deny flags of this type (to allow all users to grant/deny these flags, select no group).
- [% PROCESS select selname = "grant_group" %] + [% PROCESS group_select selname = "grant_group" %]