Commit 0d3a72b7 authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 189627: Implement per-product privileges - Patch by Fré©ric Buclin…

Bug 189627: Implement per-product privileges - Patch by Fré©ric Buclin <LpSolit@gmail.com> r=mkanat a=myk
parent bd49bafd
...@@ -609,19 +609,22 @@ sub validate_content_type { ...@@ -609,19 +609,22 @@ sub validate_content_type {
=pod =pod
=item C<validate_can_edit()> =item C<validate_can_edit($attachment, $product_id)>
Description: validates if the user is allowed to view and edit the attachment. Description: validates if the user is allowed to view and edit the attachment.
Only the submitter or someone with editbugs privs can edit it. Only the submitter or someone with editbugs privs can edit it.
Only the submitter and users in the insider group can view Only the submitter and users in the insider group can view
private attachments. private attachments.
Params: $attachment - the attachment object being edited.
$product_id - the product ID the attachment belongs to.
Returns: 1 on success. Else an error is thrown. Returns: 1 on success. Else an error is thrown.
=cut =cut
sub validate_can_edit { sub validate_can_edit {
my $attachment = shift; my ($attachment, $product_id) = @_;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user; my $user = Bugzilla->user;
...@@ -634,27 +637,27 @@ sub validate_can_edit { ...@@ -634,27 +637,27 @@ sub validate_can_edit {
} }
# Users with editbugs privs can edit all attachments. # Users with editbugs privs can edit all attachments.
return if $user->in_group('editbugs'); return if $user->in_group('editbugs', $product_id);
# If we come here, then this attachment cannot be seen by the user. # If we come here, then this attachment cannot be seen by the user.
ThrowUserError('illegal_attachment_edit', { attach_id => $attachment->id }); ThrowUserError('illegal_attachment_edit', { attach_id => $attachment->id });
} }
=item C<validate_obsolete($bug_id)> =item C<validate_obsolete($bug)>
Description: validates if attachments the user wants to mark as obsolete Description: validates if attachments the user wants to mark as obsolete
really belong to the given bug and are not already obsolete. really belong to the given bug and are not already obsolete.
Moreover, a user cannot mark an attachment as obsolete if Moreover, a user cannot mark an attachment as obsolete if
he cannot view it (due to restrictions on it). he cannot view it (due to restrictions on it).
Params: $bug_id - The bug ID obsolete attachments should belong to. Params: $bug - The bug object obsolete attachments should belong to.
Returns: 1 on success. Else an error is thrown. Returns: 1 on success. Else an error is thrown.
=cut =cut
sub validate_obsolete { sub validate_obsolete {
my ($class, $bug_id) = @_; my ($class, $bug) = @_;
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
# Make sure the attachment id is valid and the user has permissions to view # Make sure the attachment id is valid and the user has permissions to view
...@@ -674,12 +677,12 @@ sub validate_obsolete { ...@@ -674,12 +677,12 @@ sub validate_obsolete {
ThrowUserError('invalid_attach_id', $vars) unless $attachment; ThrowUserError('invalid_attach_id', $vars) unless $attachment;
# Check that the user can view and edit this attachment. # Check that the user can view and edit this attachment.
$attachment->validate_can_edit; $attachment->validate_can_edit($bug->product_id);
$vars->{'description'} = $attachment->description; $vars->{'description'} = $attachment->description;
if ($attachment->bug_id != $bug_id) { if ($attachment->bug_id != $bug->bug_id) {
$vars->{'my_bug_id'} = $bug_id; $vars->{'my_bug_id'} = $bug->bug_id;
$vars->{'attach_bug_id'} = $attachment->bug_id; $vars->{'attach_bug_id'} = $attachment->bug_id;
ThrowCodeError('mismatched_bug_ids_on_obsolete', $vars); ThrowCodeError('mismatched_bug_ids_on_obsolete', $vars);
} }
...@@ -758,7 +761,7 @@ sub insert_attachment_for_bug { ...@@ -758,7 +761,7 @@ sub insert_attachment_for_bug {
# Check attachments the user tries to mark as obsolete. # Check attachments the user tries to mark as obsolete.
my @obsolete_attachments; my @obsolete_attachments;
if ($cgi->param('obsolete')) { if ($cgi->param('obsolete')) {
@obsolete_attachments = $class->validate_obsolete($bug->bug_id); @obsolete_attachments = $class->validate_obsolete($bug);
} }
# The order of these function calls is important, as Flag::validate # The order of these function calls is important, as Flag::validate
......
...@@ -121,7 +121,6 @@ sub VALIDATORS { ...@@ -121,7 +121,6 @@ sub VALIDATORS {
commentprivacy => \&_check_commentprivacy, commentprivacy => \&_check_commentprivacy,
deadline => \&_check_deadline, deadline => \&_check_deadline,
estimated_time => \&_check_estimated_time, estimated_time => \&_check_estimated_time,
keywords => \&_check_keywords,
op_sys => \&_check_op_sys, op_sys => \&_check_op_sys,
priority => \&_check_priority, priority => \&_check_priority,
product => \&_check_product, product => \&_check_product,
...@@ -354,6 +353,8 @@ sub run_create_validators { ...@@ -354,6 +353,8 @@ sub run_create_validators {
$params->{version} = $class->_check_version($product, $params->{version}); $params->{version} = $class->_check_version($product, $params->{version});
$params->{keywords} = $class->_check_keywords($product, $params->{keywords});
$params->{groups} = $class->_check_groups($product, $params->{groups} = $class->_check_groups($product,
$params->{groups}); $params->{groups});
...@@ -381,7 +382,7 @@ sub run_create_validators { ...@@ -381,7 +382,7 @@ sub run_create_validators {
$params->{assigned_to}, $params->{qa_contact}); $params->{assigned_to}, $params->{qa_contact});
($params->{dependson}, $params->{blocked}) = ($params->{dependson}, $params->{blocked}) =
$class->_check_dependencies($params->{dependson}, $params->{blocked}); $class->_check_dependencies($product, $params->{dependson}, $params->{blocked});
# You can't set these fields on bug creation (or sometimes ever). # You can't set these fields on bug creation (or sometimes ever).
delete $params->{resolution}; delete $params->{resolution};
...@@ -480,7 +481,7 @@ sub _check_assigned_to { ...@@ -480,7 +481,7 @@ sub _check_assigned_to {
$name = trim($name); $name = trim($name);
# Default assignee is the component owner. # Default assignee is the component owner.
my $id; my $id;
if (!$user->in_group("editbugs") || !$name) { if (!$user->in_group('editbugs', $component->product_id) || !$name) {
$id = $component->default_assignee->id; $id = $component->default_assignee->id;
} else { } else {
$id = login_to_id($name, THROW_ERROR); $id = login_to_id($name, THROW_ERROR);
...@@ -508,7 +509,8 @@ sub _check_bug_status { ...@@ -508,7 +509,8 @@ sub _check_bug_status {
my @valid_statuses = VALID_ENTRY_STATUS; my @valid_statuses = VALID_ENTRY_STATUS;
if ($user->in_group('editbugs') || $user->in_group('canconfirm')) { if ($user->in_group('editbugs', $product->id)
|| $user->in_group('canconfirm', $product->id)) {
# Default to NEW if the user with privs hasn't selected another status. # Default to NEW if the user with privs hasn't selected another status.
$status ||= 'NEW'; $status ||= 'NEW';
} }
...@@ -599,10 +601,10 @@ sub _check_deadline { ...@@ -599,10 +601,10 @@ sub _check_deadline {
# Takes two comma/space-separated strings and returns arrayrefs # Takes two comma/space-separated strings and returns arrayrefs
# of valid bug IDs. # of valid bug IDs.
sub _check_dependencies { sub _check_dependencies {
my ($invocant, $depends_on, $blocks) = @_; my ($invocant, $product, $depends_on, $blocks) = @_;
# Only editbugs users can set dependencies on bug entry. # Only editbugs users can set dependencies on bug entry.
return ([], []) unless Bugzilla->user->in_group('editbugs'); return ([], []) unless Bugzilla->user->in_group('editbugs', $product->id);
$depends_on ||= ''; $depends_on ||= '';
$blocks ||= ''; $blocks ||= '';
...@@ -676,9 +678,10 @@ sub _check_groups { ...@@ -676,9 +678,10 @@ sub _check_groups {
} }
sub _check_keywords { sub _check_keywords {
my ($invocant, $keyword_string) = @_; my ($invocant, $product, $keyword_string) = @_;
$keyword_string = trim($keyword_string); $keyword_string = trim($keyword_string);
return [] if (!$keyword_string || !Bugzilla->user->in_group('editbugs')); return [] if (!$keyword_string
|| !Bugzilla->user->in_group('editbugs', $product->id));
my %keywords; my %keywords;
foreach my $keyword (split(/[\s,]+/, $keyword_string)) { foreach my $keyword (split(/[\s,]+/, $keyword_string)) {
...@@ -801,7 +804,7 @@ sub _check_qa_contact { ...@@ -801,7 +804,7 @@ sub _check_qa_contact {
$name = trim($name); $name = trim($name);
my $id; my $id;
if (!$user->in_group("editbugs") || !$name) { if (!$user->in_group('editbugs', $component->product_id) || !$name) {
# We want to insert NULL into the database if we get a 0. # We want to insert NULL into the database if we get a 0.
$id = $component->default_qa_contact->id || undef; $id = $component->default_qa_contact->id || undef;
} else { } else {
...@@ -1207,14 +1210,16 @@ sub user { ...@@ -1207,14 +1210,16 @@ sub user {
my $user = Bugzilla->user; my $user = Bugzilla->user;
my $canmove = Bugzilla->params->{'move-enabled'} && $user->is_mover; my $canmove = Bugzilla->params->{'move-enabled'} && $user->is_mover;
my $unknown_privileges = $user->in_group("editbugs"); my $prod_id = $self->{'product_id'};
my $unknown_privileges = $user->in_group('editbugs', $prod_id);
my $canedit = $unknown_privileges my $canedit = $unknown_privileges
|| $user->id == $self->{assigned_to_id} || $user->id == $self->{assigned_to_id}
|| (Bugzilla->params->{'useqacontact'} || (Bugzilla->params->{'useqacontact'}
&& $self->{'qa_contact_id'} && $self->{'qa_contact_id'}
&& $user->id == $self->{qa_contact_id}); && $user->id == $self->{qa_contact_id});
my $canconfirm = $unknown_privileges my $canconfirm = $unknown_privileges
|| $user->in_group("canconfirm"); || $user->in_group('canconfirm', $prod_id);
my $isreporter = $user->id my $isreporter = $user->id
&& $user->id == $self->{reporter_id}; && $user->id == $self->{reporter_id};
...@@ -1824,19 +1829,19 @@ sub check_can_change_field { ...@@ -1824,19 +1829,19 @@ sub check_can_change_field {
# $PrivilegesRequired = 2 : the assignee or an empowered user; # $PrivilegesRequired = 2 : the assignee or an empowered user;
# $PrivilegesRequired = 3 : an empowered user. # $PrivilegesRequired = 3 : an empowered user.
# Allow anyone with "editbugs" privs to change anything. # Allow anyone with (product-specific) "editbugs" privs to change anything.
if ($user->in_group('editbugs')) { if ($user->in_group('editbugs', $self->{'product_id'})) {
return 1; return 1;
} }
# *Only* users with "canconfirm" privs can confirm bugs. # *Only* users with (product-specific) "canconfirm" privs can confirm bugs.
if ($field eq 'canconfirm' if ($field eq 'canconfirm'
|| ($field eq 'bug_status' || ($field eq 'bug_status'
&& $oldvalue eq 'UNCONFIRMED' && $oldvalue eq 'UNCONFIRMED'
&& is_open_state($newvalue))) && is_open_state($newvalue)))
{ {
$$PrivilegesRequired = 3; $$PrivilegesRequired = 3;
return $user->in_group('canconfirm'); return $user->in_group('canconfirm', $self->{'product_id'});
} }
# Make sure that a valid bug ID has been given. # Make sure that a valid bug ID has been given.
......
...@@ -171,6 +171,15 @@ sub initial_cc { ...@@ -171,6 +171,15 @@ sub initial_cc {
return $self->{'initial_cc'}; return $self->{'initial_cc'};
} }
sub product {
my $self = shift;
if (!defined $self->{'product'}) {
require Bugzilla::Product; # We cannot |use| it.
$self->{'product'} = new Bugzilla::Product($self->product_id);
}
return $self->{'product'};
}
############################### ###############################
#### Accessors #### #### Accessors ####
############################### ###############################
...@@ -229,7 +238,8 @@ Bugzilla::Component - Bugzilla product component class. ...@@ -229,7 +238,8 @@ Bugzilla::Component - Bugzilla product component class.
my $product_id = $component->product_id; my $product_id = $component->product_id;
my $default_assignee = $component->default_assignee; my $default_assignee = $component->default_assignee;
my $default_qa_contact = $component->default_qa_contact; my $default_qa_contact = $component->default_qa_contact;
my $initial_cc = $component->initial_cc my $initial_cc = $component->initial_cc;
my $product = $component->product;
my $bug_flag_types = $component->flag_types->{'bug'}; my $bug_flag_types = $component->flag_types->{'bug'};
my $attach_flag_types = $component->flag_types->{'attachment'}; my $attach_flag_types = $component->flag_types->{'attachment'};
...@@ -305,6 +315,14 @@ Initial CC List. ...@@ -305,6 +315,14 @@ Initial CC List.
Returns: Two references to an array of flagtype objects. Returns: Two references to an array of flagtype objects.
=item C<product()>
Description: Returns the product the component belongs to.
Params: none.
Returns: A Bugzilla::Product object.
=back =back
=head1 SUBROUTINES =head1 SUBROUTINES
......
...@@ -101,6 +101,7 @@ use File::Basename; ...@@ -101,6 +101,7 @@ use File::Basename;
FULLTEXT_BUGLIST_LIMIT FULLTEXT_BUGLIST_LIMIT
ADMIN_GROUP_NAME ADMIN_GROUP_NAME
PER_PRODUCT_PRIVILEGES
SENDMAIL_EXE SENDMAIL_EXE
SENDMAIL_PATH SENDMAIL_PATH
...@@ -289,6 +290,9 @@ use constant FULLTEXT_BUGLIST_LIMIT => 200; ...@@ -289,6 +290,9 @@ use constant FULLTEXT_BUGLIST_LIMIT => 200;
# Default administration group name. # Default administration group name.
use constant ADMIN_GROUP_NAME => 'admin'; use constant ADMIN_GROUP_NAME => 'admin';
# Privileges which can be per-product.
use constant PER_PRODUCT_PRIVILEGES => ('editcomponents', 'editbugs', 'canconfirm');
# Path to sendmail.exe (Windows only) # Path to sendmail.exe (Windows only)
use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe'; use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe';
# Paths to search for the sendmail binary (non-Windows) # Paths to search for the sendmail binary (non-Windows)
......
...@@ -777,6 +777,12 @@ use constant ABSTRACT_SCHEMA => { ...@@ -777,6 +777,12 @@ use constant ABSTRACT_SCHEMA => {
membercontrol => {TYPE => 'BOOLEAN', NOTNULL => 1}, membercontrol => {TYPE => 'BOOLEAN', NOTNULL => 1},
othercontrol => {TYPE => 'BOOLEAN', NOTNULL => 1}, othercontrol => {TYPE => 'BOOLEAN', NOTNULL => 1},
canedit => {TYPE => 'BOOLEAN', NOTNULL => 1}, canedit => {TYPE => 'BOOLEAN', NOTNULL => 1},
editcomponents => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
editbugs => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
canconfirm => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
], ],
INDEXES => [ INDEXES => [
group_control_map_product_id_idx => group_control_map_product_id_idx =>
......
...@@ -501,6 +501,14 @@ sub update_table_definitions { ...@@ -501,6 +501,14 @@ sub update_table_definitions {
$dbh->bz_alter_column('longdescs', 'thetext', $dbh->bz_alter_column('longdescs', 'thetext',
{ TYPE => 'MEDIUMTEXT', NOTNULL => 1 }, ''); { TYPE => 'MEDIUMTEXT', NOTNULL => 1 }, '');
# 2006-10-20 LpSolit@gmail.com - Bug 189627
$dbh->bz_add_column('group_control_map', 'editcomponents',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
$dbh->bz_add_column('group_control_map', 'editbugs',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
$dbh->bz_add_column('group_control_map', 'canconfirm',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
################################################################ ################################################################
# New --TABLE-- changes should go *** A B O V E *** this point # # New --TABLE-- changes should go *** A B O V E *** this point #
################################################################ ################################################################
......
...@@ -85,7 +85,10 @@ sub group_controls { ...@@ -85,7 +85,10 @@ sub group_controls {
group_control_map.entry, group_control_map.entry,
group_control_map.membercontrol, group_control_map.membercontrol,
group_control_map.othercontrol, group_control_map.othercontrol,
group_control_map.canedit group_control_map.canedit,
group_control_map.editcomponents,
group_control_map.editbugs,
group_control_map.canconfirm
FROM groups FROM groups
LEFT JOIN group_control_map LEFT JOIN group_control_map
ON groups.id = group_control_map.group_id ON groups.id = group_control_map.group_id
......
...@@ -482,8 +482,32 @@ sub bless_groups { ...@@ -482,8 +482,32 @@ sub bless_groups {
} }
sub in_group { sub in_group {
my ($self, $group) = @_; my ($self, $group, $product_id) = @_;
return exists $self->groups->{$group} ? 1 : 0; if (exists $self->groups->{$group}) {
return 1;
}
elsif ($product_id && detaint_natural($product_id)) {
# Make sure $group exists on a per-product basis.
return 0 unless (grep {$_ eq $group} PER_PRODUCT_PRIVILEGES);
$self->{"product_$product_id"} = {} unless exists $self->{"product_$product_id"};
if (!defined $self->{"product_$product_id"}->{$group}) {
my $dbh = Bugzilla->dbh;
my $in_group = $dbh->selectrow_array(
"SELECT 1
FROM group_control_map
WHERE product_id = ?
AND $group != 0
AND group_id IN (" . $self->groups_as_string . ") " .
$dbh->sql_limit(1),
undef, $product_id);
$self->{"product_$product_id"}->{$group} = $in_group ? 1 : 0;
}
return $self->{"product_$product_id"}->{$group};
}
# If we come here, then the user is not in the requested group.
return 0;
} }
sub in_group_id { sub in_group_id {
...@@ -492,6 +516,26 @@ sub in_group_id { ...@@ -492,6 +516,26 @@ sub in_group_id {
return exists $j{$id} ? 1 : 0; return exists $j{$id} ? 1 : 0;
} }
sub get_products_by_permission {
my ($self, $group) = @_;
# Make sure $group exists on a per-product basis.
return [] unless (grep {$_ eq $group} PER_PRODUCT_PRIVILEGES);
my $product_ids = Bugzilla->dbh->selectcol_arrayref(
"SELECT DISTINCT product_id
FROM group_control_map
WHERE $group != 0
AND group_id IN(" . $self->groups_as_string . ")");
# No need to go further if the user has no "special" privs.
return [] unless scalar(@$product_ids);
# We will restrict the list to products the user can see.
my $selectable_products = $self->get_selectable_products;
my @products = grep {lsearch($product_ids, $_->id) > -1} @$selectable_products;
return \@products;
}
sub can_see_user { sub can_see_user {
my ($self, $otherUser) = @_; my ($self, $otherUser) = @_;
my $query; my $query;
...@@ -667,15 +711,15 @@ sub can_enter_product { ...@@ -667,15 +711,15 @@ sub can_enter_product {
} }
# It could be closed for bug entry... # It could be closed for bug entry...
elsif ($product->disallow_new) { elsif ($product->disallow_new) {
ThrowUserError('product_disabled', {product => $product->name}); ThrowUserError('product_disabled', {product => $product});
} }
# It could have no components... # It could have no components...
elsif (!@{$product->components}) { elsif (!@{$product->components}) {
ThrowUserError('missing_component', {product => $product->name}); ThrowUserError('missing_component', {product => $product});
} }
# It could have no versions... # It could have no versions...
elsif (!@{$product->versions}) { elsif (!@{$product->versions}) {
ThrowUserError ('missing_version', {product => $product->name}); ThrowUserError ('missing_version', {product => $product});
} }
die "can_enter_product reached an unreachable location."; die "can_enter_product reached an unreachable location.";
...@@ -726,6 +770,20 @@ sub get_accessible_products { ...@@ -726,6 +770,20 @@ sub get_accessible_products {
return [ values %products ]; return [ values %products ];
} }
sub check_can_admin_product {
my ($self, $product_name) = @_;
# First make sure the product name is valid.
my $product = Bugzilla::Product::check_product($product_name);
($self->in_group('editcomponents', $product->id)
&& $self->can_see_product($product->name))
|| ThrowUserError('product_access_denied', {product => $product->name});
# Return the validated product object.
return $product;
}
sub can_request_flag { sub can_request_flag {
my ($self, $flag_type) = @_; my ($self, $flag_type) = @_;
...@@ -861,23 +919,23 @@ sub derive_regexp_groups { ...@@ -861,23 +919,23 @@ sub derive_regexp_groups {
sub product_responsibilities { sub product_responsibilities {
my $self = shift; my $self = shift;
my $dbh = Bugzilla->dbh;
return $self->{'product_resp'} if defined $self->{'product_resp'}; return $self->{'product_resp'} if defined $self->{'product_resp'};
return [] unless $self->id; return [] unless $self->id;
my $h = Bugzilla->dbh->selectall_arrayref( my $comp_ids = $dbh->selectcol_arrayref('SELECT id FROM components
qq{SELECT products.name AS productname, WHERE initialowner = ?
components.name AS componentname, OR initialqacontact = ?',
initialowner, undef, ($self->id, $self->id));
initialqacontact
FROM products, components # We cannot |use| it, because Component.pm already |use|s User.pm.
WHERE products.id = components.product_id require Bugzilla::Component;
AND ? IN (initialowner, initialqacontact) my @components;
}, push(@components, new Bugzilla::Component($_)) foreach (@$comp_ids);
{'Slice' => {}}, $self->id);
$self->{'product_resp'} = $h; $self->{'product_resp'} = \@components;
return $self->{'product_resp'};
return $h;
} }
sub can_bless { sub can_bless {
...@@ -1797,9 +1855,11 @@ Returns a string containing a comma-separated list of numeric group ids. If ...@@ -1797,9 +1855,11 @@ Returns a string containing a comma-separated list of numeric group ids. If
the user is not a member of any groups, returns "-1". This is most often used the user is not a member of any groups, returns "-1". This is most often used
within an SQL IN() function. within an SQL IN() function.
=item C<in_group> =item C<in_group($group_name, $product_id)>
Determines whether or not a user is in the given group by name. Determines whether or not a user is in the given group by name.
If $product_id is given, it also checks for local privileges for
this product.
=item C<in_group_id> =item C<in_group_id>
...@@ -1814,6 +1874,12 @@ The arrayref consists of the groups the user can bless, taking into account ...@@ -1814,6 +1874,12 @@ The arrayref consists of the groups the user can bless, taking into account
that having editusers permissions means that you can bless all groups, and that having editusers permissions means that you can bless all groups, and
that you need to be aware of a group in order to bless a group. that you need to be aware of a group in order to bless a group.
=item C<get_products_by_permission($group)>
Returns a list of product objects for which the user has $group privileges
and which he can access.
$group must be one of the groups defined in PER_PRODUCT_PRIVILEGES.
=item C<can_see_user(user)> =item C<can_see_user(user)>
Returns 1 if the specified user account exists and is visible to the user, Returns 1 if the specified user account exists and is visible to the user,
...@@ -1888,6 +1954,14 @@ method should be called in such a case to force reresolution of these groups. ...@@ -1888,6 +1954,14 @@ method should be called in such a case to force reresolution of these groups.
Returns: an array of product objects. Returns: an array of product objects.
=item C<check_can_admin_product($product_name)>
Description: Checks whether the user is allowed to administrate the product.
Params: $product_name - a product name.
Returns: On success, a product object. On failure, an error is thrown.
=item C<can_request_flag($flag_type)> =item C<can_request_flag($flag_type)>
Description: Checks whether the user can request flags of the given type. Description: Checks whether the user can request flags of the given type.
...@@ -1937,29 +2011,8 @@ list). ...@@ -1937,29 +2011,8 @@ list).
=item C<product_responsibilities> =item C<product_responsibilities>
Retrieve user's product responsibilities as a list of hashes. Retrieve user's product responsibilities as a list of component objects.
One hash per Bugzilla component the user has a responsibility for. Each object is a component the user has a responsibility for.
These are the hash keys:
=over
=item productname
Name of the product.
=item componentname
Name of the component.
=item initialowner
User ID of default assignee.
=item initialqacontact
User ID of default QA contact.
=back
=item C<can_bless> =item C<can_bless>
......
...@@ -416,12 +416,14 @@ sub enter ...@@ -416,12 +416,14 @@ sub enter
ValidateBugID($bugid); ValidateBugID($bugid);
validateCanChangeBug($bugid); validateCanChangeBug($bugid);
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
my $bug = new Bugzilla::Bug($bugid, $user->id);
# Retrieve the attachments the user can edit from the database and write # Retrieve the attachments the user can edit from the database and write
# them into an array of hashes where each hash represents one attachment. # them into an array of hashes where each hash represents one attachment.
my $canEdit = ""; my $canEdit = "";
if (!Bugzilla->user->in_group("editbugs")) { if (!$user->in_group('editbugs', $bug->product_id)) {
$canEdit = "AND submitter_id = " . Bugzilla->user->id; $canEdit = "AND submitter_id = " . $user->id;
} }
my $attachments = $dbh->selectall_arrayref( my $attachments = $dbh->selectall_arrayref(
"SELECT attach_id AS id, description, isprivate "SELECT attach_id AS id, description, isprivate
...@@ -430,24 +432,13 @@ sub enter ...@@ -430,24 +432,13 @@ sub enter
AND isobsolete = 0 $canEdit AND isobsolete = 0 $canEdit
ORDER BY attach_id",{'Slice' =>{}}, $bugid); ORDER BY attach_id",{'Slice' =>{}}, $bugid);
# Retrieve the bug summary (for displaying on screen) and assignee.
my ($bugsummary, $assignee_id) = $dbh->selectrow_array(
"SELECT short_desc, assigned_to FROM bugs
WHERE bug_id = ?", undef, $bugid);
# Define the variables and functions that will be passed to the UI template. # Define the variables and functions that will be passed to the UI template.
$vars->{'bugid'} = $bugid; $vars->{'bug'} = $bug;
$vars->{'attachments'} = $attachments; $vars->{'attachments'} = $attachments;
$vars->{'bugassignee_id'} = $assignee_id;
$vars->{'bugsummary'} = $bugsummary;
my ($product_id, $component_id)= $dbh->selectrow_array(
"SELECT product_id, component_id FROM bugs
WHERE bug_id = ?", undef, $bugid);
my $flag_types = Bugzilla::FlagType::match({'target_type' => 'attachment', my $flag_types = Bugzilla::FlagType::match({'target_type' => 'attachment',
'product_id' => $product_id, 'product_id' => $bug->product_id,
'component_id' => $component_id}); 'component_id' => $bug->component_id});
$vars->{'flag_types'} = $flag_types; $vars->{'flag_types'} = $flag_types;
$vars->{'any_flags_requesteeble'} = grep($_->is_requesteeble, @$flag_types); $vars->{'any_flags_requesteeble'} = grep($_->is_requesteeble, @$flag_types);
...@@ -487,7 +478,7 @@ sub insert ...@@ -487,7 +478,7 @@ sub insert
# Assign the bug to the user, if they are allowed to take it # Assign the bug to the user, if they are allowed to take it
my $owner = ""; my $owner = "";
if ($cgi->param('takebug') && Bugzilla->user->in_group("editbugs")) { if ($cgi->param('takebug') && $user->in_group('editbugs', $bug->product_id)) {
my @fields = ("assigned_to", "bug_status", "resolution", "everconfirmed", my @fields = ("assigned_to", "bug_status", "resolution", "everconfirmed",
"login_name"); "login_name");
...@@ -606,8 +597,9 @@ sub update ...@@ -606,8 +597,9 @@ sub update
# Retrieve and validate parameters # Retrieve and validate parameters
ValidateComment(scalar $cgi->param('comment')); ValidateComment(scalar $cgi->param('comment'));
my ($attach_id, $bugid) = validateID(); my ($attach_id, $bugid) = validateID();
my $bug = new Bugzilla::Bug($bugid);
my $attachment = Bugzilla::Attachment->get($attach_id); my $attachment = Bugzilla::Attachment->get($attach_id);
$attachment->validate_can_edit; $attachment->validate_can_edit($bug->product_id);
validateCanChangeAttachment($attach_id); validateCanChangeAttachment($attach_id);
Bugzilla::Attachment->validate_description(THROW_ERROR); Bugzilla::Attachment->validate_description(THROW_ERROR);
Bugzilla::Attachment->validate_is_patch(THROW_ERROR); Bugzilla::Attachment->validate_is_patch(THROW_ERROR);
...@@ -636,7 +628,6 @@ sub update ...@@ -636,7 +628,6 @@ sub update
}); });
Bugzilla::Flag::validate($cgi, $bugid, $attach_id); Bugzilla::Flag::validate($cgi, $bugid, $attach_id);
my $bug = new Bugzilla::Bug($bugid);
# Lock database tables in preparation for updating the attachment. # Lock database tables in preparation for updating the attachment.
$dbh->bz_lock_tables('attachments WRITE', 'flags WRITE' , $dbh->bz_lock_tables('attachments WRITE', 'flags WRITE' ,
'flagtypes READ', 'fielddefs READ', 'bugs_activity WRITE', 'flagtypes READ', 'fielddefs READ', 'bugs_activity WRITE',
...@@ -784,7 +775,6 @@ sub delete_attachment { ...@@ -784,7 +775,6 @@ sub delete_attachment {
# Make sure the administrator is allowed to edit this attachment. # Make sure the administrator is allowed to edit this attachment.
my ($attach_id, $bug_id) = validateID(); my ($attach_id, $bug_id) = validateID();
my $attachment = Bugzilla::Attachment->get($attach_id); my $attachment = Bugzilla::Attachment->get($attach_id);
$attachment->validate_can_edit;
validateCanChangeAttachment($attach_id); validateCanChangeAttachment($attach_id);
$attachment->datasize || ThrowUserError('attachment_removed'); $attachment->datasize || ThrowUserError('attachment_removed');
......
...@@ -36,7 +36,6 @@ use Bugzilla::Series; ...@@ -36,7 +36,6 @@ use Bugzilla::Series;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::User; use Bugzilla::User;
use Bugzilla::Product;
use Bugzilla::Component; use Bugzilla::Component;
use Bugzilla::Bug; use Bugzilla::Bug;
use Bugzilla::Token; use Bugzilla::Token;
...@@ -76,6 +75,7 @@ my $whoid = $user->id; ...@@ -76,6 +75,7 @@ my $whoid = $user->id;
print $cgi->header(); print $cgi->header();
$user->in_group('editcomponents') $user->in_group('editcomponents')
|| scalar(@{$user->get_products_by_permission('editcomponents')})
|| ThrowUserError("auth_failure", {group => "editcomponents", || ThrowUserError("auth_failure", {group => "editcomponents",
action => "edit", action => "edit",
object => "components"}); object => "components"});
...@@ -94,7 +94,13 @@ my $token = $cgi->param('token'); ...@@ -94,7 +94,13 @@ my $token = $cgi->param('token');
# #
unless ($product_name) { unless ($product_name) {
$vars->{'products'} = $user->get_selectable_products; my $selectable_products = $user->get_selectable_products;
# If the user has editcomponents privs for some products only,
# we have to restrict the list of products to display.
unless ($user->in_group('editcomponents')) {
$selectable_products = $user->get_products_by_permission('editcomponents');
}
$vars->{'products'} = $selectable_products;
$vars->{'showbugcounts'} = $showbugcounts; $vars->{'showbugcounts'} = $showbugcounts;
$template->process("admin/components/select-product.html.tmpl", $vars) $template->process("admin/components/select-product.html.tmpl", $vars)
...@@ -102,13 +108,7 @@ unless ($product_name) { ...@@ -102,13 +108,7 @@ unless ($product_name) {
exit; exit;
} }
# First make sure the product name is valid. my $product = $user->check_can_admin_product($product_name);
my $product = Bugzilla::Product::check_product($product_name);
# Then make sure the user is allowed to edit properties of this product.
$user->can_see_product($product->name)
|| ThrowUserError('product_access_denied', {product => $product->name});
# #
# action='' -> Show nice list of components # action='' -> Show nice list of components
......
...@@ -23,7 +23,6 @@ use Bugzilla; ...@@ -23,7 +23,6 @@ use Bugzilla;
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Product;
use Bugzilla::Milestone; use Bugzilla::Milestone;
use Bugzilla::Bug; use Bugzilla::Bug;
use Bugzilla::Token; use Bugzilla::Token;
...@@ -43,6 +42,7 @@ my $whoid = $user->id; ...@@ -43,6 +42,7 @@ my $whoid = $user->id;
print $cgi->header(); print $cgi->header();
$user->in_group('editcomponents') $user->in_group('editcomponents')
|| scalar(@{$user->get_products_by_permission('editcomponents')})
|| ThrowUserError("auth_failure", {group => "editcomponents", || ThrowUserError("auth_failure", {group => "editcomponents",
action => "edit", action => "edit",
object => "milestones"}); object => "milestones"});
...@@ -62,7 +62,13 @@ my $token = $cgi->param('token'); ...@@ -62,7 +62,13 @@ my $token = $cgi->param('token');
# #
unless ($product_name) { unless ($product_name) {
$vars->{'products'} = $user->get_selectable_products; my $selectable_products = $user->get_selectable_products;
# If the user has editcomponents privs for some products only,
# we have to restrict the list of products to display.
unless ($user->in_group('editcomponents')) {
$selectable_products = $user->get_products_by_permission('editcomponents');
}
$vars->{'products'} = $selectable_products;
$vars->{'showbugcounts'} = $showbugcounts; $vars->{'showbugcounts'} = $showbugcounts;
$template->process("admin/milestones/select-product.html.tmpl", $vars) $template->process("admin/milestones/select-product.html.tmpl", $vars)
...@@ -70,13 +76,7 @@ unless ($product_name) { ...@@ -70,13 +76,7 @@ unless ($product_name) {
exit; exit;
} }
# First make sure the product name is valid. my $product = $user->check_can_admin_product($product_name);
my $product = Bugzilla::Product::check_product($product_name);
# Then make sure the user is allowed to edit properties of this product.
$user->can_see_product($product->name)
|| ThrowUserError('product_access_denied', {product => $product->name});
# #
# action='' -> Show nice list of milestones # action='' -> Show nice list of milestones
......
...@@ -364,7 +364,6 @@ if ($action eq 'search') { ...@@ -364,7 +364,6 @@ if ($action eq 'search') {
action => "delete", action => "delete",
object => "users"}); object => "users"});
$vars->{'otheruser'} = $otherUser; $vars->{'otheruser'} = $otherUser;
$vars->{'editcomponents'} = Bugzilla->user->in_group('editcomponents');
# Find other cross references. # Find other cross references.
$vars->{'assignee_or_qa'} = $dbh->selectrow_array( $vars->{'assignee_or_qa'} = $dbh->selectrow_array(
......
...@@ -35,7 +35,6 @@ use Bugzilla; ...@@ -35,7 +35,6 @@ use Bugzilla;
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Product;
use Bugzilla::Version; use Bugzilla::Version;
use Bugzilla::Token; use Bugzilla::Token;
...@@ -53,6 +52,7 @@ my $user = Bugzilla->login(LOGIN_REQUIRED); ...@@ -53,6 +52,7 @@ my $user = Bugzilla->login(LOGIN_REQUIRED);
print $cgi->header(); print $cgi->header();
$user->in_group('editcomponents') $user->in_group('editcomponents')
|| scalar(@{$user->get_products_by_permission('editcomponents')})
|| ThrowUserError("auth_failure", {group => "editcomponents", || ThrowUserError("auth_failure", {group => "editcomponents",
action => "edit", action => "edit",
object => "versions"}); object => "versions"});
...@@ -71,7 +71,13 @@ my $token = $cgi->param('token'); ...@@ -71,7 +71,13 @@ my $token = $cgi->param('token');
# #
unless ($product_name) { unless ($product_name) {
$vars->{'products'} = $user->get_selectable_products; my $selectable_products = $user->get_selectable_products;
# If the user has editcomponents privs for some products only,
# we have to restrict the list of products to display.
unless ($user->in_group('editcomponents')) {
$selectable_products = $user->get_products_by_permission('editcomponents');
}
$vars->{'products'} = $selectable_products;
$vars->{'showbugcounts'} = $showbugcounts; $vars->{'showbugcounts'} = $showbugcounts;
$template->process("admin/versions/select-product.html.tmpl", $vars) $template->process("admin/versions/select-product.html.tmpl", $vars)
...@@ -79,13 +85,7 @@ unless ($product_name) { ...@@ -79,13 +85,7 @@ unless ($product_name) {
exit; exit;
} }
# First make sure the product name is valid. my $product = $user->check_can_admin_product($product_name);
my $product = Bugzilla::Product::check_product($product_name);
# Then make sure the user is allowed to edit properties of this product.
$user->can_see_product($product->name)
|| ThrowUserError('product_access_denied', {product => $product->name});
# #
# action='' -> Show nice list of versions # action='' -> Show nice list of versions
......
...@@ -300,6 +300,9 @@ sub pickos { ...@@ -300,6 +300,9 @@ sub pickos {
# End of subroutines # End of subroutines
############################################################################## ##############################################################################
my $has_editbugs = $user->in_group('editbugs', $product->id);
my $has_canconfirm = $user->in_group('canconfirm', $product->id);
# If a user is trying to clone a bug # If a user is trying to clone a bug
# Check that the user has authorization to view the parent bug # Check that the user has authorization to view the parent bug
# Create an instance of Bug that holds the info from the parent # Create an instance of Bug that holds the info from the parent
...@@ -327,11 +330,11 @@ $vars->{'op_sys'} = get_legal_field_values('op_sys'); ...@@ -327,11 +330,11 @@ $vars->{'op_sys'} = get_legal_field_values('op_sys');
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count(); $vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
$vars->{'assigned_to'} = formvalue('assigned_to'); $vars->{'assigned_to'} = formvalue('assigned_to');
$vars->{'assigned_to_disabled'} = !Bugzilla->user->in_group('editbugs'); $vars->{'assigned_to_disabled'} = !$has_editbugs;
$vars->{'cc_disabled'} = 0; $vars->{'cc_disabled'} = 0;
$vars->{'qa_contact'} = formvalue('qa_contact'); $vars->{'qa_contact'} = formvalue('qa_contact');
$vars->{'qa_contact_disabled'} = !Bugzilla->user->in_group('editbugs'); $vars->{'qa_contact_disabled'} = !$has_editbugs;
$vars->{'cloned_bug_id'} = $cloned_bug_id; $vars->{'cloned_bug_id'} = $cloned_bug_id;
...@@ -465,7 +468,7 @@ if ( Bugzilla->params->{'usetargetmilestone'} ) { ...@@ -465,7 +468,7 @@ if ( Bugzilla->params->{'usetargetmilestone'} ) {
# to let them mark bugs as ASSIGNED) # to let them mark bugs as ASSIGNED)
my @status; my @status;
if ($user->in_group('editbugs') || $user->in_group('canconfirm')) { if ($has_editbugs || $has_canconfirm) {
@status = ('NEW', 'ASSIGNED'); @status = ('NEW', 'ASSIGNED');
} }
elsif (!$product->votes_to_confirm) { elsif (!$product->votes_to_confirm) {
......
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
[% IF has_bits.size %] [% IF has_bits.size %]
You have the following permission [% terms.bits %] set on your account: You have the following permission [% terms.bits %] set on your account:
<p> <p>
<br>
<table align="center"> <table align="center">
[% FOREACH bit_description = has_bits %] [% FOREACH bit_description = has_bits %]
<tr> <tr>
...@@ -47,6 +46,23 @@ ...@@ -47,6 +46,23 @@
</tr> </tr>
[% END %] [% END %]
</table> </table>
[% FOREACH privs = ["editcomponents", "canconfirm", "editbugs"] %]
[% SET products = ${"local_$privs"} %]
[% IF products && products.size %]
<br>
<p>
You also have local '[% privs FILTER html %]' privileges
for the following products:
</p>
<p>
[% FOREACH product = products %]
[% product.name FILTER html %]<br>
[% END %]
</p>
[% END %]
[% END %]
[% ELSE %] [% ELSE %]
There are no permission [% terms.bits %] set on your account. There are no permission [% terms.bits %] set on your account.
[% END %] [% END %]
......
...@@ -112,6 +112,9 @@ versions:</a> ...@@ -112,6 +112,9 @@ versions:</a>
[% g.othercontrol FILTER html %] [% g.othercontrol FILTER html %]
[% IF g.entry %], ENTRY[% END %] [% IF g.entry %], ENTRY[% END %]
[% IF g.canedit %], CANEDIT[% END %] [% IF g.canedit %], CANEDIT[% END %]
[% IF g.editcomponents %], editcomponents[% END %]
[% IF g.canconfirm %], canconfirm[% END %]
[% IF g.editbugs %], editbugs[% END %]
[% ELSE %] [% ELSE %]
DISABLED DISABLED
[% END %] [% END %]
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
<hr> <hr>
[% UNLESS no_add_product_link %] [% UNLESS no_add_product_link || !user.in_group("editcomponents") %]
<a title="Add a product" <a title="Add a product"
href="editproducts.cgi?action=add">Add</a> a product[% -%] href="editproducts.cgi?action=add">Add</a> a product[% -%]
[%# Strictly speaking, we should not have to check for a [%# Strictly speaking, we should not have to check for a
......
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
<th>MemberControl</th> <th>MemberControl</th>
<th>OtherControl</th> <th>OtherControl</th>
<th>Canedit</th> <th>Canedit</th>
<th>editcomponents</th>
<th>canconfirm</th>
<th>editbugs</th>
<th>[% terms.Bugs %]</th> <th>[% terms.Bugs %]</th>
</tr> </tr>
[% FOREACH group = groups %] [% FOREACH group = groups %]
...@@ -50,7 +53,7 @@ ...@@ -50,7 +53,7 @@
<td> <td>
[% group.name FILTER html %] [% group.name FILTER html %]
</td> </td>
<td align="center" colspan=4> <td align="center" colspan=7>
Disabled Disabled
</td> </td>
<td> <td>
...@@ -119,6 +122,18 @@ ...@@ -119,6 +122,18 @@
[% " checked=\"checked\"" IF group.canedit %]> [% " checked=\"checked\"" IF group.canedit %]>
</td> </td>
<td> <td>
<input type=checkbox value=1 name=editcomponents_[% group.id %]
[% " checked=\"checked\"" IF group.editcomponents %]>
</td>
<td>
<input type=checkbox value=1 name=canconfirm_[% group.id %]
[% " checked=\"checked\"" IF group.canconfirm %]>
</td>
<td>
<input type=checkbox value=1 name=editbugs_[% group.id %]
[% " checked=\"checked\"" IF group.editbugs %]>
</td>
<td>
[% group.bugcount %] [% group.bugcount %]
</td> </td>
</tr> </tr>
...@@ -146,6 +161,21 @@ the groups with Canedit selected. ONLY users who are members of ...@@ -146,6 +161,21 @@ the groups with Canedit selected. ONLY users who are members of
all the canedit groups will be able to edit. This is an additional all the canedit groups will be able to edit. This is an additional
restriction that further restricts what can be edited by a user. restriction that further restricts what can be edited by a user.
<p> <p>
The following settings control let you choose privileges on a <b>per-product basis</b>.
This is a convenient way to give privileges to some users for some products
only, without having to give them global privileges which would affect all
products:
<p>
Any group having <b>editcomponents</b> selected allows users who are
in this group to edit all aspects of this product, including components,
milestones and versions.
<p>
Any group having <b>canconfirm</b> selected allows users who are
in this group to confirm [% terms.bugs %] in this product.
<p>
Any group having <b>editbugs</b> selected allows users who are
in this group to edit all fields of [% terms.bugs %] in this product.
<p>
The <b>MemberControl</b> and <b>OtherControl</b> fields The <b>MemberControl</b> and <b>OtherControl</b> fields
indicate which [% terms.bugs %] will be placed in indicate which [% terms.bugs %] will be placed in
this group according to the following definitions. this group according to the following definitions.
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
# listselectionvalues: selection values to recreate the current user # listselectionvalues: selection values to recreate the current user
# list. # list.
# editusers: is viewing user member of editusers? # editusers: is viewing user member of editusers?
# editcomponents: is viewing user member of editcomponents?
# otheruser: Bugzilla::User object of the viewed user. # otheruser: Bugzilla::User object of the viewed user.
# reporter: number of bugs reported by the user # reporter: number of bugs reported by the user
# assignee_or_qa: number of bugs the user is either the assignee # assignee_or_qa: number of bugs the user is either the assignee
...@@ -57,8 +56,8 @@ ...@@ -57,8 +56,8 @@
%] %]
[% responsibilityterms = { [% responsibilityterms = {
'initialowner' => 'Default Assignee', 'default_assignee' => 'Default Assignee',
'initialqacontact' => 'Default QA Contact' 'default_qa_contact' => 'Default QA Contact'
} }
%] %]
...@@ -93,21 +92,21 @@ ...@@ -93,21 +92,21 @@
[% FOREACH component = otheruser.product_responsibilities %] [% FOREACH component = otheruser.product_responsibilities %]
<li> <li>
[% andstring = '' %] [% andstring = '' %]
[% FOREACH responsibility = ['initialowner', 'initialqacontact'] %] [% FOREACH responsibility = ['default_assignee', 'default_qa_contact'] %]
[% IF component.$responsibility == otheruser.id %] [% IF component.${responsibility}.id == otheruser.id %]
[% andstring %] [% responsibilityterms.$responsibility %] [% andstring %] [% responsibilityterms.$responsibility %]
[% andstring = ' and ' %] [% andstring = ' and ' %]
[% END %] [% END %]
[% END %] [% END %]
for for
[% IF editcomponents %] [% IF user.in_group("editcomponents", component.product_id) %]
<a href="editcomponents.cgi?action=edit&amp;product= <a href="editcomponents.cgi?action=edit&amp;product=
[% component.productname FILTER url_quote %]&amp;component= [% component.product.name FILTER url_quote %]&amp;component=
[% component.componentname FILTER url_quote %]"> [% component.name FILTER url_quote %]">
[% END %] [% END %]
[%+ component.productname FILTER html %]: [%+ component.product.name FILTER html %]:
[% component.componentname FILTER html %] [% component.name FILTER html %]
[% IF editcomponents %] [% IF user.in_group("editcomponents", component.product_id) %]
</a> </a>
[% END %] [% END %]
</li> </li>
...@@ -125,7 +124,7 @@ ...@@ -125,7 +124,7 @@
one product. one product.
</p> </p>
<p> <p>
[% IF editcomponents %] [% IF user.in_group("editcomponents", component.product_id) %]
Change this by clicking the product editing links above, Change this by clicking the product editing links above,
[% ELSE %] [% ELSE %]
For now, you can For now, you can
......
...@@ -25,10 +25,10 @@ ...@@ -25,10 +25,10 @@
[% PROCESS global/variables.none.tmpl %] [% PROCESS global/variables.none.tmpl %]
[%# Define strings that will serve as the title and header of this page %] [%# Define strings that will serve as the title and header of this page %]
[% title = BLOCK %]Create New Attachment for [% terms.Bug %] #[% bugid %][% END %] [% title = BLOCK %]Create New Attachment for [% terms.Bug %] #[% bug.bug_id %][% END %]
[% header = BLOCK %]Create New Attachment for [% header = BLOCK %]Create New Attachment for
[%+ "$terms.Bug $bugid" FILTER bug_link(bugid) FILTER none %][% END %] [%+ "$terms.Bug $bug.bug_id" FILTER bug_link(bug.bug_id) FILTER none %][% END %]
[% subheader = BLOCK %][% bugsummary FILTER html %][% END %] [% subheader = BLOCK %][% bug.short_desc FILTER html %][% END %]
[% PROCESS global/header.html.tmpl [% PROCESS global/header.html.tmpl
title = title title = title
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
%] %]
<form name="entryform" method="post" action="attachment.cgi" enctype="multipart/form-data"> <form name="entryform" method="post" action="attachment.cgi" enctype="multipart/form-data">
<input type="hidden" name="bugid" value="[% bugid %]"> <input type="hidden" name="bugid" value="[% bug.bug_id %]">
<input type="hidden" name="action" value="insert"> <input type="hidden" name="action" value="insert">
<table class="attachment_entry"> <table class="attachment_entry">
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
</td> </td>
</tr> </tr>
[% IF (user.id != bugassignee_id) AND user.groups.editbugs %] [% IF (user.id != bug.assigned_to.id) AND user.in_group("editbugs", bug.product_id) %]
<tr> <tr>
<th>Reassignment:</th> <th>Reassignment:</th>
<td> <td>
......
...@@ -476,7 +476,7 @@ function handleWantsAttachment(wants_attachment) { ...@@ -476,7 +476,7 @@ function handleWantsAttachment(wants_attachment) {
</td> </td>
</tr> </tr>
[% IF user.in_group('editbugs') %] [% IF user.in_group('editbugs', product.id) %]
[% IF use_keywords %] [% IF use_keywords %]
<tr> <tr>
<td align="right" valign="top"> <td align="right" valign="top">
......
...@@ -424,7 +424,7 @@ ...@@ -424,7 +424,7 @@
], ],
'attachment/create.html.tmpl' => [ 'attachment/create.html.tmpl' => [
'bugid', 'bug.bug_id',
'attachment.id', 'attachment.id',
], ],
......
...@@ -111,8 +111,8 @@ ...@@ -111,8 +111,8 @@
href="editparams.cgi">' IF user.groups.tweakparams %] href="editparams.cgi">' IF user.groups.tweakparams %]
[% '<link rel="Administration" title="Users" [% '<link rel="Administration" title="Users"
href="editusers.cgi">' IF user.groups.editusers %] href="editusers.cgi">' IF user.groups.editusers %]
[% '<link rel="Administration" title="Products" [% '<link rel="Administration" title="Products" href="editproducts.cgi">'
href="editproducts.cgi">' IF user.groups.editcomponents %] IF user.groups.editcomponents || user.get_products_by_permission("editcomponents").size %]
[% '<link rel="Administration" title="Flag Types" [% '<link rel="Administration" title="Flag Types"
href="editflagtypes.cgi">' IF user.groups.editcomponents %] href="editflagtypes.cgi">' IF user.groups.editcomponents %]
[% '<link rel="Administration" title="Groups" [% '<link rel="Administration" title="Groups"
......
...@@ -58,10 +58,13 @@ ...@@ -58,10 +58,13 @@
<li><span class="separator">[% sep %]</span><a href="editclassifications.cgi">Classifications</a></li> <li><span class="separator">[% sep %]</span><a href="editclassifications.cgi">Classifications</a></li>
[% sep = "| " %] [% sep = "| " %]
[% END %] [% END %]
[% IF user.groups.editcomponents %] [% IF user.groups.editcomponents || user.get_products_by_permission("editcomponents").size %]
<li><span class="separator">[% sep %]</span><a href="editproducts.cgi">Products</a></li> <li><span class="separator">[% sep %]</span><a href="editproducts.cgi">Products</a></li>
[% sep = "| " %] [% sep = "| " %]
[% END %]
[% IF user.groups.editcomponents %]
<li><span class="separator">[% sep %]</span><a href="editflagtypes.cgi">Flags</a></li> <li><span class="separator">[% sep %]</span><a href="editflagtypes.cgi">Flags</a></li>
[% sep = "| " %]
[% END %] [% END %]
[% IF user.groups.admin %] [% IF user.groups.admin %]
<li><span class="separator">[% sep %]</span><a href="editfields.cgi">Custom Fields</a></li> <li><span class="separator">[% sep %]</span><a href="editfields.cgi">Custom Fields</a></li>
......
...@@ -917,11 +917,11 @@ ...@@ -917,11 +917,11 @@
[% title = "Missing Component" %] [% title = "Missing Component" %]
[% admindocslinks = {'products.html' => 'Administering products', [% admindocslinks = {'products.html' => 'Administering products',
'components.html' => 'Creating a component'} %] 'components.html' => 'Creating a component'} %]
Sorry, the product <em>[% product FILTER html %]</em> Sorry, the product <em>[% product.name FILTER html %]</em>
has to have at least one component in order for you to has to have at least one component in order for you to
enter [% terms.abug %] into it.<br> enter [% terms.abug %] into it.<br>
[% IF user.in_group("editcomponents") %] [% IF user.in_group("editcomponents", product.id) %]
<a href="editcomponents.cgi?action=add&amp;product=[% product FILTER url_quote %]">Create <a href="editcomponents.cgi?action=add&amp;product=[% product.name FILTER url_quote %]">Create
a new component</a>. a new component</a>.
[% ELSE %] [% ELSE %]
Please contact [% Param("maintainer") %] and ask them Please contact [% Param("maintainer") %] and ask them
...@@ -989,11 +989,11 @@ ...@@ -989,11 +989,11 @@
[% ELSIF error == "missing_version" %] [% ELSIF error == "missing_version" %]
[% title = "Missing Version" %] [% title = "Missing Version" %]
[% admindocslinks = {'versions.html' => 'Defining versions'} %] [% admindocslinks = {'versions.html' => 'Defining versions'} %]
Sorry, the product <em>[% product FILTER html %]</em> Sorry, the product <em>[% product.name FILTER html %]</em>
has to have at least one version in order for you to has to have at least one version in order for you to
enter [% terms.abug %] into it.<p> enter [% terms.abug %] into it.<p>
[% IF user.in_group("editcomponents") %] [% IF user.in_group("editcomponents", product.id) %]
<a href="editversions.cgi?action=add&amp;product=[% product FILTER url_quote %]">Create <a href="editversions.cgi?action=add&amp;product=[% product.name FILTER url_quote %]">Create
a new version</a>. a new version</a>.
[% ELSE %] [% ELSE %]
Please contact [% Param("maintainer") %] and ask them Please contact [% Param("maintainer") %] and ask them
...@@ -1213,7 +1213,7 @@ ...@@ -1213,7 +1213,7 @@
[% title = BLOCK %]Product closed for [% terms.Bugs %] Entry[% END %] [% title = BLOCK %]Product closed for [% terms.Bugs %] Entry[% END %]
[% admindocslinks = {'products.html' => 'Administering products'} %] [% admindocslinks = {'products.html' => 'Administering products'} %]
Sorry, entering [% terms.bugs %] into the Sorry, entering [% terms.bugs %] into the
product <em>[% product FILTER html %]</em> has been disabled. product <em>[% product.name FILTER html %]</em> has been disabled.
[% ELSIF error == "product_edit_denied" %] [% ELSIF error == "product_edit_denied" %]
[% title = "Product Edit Access Denied" %] [% title = "Product Edit Access Denied" %]
......
...@@ -369,7 +369,13 @@ sub DoPermissions { ...@@ -369,7 +369,13 @@ sub DoPermissions {
push(@set_bits, {"desc" => $desc, "name" => $nam}); push(@set_bits, {"desc" => $desc, "name" => $nam});
} }
} }
# If the user has product specific privileges, inform him about that.
foreach my $privs (PER_PRODUCT_PRIVILEGES) {
next if $user->in_group($privs);
$vars->{"local_$privs"} = $user->get_products_by_permission($privs);
}
$vars->{'has_bits'} = \@has_bits; $vars->{'has_bits'} = \@has_bits;
$vars->{'set_bits'} = \@set_bits; $vars->{'set_bits'} = \@set_bits;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment