]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 189627: Implement per-product privileges - Patch by Frédéric Buclin <LpSolit...
authorlpsolit%gmail.com <>
Sat, 11 Nov 2006 00:51:27 +0000 (00:51 +0000)
committerlpsolit%gmail.com <>
Sat, 11 Nov 2006 00:51:27 +0000 (00:51 +0000)
27 files changed:
Bugzilla/Attachment.pm
Bugzilla/Bug.pm
Bugzilla/Component.pm
Bugzilla/Constants.pm
Bugzilla/DB/Schema.pm
Bugzilla/Install/DB.pm
Bugzilla/Product.pm
Bugzilla/User.pm
attachment.cgi
editcomponents.cgi
editmilestones.cgi
editproducts.cgi
editusers.cgi
editversions.cgi
enter_bug.cgi
template/en/default/account/prefs/permissions.html.tmpl
template/en/default/admin/products/edit.html.tmpl
template/en/default/admin/products/footer.html.tmpl
template/en/default/admin/products/groupcontrol/edit.html.tmpl
template/en/default/admin/users/confirm-delete.html.tmpl
template/en/default/attachment/create.html.tmpl
template/en/default/bug/create/create.html.tmpl
template/en/default/filterexceptions.pl
template/en/default/global/site-navigation.html.tmpl
template/en/default/global/useful-links.html.tmpl
template/en/default/global/user-error.html.tmpl
userprefs.cgi

index ebf8c8de81a42b724869f1a4dbc8621a1a7d353f..c1b3a8a28e19b02c6c6798f3d215662b0e70f6af 100644 (file)
@@ -609,19 +609,22 @@ sub validate_content_type {
 
 =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.
              Only the submitter or someone with editbugs privs can edit it.
              Only the submitter and users in the insider group can view
              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.
 
 =cut
 
 sub validate_can_edit {
-    my $attachment = shift;
+    my ($attachment, $product_id) = @_;
     my $dbh = Bugzilla->dbh;
     my $user = Bugzilla->user;
 
@@ -634,27 +637,27 @@ sub validate_can_edit {
     }
 
     # 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.
     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
              really belong to the given bug and are not already obsolete.
              Moreover, a user cannot mark an attachment as obsolete if
              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.
 
 =cut
 
 sub validate_obsolete {
-    my ($class, $bug_id) = @_;
+    my ($class, $bug) = @_;
     my $cgi = Bugzilla->cgi;
 
     # Make sure the attachment id is valid and the user has permissions to view
@@ -674,12 +677,12 @@ sub validate_obsolete {
         ThrowUserError('invalid_attach_id', $vars) unless $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;
 
-        if ($attachment->bug_id != $bug_id) {
-            $vars->{'my_bug_id'} = $bug_id;
+        if ($attachment->bug_id != $bug->bug_id) {
+            $vars->{'my_bug_id'} = $bug->bug_id;
             $vars->{'attach_bug_id'} = $attachment->bug_id;
             ThrowCodeError('mismatched_bug_ids_on_obsolete', $vars);
         }
@@ -758,7 +761,7 @@ sub insert_attachment_for_bug {
     # Check attachments the user tries to mark as obsolete.
     my @obsolete_attachments;
     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
index 18f8316a965797f3e1ab65dae5621f6407a97eb7..e9c8bf2e9445f950a82d3f94179152a540ec21e1 100755 (executable)
@@ -121,7 +121,6 @@ sub VALIDATORS {
         commentprivacy => \&_check_commentprivacy,
         deadline       => \&_check_deadline,
         estimated_time => \&_check_estimated_time,
-        keywords       => \&_check_keywords,
         op_sys         => \&_check_op_sys,
         priority       => \&_check_priority,
         product        => \&_check_product,
@@ -354,6 +353,8 @@ sub run_create_validators {
 
     $params->{version} = $class->_check_version($product, $params->{version});
 
+    $params->{keywords} = $class->_check_keywords($product, $params->{keywords});
+
     $params->{groups} = $class->_check_groups($product,
         $params->{groups});
 
@@ -381,7 +382,7 @@ sub run_create_validators {
                                     $params->{assigned_to}, $params->{qa_contact});
 
     ($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).
     delete $params->{resolution};
@@ -480,7 +481,7 @@ sub _check_assigned_to {
     $name = trim($name);
     # Default assignee is the component owner.
     my $id;
-    if (!$user->in_group("editbugs") || !$name) {
+    if (!$user->in_group('editbugs', $component->product_id) || !$name) {
         $id = $component->default_assignee->id;
     } else {
         $id = login_to_id($name, THROW_ERROR);
@@ -508,7 +509,8 @@ sub _check_bug_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.
        $status ||= 'NEW';
     }
@@ -599,10 +601,10 @@ sub _check_deadline {
 # Takes two comma/space-separated strings and returns arrayrefs
 # of valid bug IDs.
 sub _check_dependencies {
-    my ($invocant, $depends_on, $blocks) = @_;
+    my ($invocant, $product, $depends_on, $blocks) = @_;
 
     # 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 ||= '';
     $blocks     ||= '';
@@ -676,9 +678,10 @@ sub _check_groups {
 }
 
 sub _check_keywords {
-    my ($invocant, $keyword_string) = @_;
+    my ($invocant, $product, $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;
     foreach my $keyword (split(/[\s,]+/, $keyword_string)) {
@@ -801,7 +804,7 @@ sub _check_qa_contact {
     $name = trim($name);
 
     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.
         $id = $component->default_qa_contact->id || undef;
     } else {
@@ -1207,14 +1210,16 @@ sub user {
     my $user = Bugzilla->user;
     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
                   || $user->id == $self->{assigned_to_id}
                   || (Bugzilla->params->{'useqacontact'}
                       && $self->{'qa_contact_id'}
                       && $user->id == $self->{qa_contact_id});
     my $canconfirm = $unknown_privileges
-                     || $user->in_group("canconfirm");
+                     || $user->in_group('canconfirm', $prod_id);
     my $isreporter = $user->id
                      && $user->id == $self->{reporter_id};
 
@@ -1824,19 +1829,19 @@ sub check_can_change_field {
     # $PrivilegesRequired = 2 : the assignee or an empowered user;
     # $PrivilegesRequired = 3 : an empowered user.
 
-    # Allow anyone with "editbugs" privs to change anything.
-    if ($user->in_group('editbugs')) {
+    # Allow anyone with (product-specific) "editbugs" privs to change anything.
+    if ($user->in_group('editbugs', $self->{'product_id'})) {
         return 1;
     }
 
-    # *Only* users with "canconfirm" privs can confirm bugs.
+    # *Only* users with (product-specific) "canconfirm" privs can confirm bugs.
     if ($field eq 'canconfirm'
         || ($field eq 'bug_status'
             && $oldvalue eq 'UNCONFIRMED'
             && is_open_state($newvalue)))
     {
         $$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.
index 4b9856feba4bff1fbc4d8a6fc70530c0ee57567d..657d0f7287b01011a0fc29b07377cad88af5f598 100644 (file)
@@ -171,6 +171,15 @@ sub 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        ####
 ###############################
@@ -229,7 +238,8 @@ Bugzilla::Component - Bugzilla product component class.
     my $product_id         = $component->product_id;
     my $default_assignee   = $component->default_assignee;
     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 $attach_flag_types  = $component->flag_types->{'attachment'};
 
@@ -305,6 +315,14 @@ Initial CC List.
 
   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
 
 =head1 SUBROUTINES
index 9aefea429f26fca5c7bc36cdf5eb044751021c90..7fb95e8f2ffbd0dccfc26530e1028a79360541d7 100644 (file)
@@ -101,6 +101,7 @@ use File::Basename;
     FULLTEXT_BUGLIST_LIMIT
 
     ADMIN_GROUP_NAME
+    PER_PRODUCT_PRIVILEGES
 
     SENDMAIL_EXE
     SENDMAIL_PATH
@@ -289,6 +290,9 @@ use constant FULLTEXT_BUGLIST_LIMIT => 200;
 # Default administration group name.
 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)
 use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe';
 # Paths to search for the sendmail binary (non-Windows)
index 4235be5add8fac2329c6da9139eca03c927d3c93..6846691e2a957e942a704a2990393293cf093887 100644 (file)
@@ -777,6 +777,12 @@ use constant ABSTRACT_SCHEMA => {
             membercontrol => {TYPE => 'BOOLEAN', NOTNULL => 1},
             othercontrol  => {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 => [
             group_control_map_product_id_idx =>
index 863ce9bfa3dfc538ae24af10b43a008fae7684dd..9592976b6cb95bacc29095276d1cc0daf17bfac0 100644 (file)
@@ -501,6 +501,14 @@ sub update_table_definitions {
     $dbh->bz_alter_column('longdescs', 'thetext', 
         { 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 #
     ################################################################
index 6f65eae294837cb296e1d61600cda6032579b375..4edc7ef8575f3a64b322eded93390e6a376a57d1 100644 (file)
@@ -85,7 +85,10 @@ sub group_controls {
                        group_control_map.entry,
                        group_control_map.membercontrol,
                        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
                   LEFT JOIN group_control_map
                         ON groups.id = group_control_map.group_id
index b3bce908708057d1738ebeb8ecb0757233098e60..ff61034ddd3aaf27bdb070f33ef22b4afa894bd4 100644 (file)
@@ -482,8 +482,32 @@ sub bless_groups {
 }
 
 sub in_group {
-    my ($self, $group) = @_;
-    return exists $self->groups->{$group} ? 1 : 0;
+    my ($self, $group, $product_id) = @_;
+    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 {
@@ -492,6 +516,26 @@ sub in_group_id {
     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 {
     my ($self, $otherUser) = @_;
     my $query;
@@ -667,15 +711,15 @@ sub can_enter_product {
     }
     # It could be closed for bug entry...
     elsif ($product->disallow_new) {
-        ThrowUserError('product_disabled', {product => $product->name});
+        ThrowUserError('product_disabled', {product => $product});
     }
     # It could have no components...
     elsif (!@{$product->components}) {
-        ThrowUserError('missing_component', {product => $product->name});
+        ThrowUserError('missing_component', {product => $product});
     }
     # It could have no versions...
     elsif (!@{$product->versions}) {
-        ThrowUserError ('missing_version', {product => $product->name});
+        ThrowUserError ('missing_version', {product => $product});
     }
 
     die "can_enter_product reached an unreachable location.";
@@ -726,6 +770,20 @@ sub get_accessible_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 {
     my ($self, $flag_type) = @_;
 
@@ -861,23 +919,23 @@ sub derive_regexp_groups {
 
 sub product_responsibilities {
     my $self = shift;
+    my $dbh = Bugzilla->dbh;
 
     return $self->{'product_resp'} if defined $self->{'product_resp'};
     return [] unless $self->id;
 
-    my $h = Bugzilla->dbh->selectall_arrayref(
-        qq{SELECT products.name AS productname,
-                  components.name AS componentname,
-                  initialowner,
-                  initialqacontact
-           FROM products, components
-           WHERE products.id = components.product_id
-             AND ? IN (initialowner, initialqacontact)
-          },
-        {'Slice' => {}}, $self->id);
-    $self->{'product_resp'} = $h;
-
-    return $h;
+    my $comp_ids = $dbh->selectcol_arrayref('SELECT id FROM components
+                                              WHERE initialowner = ?
+                                                 OR initialqacontact = ?',
+                                              undef, ($self->id, $self->id));
+
+    # We cannot |use| it, because Component.pm already |use|s User.pm.
+    require Bugzilla::Component;
+    my @components;
+    push(@components, new Bugzilla::Component($_)) foreach (@$comp_ids);
+
+    $self->{'product_resp'} = \@components;
+    return $self->{'product_resp'};
 }
 
 sub can_bless {
@@ -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
 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>
 
@@ -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 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)>
 
 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.
 
  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)>
 
  Description: Checks whether the user can request flags of the given type.
@@ -1937,29 +2011,8 @@ list).
 
 =item C<product_responsibilities>
 
-Retrieve user's product responsibilities as a list of hashes.
-One hash per Bugzilla 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
+Retrieve user's product responsibilities as a list of component objects.
+Each object is a component the user has a responsibility for.
 
 =item C<can_bless>
 
index 2b35b5e2c3ce85d8e1fee9338e4ef5f6f77047b2..7c3605bfd0a72268262644ee2402e36c1d084f8b 100755 (executable)
@@ -416,12 +416,14 @@ sub enter
   ValidateBugID($bugid);
   validateCanChangeBug($bugid);
   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
   # them into an array of hashes where each hash represents one attachment.
   my $canEdit = "";
-  if (!Bugzilla->user->in_group("editbugs")) {
-      $canEdit = "AND submitter_id = " . Bugzilla->user->id;
+  if (!$user->in_group('editbugs', $bug->product_id)) {
+      $canEdit = "AND submitter_id = " . $user->id;
   }
   my $attachments = $dbh->selectall_arrayref(
           "SELECT attach_id AS id, description, isprivate
@@ -430,24 +432,13 @@ sub enter
            AND isobsolete = 0 $canEdit
            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.
-  $vars->{'bugid'} = $bugid;
+  $vars->{'bug'} = $bug;
   $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',
-                                              'product_id'   => $product_id,
-                                              'component_id' => $component_id});
+                                              'product_id'   => $bug->product_id,
+                                              'component_id' => $bug->component_id});
   $vars->{'flag_types'} = $flag_types;
   $vars->{'any_flags_requesteeble'} = grep($_->is_requesteeble, @$flag_types);
 
@@ -487,7 +478,7 @@ sub insert
   # Assign the bug to the user, if they are allowed to take it
   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",
                     "login_name");
@@ -606,8 +597,9 @@ sub update
     # Retrieve and validate parameters
     ValidateComment(scalar $cgi->param('comment'));
     my ($attach_id, $bugid) = validateID();
+    my $bug = new Bugzilla::Bug($bugid);
     my $attachment = Bugzilla::Attachment->get($attach_id);
-    $attachment->validate_can_edit;
+    $attachment->validate_can_edit($bug->product_id);
     validateCanChangeAttachment($attach_id);
     Bugzilla::Attachment->validate_description(THROW_ERROR);
     Bugzilla::Attachment->validate_is_patch(THROW_ERROR);
@@ -636,7 +628,6 @@ sub update
     });
     Bugzilla::Flag::validate($cgi, $bugid, $attach_id);
 
-    my $bug = new Bugzilla::Bug($bugid);
     # Lock database tables in preparation for updating the attachment.
     $dbh->bz_lock_tables('attachments WRITE', 'flags WRITE' ,
           'flagtypes READ', 'fielddefs READ', 'bugs_activity WRITE',
@@ -784,7 +775,6 @@ sub delete_attachment {
     # Make sure the administrator is allowed to edit this attachment.
     my ($attach_id, $bug_id) = validateID();
     my $attachment = Bugzilla::Attachment->get($attach_id);
-    $attachment->validate_can_edit;
     validateCanChangeAttachment($attach_id);
 
     $attachment->datasize || ThrowUserError('attachment_removed');
index 2ff41d6280a61e7fe2e6f5343f7807ae4a43da47..17ad290c52574e625c2883434524eafc95fe227f 100755 (executable)
@@ -36,7 +36,6 @@ use Bugzilla::Series;
 use Bugzilla::Util;
 use Bugzilla::Error;
 use Bugzilla::User;
-use Bugzilla::Product;
 use Bugzilla::Component;
 use Bugzilla::Bug;
 use Bugzilla::Token;
@@ -76,6 +75,7 @@ my $whoid = $user->id;
 print $cgi->header();
 
 $user->in_group('editcomponents')
+  || scalar(@{$user->get_products_by_permission('editcomponents')})
   || ThrowUserError("auth_failure", {group  => "editcomponents",
                                      action => "edit",
                                      object => "components"});
@@ -94,7 +94,13 @@ my $token         = $cgi->param('token');
 #
 
 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;
 
     $template->process("admin/components/select-product.html.tmpl", $vars)
@@ -102,13 +108,7 @@ unless ($product_name) {
     exit;
 }
 
-# First make sure the product name is valid.
-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});
-
+my $product = $user->check_can_admin_product($product_name);
 
 #
 # action='' -> Show nice list of components
index d3a8c7a73f33de609eab2c7f20de646fbb5ae1e9..2df40451acc41865b49fef8642680c221bbf8de0 100755 (executable)
@@ -23,7 +23,6 @@ use Bugzilla;
 use Bugzilla::Constants;
 use Bugzilla::Util;
 use Bugzilla::Error;
-use Bugzilla::Product;
 use Bugzilla::Milestone;
 use Bugzilla::Bug;
 use Bugzilla::Token;
@@ -43,6 +42,7 @@ my $whoid = $user->id;
 print $cgi->header();
 
 $user->in_group('editcomponents')
+  || scalar(@{$user->get_products_by_permission('editcomponents')})
   || ThrowUserError("auth_failure", {group  => "editcomponents",
                                      action => "edit",
                                      object => "milestones"});
@@ -62,7 +62,13 @@ my $token          = $cgi->param('token');
 #
 
 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;
 
     $template->process("admin/milestones/select-product.html.tmpl", $vars)
@@ -70,13 +76,7 @@ unless ($product_name) {
     exit;
 }
 
-# First make sure the product name is valid.
-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});
-
+my $product = $user->check_can_admin_product($product_name);
 
 #
 # action='' -> Show nice list of milestones
index 6fc5da25831bdb62d2eb1da92921af9d389e74d9..8e42130dce04d59e757999b009c69c3648f12dd5 100755 (executable)
@@ -64,6 +64,7 @@ my $vars = {};
 print $cgi->header();
 
 $user->in_group('editcomponents')
+  || scalar(@{$user->get_products_by_permission('editcomponents')})
   || ThrowUserError("auth_failure", {group  => "editcomponents",
                                      action => "edit",
                                      object => "products"});
@@ -100,10 +101,11 @@ if (Bugzilla->params->{'useclassification'}
 #
 
 if (!$action && !$product_name) {
+    my $classification;
     my $products;
 
     if (Bugzilla->params->{'useclassification'}) {
-        my $classification = 
+        $classification =
             Bugzilla::Classification::check_classification($classification_name);
 
         $products = $user->get_selectable_products($classification->id);
@@ -112,6 +114,14 @@ if (!$action && !$product_name) {
         $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')) {
+        $products = $user->get_products_by_permission('editcomponents');
+        if (Bugzilla->params->{'useclassification'}) {
+            @$products = grep {$_->classification_id == $classification->id} @$products;
+        }
+    }
     $vars->{'products'} = $products;
     $vars->{'showbugcounts'} = $showbugcounts;
 
@@ -130,6 +140,13 @@ if (!$action && !$product_name) {
 #
 
 if ($action eq 'add') {
+    # The user must have the global editcomponents privs to add
+    # new products.
+    $user->in_group('editcomponents')
+      || ThrowUserError("auth_failure", {group  => "editcomponents",
+                                         action => "add",
+                                         object => "products"});
+
     if (Bugzilla->params->{'useclassification'}) {
         my $classification = 
             Bugzilla::Classification::check_classification($classification_name);
@@ -149,6 +166,13 @@ if ($action eq 'add') {
 #
 
 if ($action eq 'new') {
+    # The user must have the global editcomponents privs to add
+    # new products.
+    $user->in_group('editcomponents')
+      || ThrowUserError("auth_failure", {group  => "editcomponents",
+                                         action => "add",
+                                         object => "products"});
+
     check_token_data($token, 'add_product');
     # Cleanups and validity checks
 
@@ -325,12 +349,7 @@ if ($action eq 'new') {
 #
 
 if ($action eq 'del') {
-    # First make sure the product name is valid.
-    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});
+    my $product = $user->check_can_admin_product($product_name);
 
     if (Bugzilla->params->{'useclassification'}) {
         my $classification = 
@@ -356,14 +375,9 @@ if ($action eq 'del') {
 #
 
 if ($action eq 'delete') {
+    my $product = $user->check_can_admin_product($product_name);
     check_token_data($token, 'delete_product');
-    # First make sure the product name is valid.
-    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});
-    
     $vars->{'product'} = $product;
 
     if (Bugzilla->params->{'useclassification'}) {
@@ -435,12 +449,7 @@ if ($action eq 'delete') {
 #
 
 if ($action eq 'edit' || (!$action && $product_name)) {
-    # First make sure the product name is valid.
-    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});
+    my $product = $user->check_can_admin_product($product_name);
 
     if (Bugzilla->params->{'useclassification'}) {
         my $classification; 
@@ -490,13 +499,8 @@ if ($action eq 'edit' || (!$action && $product_name)) {
 #
 
 if ($action eq 'updategroupcontrols') {
+    my $product = $user->check_can_admin_product($product_name);
     check_token_data($token, 'edit_group_controls');
-    # First make sure the product name is valid.
-    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});
 
     my @now_na = ();
     my @now_mandatory = ();
@@ -584,19 +588,23 @@ if ($action eq 'updategroupcontrols') {
 
     my $sth_Insert = $dbh->prepare('INSERT INTO group_control_map
                                     (group_id, product_id, entry, membercontrol,
-                                     othercontrol, canedit)
-                                    VALUES (?, ?, ?, ?, ?, ?)');
+                                     othercontrol, canedit, editcomponents,
+                                     canconfirm, editbugs)
+                                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
 
     my $sth_Update = $dbh->prepare('UPDATE group_control_map
                                        SET entry = ?, membercontrol = ?,
-                                           othercontrol = ?, canedit = ?
+                                           othercontrol = ?, canedit = ?,
+                                           editcomponents = ?, canconfirm = ?,
+                                           editbugs = ?
                                      WHERE group_id = ? AND product_id = ?');
 
     my $sth_Delete = $dbh->prepare('DELETE FROM group_control_map
                                      WHERE group_id = ? AND product_id = ?');
 
     $groups = $dbh->selectall_arrayref('SELECT id, name, entry, membercontrol,
-                                               othercontrol, canedit
+                                               othercontrol, canedit,
+                                               editcomponents, canconfirm, editbugs
                                           FROM groups
                                      LEFT JOIN group_control_map
                                             ON group_control_map.group_id = id
@@ -606,35 +614,60 @@ if ($action eq 'updategroupcontrols') {
                                          undef, $product->id);
 
     foreach my $group (@$groups) {
-        my ($groupid, $groupname, $entry, $membercontrol, 
-            $othercontrol, $canedit) = @$group;
+        my ($groupid, $groupname, $entry, $membercontrol, $othercontrol,
+            $canedit, $editcomponents, $canconfirm, $editbugs) = @$group;
         my $newentry = $cgi->param("entry_$groupid") || 0;
         my $newmembercontrol = $cgi->param("membercontrol_$groupid") || 0;
         my $newothercontrol = $cgi->param("othercontrol_$groupid") || 0;
         my $newcanedit = $cgi->param("canedit_$groupid") || 0;
+        my $new_editcomponents = $cgi->param("editcomponents_$groupid") || 0;
+        my $new_canconfirm = $cgi->param("canconfirm_$groupid") || 0;
+        my $new_editbugs = $cgi->param("editbugs_$groupid") || 0;
+
         my $oldentry = $entry;
-        $entry = $entry || 0;
-        $membercontrol = $membercontrol || 0;
-        $othercontrol = $othercontrol || 0;
-        $canedit = $canedit || 0;
+        # Set undefined values to 0.
+        $entry ||= 0;
+        $membercontrol ||= 0;
+        $othercontrol ||= 0;
+        $canedit ||= 0;
+        $editcomponents ||= 0;
+        $canconfirm ||= 0;
+        $editbugs ||= 0;
+
+        # We use them in placeholders only. So it's safe to detaint them.
         detaint_natural($newentry);
         detaint_natural($newothercontrol);
         detaint_natural($newmembercontrol);
         detaint_natural($newcanedit);
-        if ((!defined($oldentry)) && 
-             (($newentry) || ($newmembercontrol) || ($newcanedit))) {
+        detaint_natural($new_editcomponents);
+        detaint_natural($new_canconfirm);
+        detaint_natural($new_editbugs);
+
+        if (!defined($oldentry)
+            && ($newentry || $newmembercontrol || $newcanedit
+                || $new_editcomponents || $new_canconfirm || $new_editbugs))
+        {
             $sth_Insert->execute($groupid, $product->id, $newentry,
-                                 $newmembercontrol, $newothercontrol, $newcanedit);
-        } elsif (($newentry != $entry) 
-                  || ($newmembercontrol != $membercontrol) 
-                  || ($newothercontrol != $othercontrol) 
-                  || ($newcanedit != $canedit)) {
+                                 $newmembercontrol, $newothercontrol, $newcanedit,
+                                 $new_editcomponents, $new_canconfirm, $new_editbugs);
+        }
+        elsif (($newentry != $entry)
+               || ($newmembercontrol != $membercontrol)
+               || ($newothercontrol != $othercontrol)
+               || ($newcanedit != $canedit)
+               || ($new_editcomponents != $editcomponents)
+               || ($new_canconfirm != $canconfirm)
+               || ($new_editbugs != $editbugs))
+        {
             $sth_Update->execute($newentry, $newmembercontrol, $newothercontrol,
-                                 $newcanedit, $groupid, $product->id);
+                                 $newcanedit, $new_editcomponents, $new_canconfirm,
+                                 $new_editbugs, $groupid, $product->id);
         }
 
-        if (($newentry == 0) && ($newmembercontrol == 0)
-          && ($newothercontrol == 0) && ($newcanedit == 0)) {
+        if (!$newentry && !$newmembercontrol && !$newothercontrol
+            && !$newcanedit && !$new_editcomponents && !$new_canconfirm
+            && !$new_editbugs)
+        {
             $sth_Delete->execute($groupid, $product->id);
         }
     }
@@ -759,12 +792,7 @@ if ($action eq 'update') {
 
     my $checkvotes = 0;
 
-    # First make sure the product name is valid.
-    my $product_old = Bugzilla::Product::check_product($product_old_name);
-
-    # Then make sure the user is allowed to edit properties of this product.
-    $user->can_see_product($product_old->name)
-      || ThrowUserError('product_access_denied', {product => $product_old->name});
+    my $product_old = $user->check_can_admin_product($product_old_name);
 
     if (Bugzilla->params->{'useclassification'}) {
         my $classification; 
@@ -1005,16 +1033,12 @@ if ($action eq 'update') {
 #
 
 if ($action eq 'editgroupcontrols') {
-    # First make sure the product name is valid.
-    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});
+    my $product = $user->check_can_admin_product($product_name);
 
     # Display a group if it is either enabled or has bugs for this product.
     my $groups = $dbh->selectall_arrayref(
         'SELECT id, name, entry, membercontrol, othercontrol, canedit,
+                editcomponents, editbugs, canconfirm,
                 isactive, COUNT(bugs.bug_id) AS bugcount
            FROM groups
       LEFT JOIN group_control_map
@@ -1028,7 +1052,8 @@ if ($action eq 'editgroupcontrols') {
           WHERE isbuggroup != 0
             AND (isactive != 0 OR entry IS NOT NULL OR bugs.bug_id IS NOT NULL) ' .
            $dbh->sql_group_by('name', 'id, entry, membercontrol,
-                              othercontrol, canedit, isactive'),
+                              othercontrol, canedit, isactive,
+                              editcomponents, canconfirm, editbugs'),
         {'Slice' => {}}, ($product->id, $product->id));
 
     $vars->{'product'} = $product;
index a1c82db9fd1302cdd2bd2b105cd9de73285501e0..b4e3f698eb62ff1c295db8896a328a990bbe2ef9 100755 (executable)
@@ -364,7 +364,6 @@ if ($action eq 'search') {
                                                   action => "delete",
                                                   object => "users"});
     $vars->{'otheruser'}      = $otherUser;
-    $vars->{'editcomponents'} = Bugzilla->user->in_group('editcomponents');
 
     # Find other cross references.
     $vars->{'assignee_or_qa'} = $dbh->selectrow_array(
index 4867563076c8bc78a9ee34657f3408a59516896e..7bda6215dc03c62283b937960d088a1b7e5c576d 100755 (executable)
@@ -35,7 +35,6 @@ use Bugzilla;
 use Bugzilla::Constants;
 use Bugzilla::Util;
 use Bugzilla::Error;
-use Bugzilla::Product;
 use Bugzilla::Version;
 use Bugzilla::Token;
 
@@ -53,6 +52,7 @@ my $user = Bugzilla->login(LOGIN_REQUIRED);
 print $cgi->header();
 
 $user->in_group('editcomponents')
+  || scalar(@{$user->get_products_by_permission('editcomponents')})
   || ThrowUserError("auth_failure", {group  => "editcomponents",
                                      action => "edit",
                                      object => "versions"});
@@ -71,7 +71,13 @@ my $token        = $cgi->param('token');
 #
 
 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;
 
     $template->process("admin/versions/select-product.html.tmpl", $vars)
@@ -79,13 +85,7 @@ unless ($product_name) {
     exit;
 }
 
-# First make sure the product name is valid.
-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});
-
+my $product = $user->check_can_admin_product($product_name);
 
 #
 # action='' -> Show nice list of versions
index 317bd6d0c68d6b804df70a0cea1d302b38375874..7f5a17e46b95ee7a6fa1ca99b81ec1baf180ea40 100755 (executable)
@@ -300,6 +300,9 @@ sub pickos {
 # 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
 #   Check that the user has authorization to view the parent bug
 #   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');
 $vars->{'use_keywords'}          = 1 if Bugzilla::Keyword::keyword_count();
 
 $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->{'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;
 
@@ -465,7 +468,7 @@ if ( Bugzilla->params->{'usetargetmilestone'} ) {
 #                             to let them mark bugs as ASSIGNED)
 
 my @status;
-if ($user->in_group('editbugs') || $user->in_group('canconfirm')) {
+if ($has_editbugs || $has_canconfirm) {
     @status = ('NEW', 'ASSIGNED');
 }
 elsif (!$product->votes_to_confirm) {
index 77dda1ce42ea1de0d91429b4f279faef9e177196..a178393ae33301d076dedf4e4872bba1645665ae 100644 (file)
@@ -38,7 +38,6 @@
       [% IF has_bits.size %]
         You have the following permission [% terms.bits %] set on your account:
         <p>
-        <br>
         <table align="center">
           [% FOREACH bit_description = has_bits %]
             <tr>
             </tr>
           [% END %]
         </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 %]
         There are no permission [% terms.bits %] set on your account.
       [% END %]
index 0371e3343bd469dd523bae24eea0b3fd6b2e637f..72a5532aaf77bd33682d17540b8de267c202f44a 100644 (file)
@@ -112,6 +112,9 @@ versions:</a>
               [% g.othercontrol FILTER html %]
               [% IF g.entry %], ENTRY[% END %]
               [% IF g.canedit %], CANEDIT[% END %]
+              [% IF g.editcomponents %], editcomponents[% END %]
+              [% IF g.canconfirm %], canconfirm[% END %]
+              [% IF g.editbugs %], editbugs[% END %]
             [% ELSE %]
               DISABLED
             [% END %]
index 480868abdb44cae13838724427f96ce5d7d04294..a6330dbb249454409a4b078906fa5ecce4b014b0 100644 (file)
@@ -45,7 +45,7 @@
 
 <hr>
 
-[% UNLESS no_add_product_link %]
+[% UNLESS no_add_product_link || !user.in_group("editcomponents") %]
   <a title="Add a product"
      href="editproducts.cgi?action=add">Add</a> a product[% -%]
 [%# Strictly speaking, we should not have to check for a
index 32b5e9d8c454e7ff6412c04b7b9ff810f9fb60aa..aff85be0d678b4e20c9c3792d284fd3f76ab62e8 100644 (file)
@@ -42,6 +42,9 @@
       <th>MemberControl</th>
       <th>OtherControl</th>
       <th>Canedit</th>
+      <th>editcomponents</th>
+      <th>canconfirm</th>
+      <th>editbugs</th>
       <th>[% terms.Bugs %]</th>
     </tr>
     [% FOREACH group = groups %]
@@ -50,7 +53,7 @@
           <td>
             [% group.name FILTER html %]
           </td>
-          <td align="center" colspan=4>
+          <td align="center" colspan=7>
             Disabled
           </td>
           <td>
             <input type=checkbox value=1 name=canedit_[% group.id %]
             [% " checked=\"checked\"" IF group.canedit %]>
           </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 %]
           </td>
@@ -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
 restriction that further restricts what can be edited by a user.
 <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
 indicate which [% terms.bugs %] will be placed in
 this group according to the following definitions.
index 4c348fa106d16a160c8c4a56da5046f8823a79d3..0fd4aafa70b6699657ed9537551f3aa4e91c30d2 100644 (file)
@@ -19,7 +19,6 @@
   # listselectionvalues:      selection values to recreate the current user
   #                           list.
   # editusers:                is viewing user member of editusers?
-  # editcomponents:           is viewing user member of editcomponents?
   # otheruser:                Bugzilla::User object of the viewed user.
   # reporter:                 number of bugs reported by the user
   # assignee_or_qa:           number of bugs the user is either the assignee
@@ -57,8 +56,8 @@
 %]
 
 [% responsibilityterms = {
-  'initialowner'     => 'Default Assignee',
-  'initialqacontact' => 'Default QA Contact'
+  'default_assignee'   => 'Default Assignee',
+  'default_qa_contact' => 'Default QA Contact'
   }
 %]
 
           [% FOREACH component = otheruser.product_responsibilities %]
             <li>
               [% andstring = '' %]
-              [% FOREACH responsibility = ['initialowner', 'initialqacontact'] %]
-                [% IF component.$responsibility == otheruser.id %]
+              [% FOREACH responsibility = ['default_assignee', 'default_qa_contact'] %]
+                [% IF component.${responsibility}.id == otheruser.id %]
                   [% andstring %] [% responsibilityterms.$responsibility %]
                   [% andstring = ' and ' %]
                 [% END %]
               [% END %]
               for
-              [% IF editcomponents %]
+              [% IF user.in_group("editcomponents", component.product_id) %]
                 <a href="editcomponents.cgi?action=edit&amp;product=
-                         [% component.productname FILTER url_quote %]&amp;component=
-                         [% component.componentname FILTER url_quote %]">
+                         [% component.product.name FILTER url_quote %]&amp;component=
+                         [% component.name FILTER url_quote %]">
               [% END %]
-                [%+ component.productname FILTER html %]:
-                [% component.componentname FILTER html %]
-              [% IF editcomponents %]
+                [%+ component.product.name FILTER html %]:
+                [% component.name FILTER html %]
+              [% IF user.in_group("editcomponents", component.product_id) %]
                 </a>
               [% END %]
             </li>
     one product.
   </p>
   <p>
-    [% IF editcomponents %]
+    [% IF user.in_group("editcomponents", component.product_id) %]
       Change this by clicking the product editing links above,
     [% ELSE %]
       For now, you can
index fa2692d852909956502cb682c8d5995aa78344d8..381c75901e4a65b65fc7f4949ac6dd2d6e46b51c 100644 (file)
 [% PROCESS global/variables.none.tmpl %]
 
 [%# 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
-  [%+ "$terms.Bug $bugid" FILTER bug_link(bugid) FILTER none %][% END %]
-[% subheader = BLOCK %][% bugsummary FILTER html %][% END %]
+  [%+ "$terms.Bug $bug.bug_id" FILTER bug_link(bug.bug_id) FILTER none %][% END %]
+[% subheader = BLOCK %][% bug.short_desc FILTER html %][% END %]
 
 [% PROCESS global/header.html.tmpl
   title = title
@@ -40,7 +40,7 @@
 %]
 
 <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">
 
   <table class="attachment_entry">
@@ -66,7 +66,7 @@
       </td>
     </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>
         <th>Reassignment:</th>
         <td>
index 62f8004f2cda5a38e026f6eb818318351f83b2c3..c27750e6c2221b26bb52e0976babe21a364c0d34 100644 (file)
@@ -476,7 +476,7 @@ function handleWantsAttachment(wants_attachment) {
     </td>
   </tr>
 
-  [% IF user.in_group('editbugs') %]
+  [% IF user.in_group('editbugs', product.id) %]
     [% IF use_keywords %]
       <tr>
         <td align="right" valign="top">
index f8590fc28e3daad8d9e33a63fc7cb05f73ce2ef9..ea5896730db6921b96982986f1f8f240361ec83d 100644 (file)
 ],
 
 'attachment/create.html.tmpl' => [
-  'bugid', 
+  'bug.bug_id',
   'attachment.id', 
 ],
 
index cb57df61034b2b5a5a6e7a49491eee2312b4c9ee..7c2eabecf6e653b92dee4352c9a37f4383c35a43 100644 (file)
               href="editparams.cgi">' IF user.groups.tweakparams %]
     [% '<link rel="Administration" title="Users"    
               href="editusers.cgi">' IF user.groups.editusers %]
-    [% '<link rel="Administration" title="Products"      
-              href="editproducts.cgi">' IF user.groups.editcomponents %]
+    [% '<link rel="Administration" title="Products" href="editproducts.cgi">'
+       IF user.groups.editcomponents || user.get_products_by_permission("editcomponents").size %]
     [% '<link rel="Administration" title="Flag Types"   
               href="editflagtypes.cgi">' IF user.groups.editcomponents %]
     [% '<link rel="Administration" title="Groups"        
index 079269b1b26a53ef8951cacb92eb72ad77acefdb..8078a5d809d3001525a8349348b7c7f8b1477ebe 100644 (file)
           <li><span class="separator">[% sep %]</span><a href="editclassifications.cgi">Classifications</a></li>
           [% sep = "| " %]
         [% 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>
           [% sep = "| " %]
+        [% END %]
+        [% IF user.groups.editcomponents %]
           <li><span class="separator">[% sep %]</span><a href="editflagtypes.cgi">Flags</a></li>
+          [% sep = "| " %]
         [% END %]
         [% IF user.groups.admin %]
           <li><span class="separator">[% sep %]</span><a href="editfields.cgi">Custom Fields</a></li>
index 6fc244fb51706cb609dd0377c67b159fa705f869..d6b596ea59229da8947b9b916ee386d19a031e60 100644 (file)
     [% title = "Missing Component" %]
     [% admindocslinks = {'products.html'   => 'Administering products',
                          '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
     enter [% terms.abug %] into it.<br>
-    [% IF user.in_group("editcomponents") %]
-      <a href="editcomponents.cgi?action=add&amp;product=[% product FILTER url_quote %]">Create
+    [% IF user.in_group("editcomponents", product.id) %]
+      <a href="editcomponents.cgi?action=add&amp;product=[% product.name FILTER url_quote %]">Create
       a new component</a>.
     [% ELSE %]
       Please contact [% Param("maintainer") %] and ask them
   [% ELSIF error == "missing_version" %]
     [% title = "Missing Version" %]
     [% 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
     enter [% terms.abug %] into it.<p>
-    [% IF user.in_group("editcomponents") %]
-      <a href="editversions.cgi?action=add&amp;product=[% product FILTER url_quote %]">Create
+    [% IF user.in_group("editcomponents", product.id) %]
+      <a href="editversions.cgi?action=add&amp;product=[% product.name FILTER url_quote %]">Create
       a new version</a>.
     [% ELSE %]
       Please contact [% Param("maintainer") %] and ask them
     [% title = BLOCK %]Product closed for [% terms.Bugs %] Entry[% END %]
     [% admindocslinks = {'products.html' => 'Administering products'} %]
     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" %]
     [% title = "Product Edit Access Denied" %]
index 4bb65c152321f3eda977b9d26b220dcd57f2e01b..555b017a819d5bf620980c3fbd6b9782b349f280 100755 (executable)
@@ -369,7 +369,13 @@ sub DoPermissions {
             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->{'set_bits'} = \@set_bits;    
 }