diff options
Diffstat (limited to 'extensions/BMO/lib/Reports')
-rw-r--r-- | extensions/BMO/lib/Reports/Groups.pm | 485 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/Internship.pm | 135 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/ProductSecurity.pm | 76 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/Recruiting.pm | 126 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/ReleaseTracking.pm | 779 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/Triage.pm | 516 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/UserActivity.pm | 382 |
7 files changed, 1202 insertions, 1297 deletions
diff --git a/extensions/BMO/lib/Reports/Groups.pm b/extensions/BMO/lib/Reports/Groups.pm index ce7df767c..7b395aca9 100644 --- a/extensions/BMO/lib/Reports/Groups.pm +++ b/extensions/BMO/lib/Reports/Groups.pm @@ -19,25 +19,24 @@ use Bugzilla::Util qw(trim datetime_from); use JSON qw(encode_json); sub admins_report { - my ($vars) = @_; - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; + my ($vars) = @_; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; - ($user->in_group('editbugs')) - || ThrowUserError('auth_failure', { group => 'editbugs', - action => 'run', - object => 'group_admins' }); + ($user->in_group('editbugs')) + || ThrowUserError('auth_failure', + {group => 'editbugs', action => 'run', object => 'group_admins'}); - my @grouplist = - ($user->in_group('editusers') || $user->in_group('infrasec')) - ? map { lc($_->name) } Bugzilla::Group->get_all - : _get_permitted_membership_groups(); + my @grouplist + = ($user->in_group('editusers') || $user->in_group('infrasec')) + ? map { lc($_->name) } Bugzilla::Group->get_all + : _get_permitted_membership_groups(); - my $groups = join(',', map { $dbh->quote($_) } @grouplist); + my $groups = join(',', map { $dbh->quote($_) } @grouplist); - my $query = " - SELECT groups.id, " . - $dbh->sql_group_concat('profiles.userid', "','", 1) . " + my $query = " + SELECT groups.id, " + . $dbh->sql_group_concat('profiles.userid', "','", 1) . " FROM groups LEFT JOIN user_group_map ON user_group_map.group_id = groups.id @@ -49,271 +48,275 @@ sub admins_report { AND groups.name IN ($groups) GROUP BY groups.name"; - my @groups; - foreach my $row (@{ $dbh->selectall_arrayref($query) }) { - my $group = Bugzilla::Group->new({ id => shift @$row, cache => 1}); - my @admins; - if (my $admin_ids = shift @$row) { - foreach my $uid (split(/,/, $admin_ids)) { - push(@admins, Bugzilla::User->new({ id => $uid, cache => 1 })); - } - } - push(@groups, { name => $group->name, - description => $group->description, - owner => $group->owner, - admins => \@admins }); + my @groups; + foreach my $row (@{$dbh->selectall_arrayref($query)}) { + my $group = Bugzilla::Group->new({id => shift @$row, cache => 1}); + my @admins; + if (my $admin_ids = shift @$row) { + foreach my $uid (split(/,/, $admin_ids)) { + push(@admins, Bugzilla::User->new({id => $uid, cache => 1})); + } } + push( + @groups, + { + name => $group->name, + description => $group->description, + owner => $group->owner, + admins => \@admins + } + ); + } - $vars->{'groups'} = \@groups; + $vars->{'groups'} = \@groups; } sub membership_report { - my ($page, $vars) = @_; - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - my $cgi = Bugzilla->cgi; - - ($user->in_group('editusers') || $user->in_group('infrasec')) - || ThrowUserError('auth_failure', { group => 'editusers', - action => 'run', - object => 'group_admins' }); - - my $who = $cgi->param('who'); - if (!defined($who) || $who eq '') { - if ($page eq 'group_membership.txt') { - print $cgi->redirect("page.cgi?id=group_membership.html&output=txt"); - exit; - } - $vars->{'output'} = $cgi->param('output'); - return; + my ($page, $vars) = @_; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + my $cgi = Bugzilla->cgi; + + ($user->in_group('editusers') || $user->in_group('infrasec')) + || ThrowUserError('auth_failure', + {group => 'editusers', action => 'run', object => 'group_admins'}); + + my $who = $cgi->param('who'); + if (!defined($who) || $who eq '') { + if ($page eq 'group_membership.txt') { + print $cgi->redirect("page.cgi?id=group_membership.html&output=txt"); + exit; } + $vars->{'output'} = $cgi->param('output'); + return; + } - Bugzilla::User::match_field({ 'who' => {'type' => 'multi'} }); - $who = Bugzilla->input_params->{'who'}; - $who = ref($who) ? $who : [ $who ]; + Bugzilla::User::match_field({'who' => {'type' => 'multi'}}); + $who = Bugzilla->input_params->{'who'}; + $who = ref($who) ? $who : [$who]; - my @users; - foreach my $login (@$who) { - my $u = Bugzilla::User->new(login_to_id($login, 1)); + my @users; + foreach my $login (@$who) { + my $u = Bugzilla::User->new(login_to_id($login, 1)); - # this is lifted from $user->groups() - # we need to show which groups are direct and which are inherited + # this is lifted from $user->groups() + # we need to show which groups are direct and which are inherited - my $groups_to_check = $dbh->selectcol_arrayref( - q{SELECT DISTINCT group_id + my $groups_to_check = $dbh->selectcol_arrayref( + q{SELECT DISTINCT group_id FROM user_group_map - WHERE user_id = ? AND isbless = 0}, undef, $u->id); + WHERE user_id = ? AND isbless = 0}, undef, $u->id + ); - my $rows = $dbh->selectall_arrayref( - "SELECT DISTINCT grantor_id, member_id + my $rows = $dbh->selectall_arrayref( + "SELECT DISTINCT grantor_id, member_id FROM group_group_map - WHERE grant_type = " . GROUP_MEMBERSHIP); + WHERE grant_type = " . GROUP_MEMBERSHIP + ); - my %group_membership; - foreach my $row (@$rows) { - my ($grantor_id, $member_id) = @$row; - push (@{ $group_membership{$member_id} }, $grantor_id); - } + my %group_membership; + foreach my $row (@$rows) { + my ($grantor_id, $member_id) = @$row; + push(@{$group_membership{$member_id}}, $grantor_id); + } - my %checked_groups; - my %direct_groups; - my %indirect_groups; - my %groups; + my %checked_groups; + my %direct_groups; + my %indirect_groups; + my %groups; - foreach my $member_id (@$groups_to_check) { - $direct_groups{$member_id} = 1; - } + foreach my $member_id (@$groups_to_check) { + $direct_groups{$member_id} = 1; + } - while (scalar(@$groups_to_check) > 0) { - my $member_id = shift @$groups_to_check; - if (!$checked_groups{$member_id}) { - $checked_groups{$member_id} = 1; - my $members = $group_membership{$member_id}; - my @new_to_check = grep(!$checked_groups{$_}, @$members); - push(@$groups_to_check, @new_to_check); - foreach my $id (@new_to_check) { - $indirect_groups{$id} = $member_id; - } - $groups{$member_id} = 1; - } + while (scalar(@$groups_to_check) > 0) { + my $member_id = shift @$groups_to_check; + if (!$checked_groups{$member_id}) { + $checked_groups{$member_id} = 1; + my $members = $group_membership{$member_id}; + my @new_to_check = grep(!$checked_groups{$_}, @$members); + push(@$groups_to_check, @new_to_check); + foreach my $id (@new_to_check) { + $indirect_groups{$id} = $member_id; } + $groups{$member_id} = 1; + } + } - my @groups; - my $ra_groups = Bugzilla::Group->new_from_list([keys %groups]); - foreach my $group (@$ra_groups) { - my $via; - if ($direct_groups{$group->id}) { - $via = ''; - } else { - foreach my $g (@$ra_groups) { - if ($g->id == $indirect_groups{$group->id}) { - $via = $g->name; - last; - } - } - } - push @groups, { - name => $group->name, - desc => $group->description, - via => $via, - }; + my @groups; + my $ra_groups = Bugzilla::Group->new_from_list([keys %groups]); + foreach my $group (@$ra_groups) { + my $via; + if ($direct_groups{$group->id}) { + $via = ''; + } + else { + foreach my $g (@$ra_groups) { + if ($g->id == $indirect_groups{$group->id}) { + $via = $g->name; + last; + } } - - push @users, { - user => $u, - groups => \@groups, - }; + } + push @groups, {name => $group->name, desc => $group->description, via => $via,}; } - $vars->{'who'} = $who; - $vars->{'users'} = \@users; + push @users, {user => $u, groups => \@groups,}; + } + + $vars->{'who'} = $who; + $vars->{'users'} = \@users; } sub members_report { - my ($page, $vars) = @_; - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - my $cgi = Bugzilla->cgi; - - ($user->in_group('editbugs')) - || ThrowUserError('auth_failure', { group => 'editbugs', - action => 'run', - object => 'group_admins' }); - - my $privileged = $user->in_group('editusers') || $user->in_group('infrasec'); - $vars->{privileged} = $privileged; - - my @grouplist = $privileged - ? map { lc($_->name) } Bugzilla::Group->get_all - : _get_permitted_membership_groups(); - - my $include_disabled = $cgi->param('include_disabled') ? 1 : 0; - $vars->{'include_disabled'} = $include_disabled; - - # don't allow all groups, to avoid putting pain on the servers - my @group_names = - sort - grep { !/^(?:bz_.+|canconfirm|editbugs|editbugs-team|everyone)$/ } - @grouplist; - unshift(@group_names, ''); - $vars->{'groups'} = \@group_names; - - # load selected group - my $group = lc(trim($cgi->param('group') || '')); - $group = '' unless grep { $_ eq $group } @group_names; - return if $group eq ''; - my $group_obj = Bugzilla::Group->new({ name => $group }); - $vars->{'group'} = $group_obj; - - $vars->{'privileged'} = 1 if ($group_obj->owner && $group_obj->owner->id == $user->id); - - my @types; - my $members = $group_obj->members_complete(); - foreach my $name (sort keys %$members) { - push @types, { - name => ($name eq '_direct' ? 'direct' : $name), - members => _filter_userlist($members->{$name}), + my ($page, $vars) = @_; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + my $cgi = Bugzilla->cgi; + + ($user->in_group('editbugs')) + || ThrowUserError('auth_failure', + {group => 'editbugs', action => 'run', object => 'group_admins'}); + + my $privileged = $user->in_group('editusers') || $user->in_group('infrasec'); + $vars->{privileged} = $privileged; + + my @grouplist + = $privileged + ? map { lc($_->name) } Bugzilla::Group->get_all + : _get_permitted_membership_groups(); + + my $include_disabled = $cgi->param('include_disabled') ? 1 : 0; + $vars->{'include_disabled'} = $include_disabled; + + # don't allow all groups, to avoid putting pain on the servers + my @group_names + = sort grep { !/^(?:bz_.+|canconfirm|editbugs|editbugs-team|everyone)$/ } + @grouplist; + unshift(@group_names, ''); + $vars->{'groups'} = \@group_names; + + # load selected group + my $group = lc(trim($cgi->param('group') || '')); + $group = '' unless grep { $_ eq $group } @group_names; + return if $group eq ''; + my $group_obj = Bugzilla::Group->new({name => $group}); + $vars->{'group'} = $group_obj; + + $vars->{'privileged'} = 1 + if ($group_obj->owner && $group_obj->owner->id == $user->id); + + my @types; + my $members = $group_obj->members_complete(); + foreach my $name (sort keys %$members) { + push @types, + { + name => ($name eq '_direct' ? 'direct' : $name), + members => _filter_userlist($members->{$name}), + }; + } + + # make it easy for the template to detect an empty group + my $has_members = 0; + foreach my $type (@types) { + $has_members += scalar(@{$type->{members}}); + last if $has_members; + } + @types = () unless $has_members; + + if ($page eq 'group_members.json') { + my %users; + foreach my $rh (@types) { + foreach my $member (@{$rh->{members}}) { + my $login = $member->login; + if (exists $users{$login}) { + push @{$users{$login}->{groups}}, $rh->{name} if $privileged; } - } - - # make it easy for the template to detect an empty group - my $has_members = 0; - foreach my $type (@types) { - $has_members += scalar(@{ $type->{members} }); - last if $has_members; - } - @types = () unless $has_members; - - if ($page eq 'group_members.json') { - my %users; - foreach my $rh (@types) { - foreach my $member (@{ $rh->{members} }) { - my $login = $member->login; - if (exists $users{$login}) { - push @{ $users{$login}->{groups} }, $rh->{name} if $privileged; - } - else { - my $rh_user = { - login => $login, - membership => $rh->{name} eq 'direct' ? 'direct' : 'indirect', - rh_name => $rh->{name}, - }; - if ($privileged) { - $rh_user->{group} = $rh->{name}; - $rh_user->{groups} = [ $rh->{name} ]; - $rh_user->{lastseeon} = $member->last_seen_date; - $rh_user->{mfa} = $member->mfa; - $rh_user->{api_key_only} = $member->settings->{api_key_only}->{value} eq 'on' - ? JSON::true : JSON::false; - } - $users{$login} = $rh_user; - } - } + else { + my $rh_user = { + login => $login, + membership => $rh->{name} eq 'direct' ? 'direct' : 'indirect', + rh_name => $rh->{name}, + }; + if ($privileged) { + $rh_user->{group} = $rh->{name}; + $rh_user->{groups} = [$rh->{name}]; + $rh_user->{lastseeon} = $member->last_seen_date; + $rh_user->{mfa} = $member->mfa; + $rh_user->{api_key_only} + = $member->settings->{api_key_only}->{value} eq 'on' + ? JSON::true + : JSON::false; + } + $users{$login} = $rh_user; } - $vars->{types_json} = JSON->new->pretty->canonical->utf8->encode([ values %users ]); + } } - else { - my %users; - foreach my $rh (@types) { - foreach my $member (@{ $rh->{members} }) { - $users{$member->login} = 1 unless exists $users{$member->login}; - } - } - $vars->{types} = \@types; - $vars->{count} = scalar(keys %users); + $vars->{types_json} + = JSON->new->pretty->canonical->utf8->encode([values %users]); + } + else { + my %users; + foreach my $rh (@types) { + foreach my $member (@{$rh->{members}}) { + $users{$member->login} = 1 unless exists $users{$member->login}; + } } + $vars->{types} = \@types; + $vars->{count} = scalar(keys %users); + } } sub _filter_userlist { - my ($list, $include_disabled) = @_; - $list = [ grep { $_->is_enabled } @$list ] unless $include_disabled; - my $now = DateTime->now(); - my $never = DateTime->from_epoch( epoch => 0 ); - foreach my $user (@$list) { - my $last_seen = $user->last_seen_date ? datetime_from($user->last_seen_date) : $never; - $user->{last_seen_days} = sprintf( - '%.0f', - $now->subtract_datetime_absolute($last_seen)->delta_seconds / (28 * 60 * 60)); - } - return [ sort { lc($a->identity) cmp lc($b->identity) } @$list ]; + my ($list, $include_disabled) = @_; + $list = [grep { $_->is_enabled } @$list] unless $include_disabled; + my $now = DateTime->now(); + my $never = DateTime->from_epoch(epoch => 0); + foreach my $user (@$list) { + my $last_seen + = $user->last_seen_date ? datetime_from($user->last_seen_date) : $never; + $user->{last_seen_days} = sprintf('%.0f', + $now->subtract_datetime_absolute($last_seen)->delta_seconds / (28 * 60 * 60)); + } + return [sort { lc($a->identity) cmp lc($b->identity) } @$list]; } # Groups that any user with editbugs can see the membership or admin lists for. # Transparency FTW. sub _get_permitted_membership_groups { - my $user = Bugzilla->user; - - # Default publicly viewable groups - my %default_public_groups = map { $_ => 1 } qw( - bugzilla-approvers - bugzilla-reviewers - can_restrict_comments - community-it-team - mozilla-employee-confidential - mozilla-foundation-confidential - mozilla-reps - qa-approvers - ); - - # We add the group to the permitted list if: - # 1. it is a drivers group - this gives us a little - # future-proofing - # 2. it is a one of the default public groups - # 3. the user is the group's owner - # 4. or the user can bless others into the group - my @permitted_groups; - foreach my $group (Bugzilla::Group->get_all) { - my $name = $group->name; - if ($name =~ /-drivers$/ - || exists $default_public_groups{$name} - || ($group->owner && $group->owner->id == $user->id) - || $user->can_bless($group->id)) - { - push(@permitted_groups, $name); - } + my $user = Bugzilla->user; + + # Default publicly viewable groups + my %default_public_groups = map { $_ => 1 } qw( + bugzilla-approvers + bugzilla-reviewers + can_restrict_comments + community-it-team + mozilla-employee-confidential + mozilla-foundation-confidential + mozilla-reps + qa-approvers + ); + + # We add the group to the permitted list if: + # 1. it is a drivers group - this gives us a little + # future-proofing + # 2. it is a one of the default public groups + # 3. the user is the group's owner + # 4. or the user can bless others into the group + my @permitted_groups; + foreach my $group (Bugzilla::Group->get_all) { + my $name = $group->name; + if ( $name =~ /-drivers$/ + || exists $default_public_groups{$name} + || ($group->owner && $group->owner->id == $user->id) + || $user->can_bless($group->id)) + { + push(@permitted_groups, $name); } + } - return @permitted_groups; + return @permitted_groups; } 1; diff --git a/extensions/BMO/lib/Reports/Internship.pm b/extensions/BMO/lib/Reports/Internship.pm index 2dfa583a6..f9ad1a578 100644 --- a/extensions/BMO/lib/Reports/Internship.pm +++ b/extensions/BMO/lib/Reports/Internship.pm @@ -17,33 +17,30 @@ use Bugzilla::Product; use Bugzilla::Component; sub report { - my ($vars) = @_; - my $user = Bugzilla->user; - - $user->in_group('hr') - || ThrowUserError('auth_failure', { group => 'hr', - action => 'run', - object => 'internship_dashboard' }); - - my $product = Bugzilla::Product->check({ name => 'Recruiting', cache => 1 }); - my $component = Bugzilla::Component->new({ product => $product, name => 'Intern', cache => 1 }); - - # find all open internship bugs - my $bugs = Bugzilla::Bug->match({ - product_id => $product->id, - component_id => $component->id, - resolution => '', - }); - - # filter bugs based on visibility and re-bless - $user->visible_bugs($bugs); - $bugs = [ - map { bless($_, 'InternshipBug') } - grep { $user->can_see_bug($_->id) } - @$bugs - ]; - - $vars->{bugs} = $bugs; + my ($vars) = @_; + my $user = Bugzilla->user; + + $user->in_group('hr') + || ThrowUserError('auth_failure', + {group => 'hr', action => 'run', object => 'internship_dashboard'}); + + my $product = Bugzilla::Product->check({name => 'Recruiting', cache => 1}); + my $component = Bugzilla::Component->new( + {product => $product, name => 'Intern', cache => 1}); + + # find all open internship bugs + my $bugs = Bugzilla::Bug->match({ + product_id => $product->id, + component_id => $component->id, + resolution => '', + }); + + # filter bugs based on visibility and re-bless + $user->visible_bugs($bugs); + $bugs = [map { bless($_, 'InternshipBug') } + grep { $user->can_see_bug($_->id) } @$bugs]; + + $vars->{bugs} = $bugs; } 1; @@ -58,64 +55,62 @@ use Bugzilla::Comment; use Bugzilla::Util qw(trim); sub _extract { - my ($self) = @_; - return if exists $self->{internship_data}; - $self->{internship_data} = {}; - - # we only need the first comment - my $comment = Bugzilla::Comment->match({ - bug_id => $self->id, - LIMIT => 1, - })->[0]->body; - - # extract just what we need - # changing the comment will break this - - if ($comment =~ /Hiring Manager:\s+(.+)\nTeam:\n/s) { - $self->{internship_data}->{hiring_manager} = trim($1); - } - if ($comment =~ /\nVP Authority:\s+(.+)\nProduct Line:\n/s) { - $self->{internship_data}->{scvp} = trim($1); - } - if ($comment =~ /\nProduct Line:\s+(.+)\nLevel 1/s) { - $self->{internship_data}->{product_line} = trim($1); - } - if ($comment =~ /\nBusiness Need:\s+(.+)\nPotential Project:\n/s) { - $self->{internship_data}->{business_need} = trim($1); - } - if ($comment =~ /\nName:\s+(.+)$/s) { - $self->{internship_data}->{intern_name} = trim($1); - } + my ($self) = @_; + return if exists $self->{internship_data}; + $self->{internship_data} = {}; + + # we only need the first comment + my $comment + = Bugzilla::Comment->match({bug_id => $self->id, LIMIT => 1,})->[0]->body; + + # extract just what we need + # changing the comment will break this + + if ($comment =~ /Hiring Manager:\s+(.+)\nTeam:\n/s) { + $self->{internship_data}->{hiring_manager} = trim($1); + } + if ($comment =~ /\nVP Authority:\s+(.+)\nProduct Line:\n/s) { + $self->{internship_data}->{scvp} = trim($1); + } + if ($comment =~ /\nProduct Line:\s+(.+)\nLevel 1/s) { + $self->{internship_data}->{product_line} = trim($1); + } + if ($comment =~ /\nBusiness Need:\s+(.+)\nPotential Project:\n/s) { + $self->{internship_data}->{business_need} = trim($1); + } + if ($comment =~ /\nName:\s+(.+)$/s) { + $self->{internship_data}->{intern_name} = trim($1); + } } sub hiring_manager { - my ($self) = @_; - $self->_extract(); - return $self->{internship_data}->{hiring_manager}; + my ($self) = @_; + $self->_extract(); + return $self->{internship_data}->{hiring_manager}; } sub scvp { - my ($self) = @_; - $self->_extract(); - return $self->{internship_data}->{scvp}; + my ($self) = @_; + $self->_extract(); + return $self->{internship_data}->{scvp}; } sub business_need { - my ($self) = @_; - $self->_extract(); - return $self->{internship_data}->{business_need}; + my ($self) = @_; + $self->_extract(); + return $self->{internship_data}->{business_need}; } sub product_line { - my ($self) = @_; - $self->_extract(); - return $self->{internship_data}->{product_line}; + my ($self) = @_; + $self->_extract(); + return $self->{internship_data}->{product_line}; } sub intern_name { - my ($self) = @_; - $self->_extract(); - return $self->{internship_data}->{intern_name}; + my ($self) = @_; + $self->_extract(); + return $self->{internship_data}->{intern_name}; } 1; diff --git a/extensions/BMO/lib/Reports/ProductSecurity.pm b/extensions/BMO/lib/Reports/ProductSecurity.pm index e7ccda171..fb773cd93 100644 --- a/extensions/BMO/lib/Reports/ProductSecurity.pm +++ b/extensions/BMO/lib/Reports/ProductSecurity.pm @@ -16,54 +16,54 @@ use Bugzilla::Error; use Bugzilla::Product; sub report { - my ($vars) = @_; - my $user = Bugzilla->user; + my ($vars) = @_; + my $user = Bugzilla->user; - ($user->in_group('admin') || $user->in_group('infrasec')) - || ThrowUserError('auth_failure', { group => 'admin', - action => 'run', - object => 'product_security' }); + ($user->in_group('admin') || $user->in_group('infrasec')) + || ThrowUserError('auth_failure', + {group => 'admin', action => 'run', object => 'product_security'}); - my $moco = Bugzilla::Group->new({ name => 'mozilla-employee-confidential' }) - or return; + my $moco = Bugzilla::Group->new({name => 'mozilla-employee-confidential'}) + or return; - my $products = []; - foreach my $product (@{ Bugzilla::Product->match({}) }) { - my $default_group = $product->default_security_group_obj; - my $group_controls = $product->group_controls(); + my $products = []; + foreach my $product (@{Bugzilla::Product->match({})}) { + my $default_group = $product->default_security_group_obj; + my $group_controls = $product->group_controls(); - my $item = { - name => $product->name, - default_security_group => $product->default_security_group, - group_visibility => 'None/None', - moco => exists $group_controls->{$moco->id}, - }; + my $item = { + name => $product->name, + default_security_group => $product->default_security_group, + group_visibility => 'None/None', + moco => exists $group_controls->{$moco->id}, + }; - if ($default_group) { - if (my $control = $group_controls->{$default_group->id}) { - $item->{group_visibility} = control_to_string($control->{membercontrol}) . - '/' . control_to_string($control->{othercontrol}); - } - } + if ($default_group) { + if (my $control = $group_controls->{$default_group->id}) { + $item->{group_visibility} = control_to_string($control->{membercontrol}) . '/' + . control_to_string($control->{othercontrol}); + } + } - $item->{group_problem} = $default_group ? '' : "Invalid group " . $product->default_security_group; - $item->{visibility_problem} = 'Default security group should be Shown/Shown' - if ($item->{group_visibility} ne 'Shown/Shown') - && ($item->{group_visibility} ne 'Mandatory/Mandatory') - && ($item->{group_visibility} ne 'Default/Default'); + $item->{group_problem} + = $default_group ? '' : "Invalid group " . $product->default_security_group; + $item->{visibility_problem} = 'Default security group should be Shown/Shown' + if ($item->{group_visibility} ne 'Shown/Shown') + && ($item->{group_visibility} ne 'Mandatory/Mandatory') + && ($item->{group_visibility} ne 'Default/Default'); - push @$products, $item; - } - $vars->{products} = $products; + push @$products, $item; + } + $vars->{products} = $products; } sub control_to_string { - my ($control) = @_; - return 'NA' if $control == CONTROLMAPNA; - return 'Shown' if $control == CONTROLMAPSHOWN; - return 'Default' if $control == CONTROLMAPDEFAULT; - return 'Mandatory' if $control == CONTROLMAPMANDATORY; - return ''; + my ($control) = @_; + return 'NA' if $control == CONTROLMAPNA; + return 'Shown' if $control == CONTROLMAPSHOWN; + return 'Default' if $control == CONTROLMAPDEFAULT; + return 'Mandatory' if $control == CONTROLMAPMANDATORY; + return ''; } 1; diff --git a/extensions/BMO/lib/Reports/Recruiting.pm b/extensions/BMO/lib/Reports/Recruiting.pm index 39eb8327d..c35b0cbff 100644 --- a/extensions/BMO/lib/Reports/Recruiting.pm +++ b/extensions/BMO/lib/Reports/Recruiting.pm @@ -17,33 +17,30 @@ use Bugzilla::Product; use Bugzilla::Component; sub report { - my ($vars) = @_; - my $user = Bugzilla->user; - - $user->in_group('hr') - || ThrowUserError('auth_failure', { group => 'hr', - action => 'run', - object => 'recruiting_dashboard' }); - - my $product = Bugzilla::Product->check({ name => 'Recruiting', cache => 1 }); - my $component = Bugzilla::Component->new({ product => $product, name => 'General', cache => 1 }); - - # find all open recruiting bugs - my $bugs = Bugzilla::Bug->match({ - product_id => $product->id, - component_id => $component->id, - resolution => '', - }); - - # filter bugs based on visibility and re-bless - $user->visible_bugs($bugs); - $bugs = [ - map { bless($_, 'RecruitingBug') } - grep { $user->can_see_bug($_->id) } - @$bugs - ]; - - $vars->{bugs} = $bugs; + my ($vars) = @_; + my $user = Bugzilla->user; + + $user->in_group('hr') + || ThrowUserError('auth_failure', + {group => 'hr', action => 'run', object => 'recruiting_dashboard'}); + + my $product = Bugzilla::Product->check({name => 'Recruiting', cache => 1}); + my $component = Bugzilla::Component->new( + {product => $product, name => 'General', cache => 1}); + + # find all open recruiting bugs + my $bugs = Bugzilla::Bug->match({ + product_id => $product->id, + component_id => $component->id, + resolution => '', + }); + + # filter bugs based on visibility and re-bless + $user->visible_bugs($bugs); + $bugs = [map { bless($_, 'RecruitingBug') } + grep { $user->can_see_bug($_->id) } @$bugs]; + + $vars->{bugs} = $bugs; } 1; @@ -58,55 +55,56 @@ use Bugzilla::Comment; use Bugzilla::Util qw(trim); sub _extract { - my ($self) = @_; - return if exists $self->{recruitment_data}; - $self->{recruitment_data} = {}; - - # we only need the first comment - my $comment = Bugzilla::Comment->match({ - bug_id => $self->id, - LIMIT => 1, - })->[0]->body; - - # extract just what we need - # changing the comment will break this - - if ($comment =~ /\nHiring Manager:\s+(.+)VP Authority:\n/s) { - $self->{recruitment_data}->{hiring_manager} = trim($1); - } - if ($comment =~ /\nVP Authority:\s+(.+)HRBP:\n/s) { - $self->{recruitment_data}->{scvp} = trim($1); - } - if ($comment =~ /\nWhat part of your strategic plan does this role impact\?\s+(.+)Why is this critical for success\?\n/s) { - $self->{recruitment_data}->{strategic_plan} = trim($1); - } - if ($comment =~ /\nWhy is this critical for success\?\s+(.+)$/s) { - $self->{recruitment_data}->{why_critical} = trim($1); - } + my ($self) = @_; + return if exists $self->{recruitment_data}; + $self->{recruitment_data} = {}; + + # we only need the first comment + my $comment + = Bugzilla::Comment->match({bug_id => $self->id, LIMIT => 1,})->[0]->body; + + # extract just what we need + # changing the comment will break this + + if ($comment =~ /\nHiring Manager:\s+(.+)VP Authority:\n/s) { + $self->{recruitment_data}->{hiring_manager} = trim($1); + } + if ($comment =~ /\nVP Authority:\s+(.+)HRBP:\n/s) { + $self->{recruitment_data}->{scvp} = trim($1); + } + if ($comment + =~ /\nWhat part of your strategic plan does this role impact\?\s+(.+)Why is this critical for success\?\n/s + ) + { + $self->{recruitment_data}->{strategic_plan} = trim($1); + } + if ($comment =~ /\nWhy is this critical for success\?\s+(.+)$/s) { + $self->{recruitment_data}->{why_critical} = trim($1); + } } sub hiring_manager { - my ($self) = @_; - $self->_extract(); - return $self->{recruitment_data}->{hiring_manager}; + my ($self) = @_; + $self->_extract(); + return $self->{recruitment_data}->{hiring_manager}; } sub scvp { - my ($self) = @_; - $self->_extract(); - return $self->{recruitment_data}->{scvp}; + my ($self) = @_; + $self->_extract(); + return $self->{recruitment_data}->{scvp}; } sub strategic_plan { - my ($self) = @_; - $self->_extract(); - return $self->{recruitment_data}->{strategic_plan}; + my ($self) = @_; + $self->_extract(); + return $self->{recruitment_data}->{strategic_plan}; } sub why_critical { - my ($self) = @_; - $self->_extract(); - return $self->{recruitment_data}->{why_critical}; + my ($self) = @_; + $self->_extract(); + return $self->{recruitment_data}->{why_critical}; } 1; diff --git a/extensions/BMO/lib/Reports/ReleaseTracking.pm b/extensions/BMO/lib/Reports/ReleaseTracking.pm index 9fba1e14b..38a07aee7 100644 --- a/extensions/BMO/lib/Reports/ReleaseTracking.pm +++ b/extensions/BMO/lib/Reports/ReleaseTracking.pm @@ -21,496 +21,381 @@ use JSON qw(-convert_blessed_universally); use List::MoreUtils qw(uniq); use constant DATE_RANGES => [ - { - value => '20160126-20160307', - label => '2016-01-26 and 2016-03-07' - }, - { - value => '20151215-20160125', - label => '2015-12-15 and 2016-01-25' - }, - { - value => '20151103-20151214', - label => '2015-11-03 and 2015-12-14' - }, - { - value => '20150922-20151102', - label => '2015-09-22 and 2015-11-02' - }, - { - value => '20150811-20150921', - label => '2015-08-11 and 2015-09-21' - }, - { - value => '20150630-20150810', - label => '2015-06-30 and 2015-08-10' - }, - { - value => '20150512-20150629', - label => '2015-05-12 and 2015-06-29' - }, - { - value => '20150331-20150511', - label => '2015-03-31 and 2015-05-11' - }, - { - value => '20150224-20150330', - label => '2015-02-24 and 2015-03-30' - }, - { - value => '20150113-20150223', - label => '2015-01-13 and 2015-02-23' - }, - { - value => '20141111-20141222', - label => '2014-11-11 and 2014-12-22' - }, - { - value => '20140930-20141110', - label => '2014-09-30 and 2014-11-10' - }, - { - value => '20140819-20140929', - label => '2014-08-19 and 2014-09-29' - }, - { - value => '20140708-20140818', - label => '2014-07-08 and 2014-08-18' - }, - { - value => '20140527-20140707', - label => '2014-05-27 and 2014-07-07' - }, - { - value => '20140415-20140526', - label => '2014-04-15 and 2014-05-26' - }, - { - value => '20140304-20140414', - label => '2014-03-04 and 2014-04-14' - }, - { - value => '20140121-20140303', - label => '2014-01-21 and 2014-03-03' - }, - { - value => '20131210-20140120', - label => '2013-12-10 and 2014-01-20' - }, - { - value => '20131029-20131209', - label => '2013-10-29 and 2013-12-09' - }, - { - value => '20130917-20131028', - label => '2013-09-17 and 2013-10-28' - }, - { - value => '20130806-20130916', - label => '2013-08-06 and 2013-09-16' - }, - { - value => '20130625-20130805', - label => '2013-06-25 and 2013-08-05' - }, - { - value => '20130514-20130624', - label => '2013-05-14 and 2013-06-24' - }, - { - value => '20130402-20130513', - label => '2013-04-02 and 2013-05-13' - }, - { - value => '20130219-20130401', - label => '2013-02-19 and 2013-04-01' - }, - { - value => '20130108-20130218', - label => '2013-01-08 and 2013-02-18' - }, - { - value => '20121120-20130107', - label => '2012-11-20 and 2013-01-07' - }, - { - value => '20121009-20121119', - label => '2012-10-09 and 2012-11-19' - }, - { - value => '20120828-20121008', - label => '2012-08-28 and 2012-10-08' - }, - { - value => '20120717-20120827', - label => '2012-07-17 and 2012-08-27' - }, - { - value => '20120605-20120716', - label => '2012-06-05 and 2012-07-16' - }, - { - value => '20120424-20120604', - label => '2012-04-24 and 2012-06-04' - }, - { - value => '20120313-20120423', - label => '2012-03-13 and 2012-04-23' - }, - { - value => '20120131-20120312', - label => '2012-01-31 and 2012-03-12' - }, - { - value => '20111220-20120130', - label => '2011-12-20 and 2012-01-30' - }, - { - value => '20111108-20111219', - label => '2011-11-08 and 2011-12-19' - }, - { - value => '20110927-20111107', - label => '2011-09-27 and 2011-11-07' - }, - { - value => '20110816-20110926', - label => '2011-08-16 and 2011-09-26' - }, - { - value => '*', - label => 'Anytime' - } + {value => '20160126-20160307', label => '2016-01-26 and 2016-03-07'}, + {value => '20151215-20160125', label => '2015-12-15 and 2016-01-25'}, + {value => '20151103-20151214', label => '2015-11-03 and 2015-12-14'}, + {value => '20150922-20151102', label => '2015-09-22 and 2015-11-02'}, + {value => '20150811-20150921', label => '2015-08-11 and 2015-09-21'}, + {value => '20150630-20150810', label => '2015-06-30 and 2015-08-10'}, + {value => '20150512-20150629', label => '2015-05-12 and 2015-06-29'}, + {value => '20150331-20150511', label => '2015-03-31 and 2015-05-11'}, + {value => '20150224-20150330', label => '2015-02-24 and 2015-03-30'}, + {value => '20150113-20150223', label => '2015-01-13 and 2015-02-23'}, + {value => '20141111-20141222', label => '2014-11-11 and 2014-12-22'}, + {value => '20140930-20141110', label => '2014-09-30 and 2014-11-10'}, + {value => '20140819-20140929', label => '2014-08-19 and 2014-09-29'}, + {value => '20140708-20140818', label => '2014-07-08 and 2014-08-18'}, + {value => '20140527-20140707', label => '2014-05-27 and 2014-07-07'}, + {value => '20140415-20140526', label => '2014-04-15 and 2014-05-26'}, + {value => '20140304-20140414', label => '2014-03-04 and 2014-04-14'}, + {value => '20140121-20140303', label => '2014-01-21 and 2014-03-03'}, + {value => '20131210-20140120', label => '2013-12-10 and 2014-01-20'}, + {value => '20131029-20131209', label => '2013-10-29 and 2013-12-09'}, + {value => '20130917-20131028', label => '2013-09-17 and 2013-10-28'}, + {value => '20130806-20130916', label => '2013-08-06 and 2013-09-16'}, + {value => '20130625-20130805', label => '2013-06-25 and 2013-08-05'}, + {value => '20130514-20130624', label => '2013-05-14 and 2013-06-24'}, + {value => '20130402-20130513', label => '2013-04-02 and 2013-05-13'}, + {value => '20130219-20130401', label => '2013-02-19 and 2013-04-01'}, + {value => '20130108-20130218', label => '2013-01-08 and 2013-02-18'}, + {value => '20121120-20130107', label => '2012-11-20 and 2013-01-07'}, + {value => '20121009-20121119', label => '2012-10-09 and 2012-11-19'}, + {value => '20120828-20121008', label => '2012-08-28 and 2012-10-08'}, + {value => '20120717-20120827', label => '2012-07-17 and 2012-08-27'}, + {value => '20120605-20120716', label => '2012-06-05 and 2012-07-16'}, + {value => '20120424-20120604', label => '2012-04-24 and 2012-06-04'}, + {value => '20120313-20120423', label => '2012-03-13 and 2012-04-23'}, + {value => '20120131-20120312', label => '2012-01-31 and 2012-03-12'}, + {value => '20111220-20120130', label => '2011-12-20 and 2012-01-30'}, + {value => '20111108-20111219', label => '2011-11-08 and 2011-12-19'}, + {value => '20110927-20111107', label => '2011-09-27 and 2011-11-07'}, + {value => '20110816-20110926', label => '2011-08-16 and 2011-09-26'}, + {value => '*', label => 'Anytime'} ]; sub report { - my ($vars) = @_; - my $dbh = Bugzilla->dbh; - my $input = Bugzilla->input_params; - my $user = Bugzilla->user; - - my @flag_names = qw( - approval-mozilla-release - approval-mozilla-beta - approval-mozilla-aurora - approval-mozilla-central - approval-comm-release - approval-comm-beta - approval-comm-aurora - approval-calendar-release - approval-calendar-beta - approval-calendar-aurora - approval-mozilla-esr10 - ); - - my @flags_json; - my @fields_json; - my @products_json; - - # - # tracking flags - # - - my $all_products = $user->get_selectable_products; - my @usable_products; - - # build list of flags and their matching products - - my @invalid_flag_names; - foreach my $flag_name (@flag_names) { - # grab all matching flag_types - my @flag_types = @{Bugzilla::FlagType::match({ name => $flag_name, is_active => 1 })}; - - # remove invalid flags - if (!@flag_types) { - push @invalid_flag_names, $flag_name; - next; - } + my ($vars) = @_; + my $dbh = Bugzilla->dbh; + my $input = Bugzilla->input_params; + my $user = Bugzilla->user; + + my @flag_names = qw( + approval-mozilla-release + approval-mozilla-beta + approval-mozilla-aurora + approval-mozilla-central + approval-comm-release + approval-comm-beta + approval-comm-aurora + approval-calendar-release + approval-calendar-beta + approval-calendar-aurora + approval-mozilla-esr10 + ); + + my @flags_json; + my @fields_json; + my @products_json; + + # + # tracking flags + # + + my $all_products = $user->get_selectable_products; + my @usable_products; + + # build list of flags and their matching products + + my @invalid_flag_names; + foreach my $flag_name (@flag_names) { + + # grab all matching flag_types + my @flag_types + = @{Bugzilla::FlagType::match({name => $flag_name, is_active => 1})}; + + # remove invalid flags + if (!@flag_types) { + push @invalid_flag_names, $flag_name; + next; + } - # we need a list of products, based on inclusions/exclusions - my @products; - my %flag_types; - foreach my $flag_type (@flag_types) { - $flag_types{$flag_type->name} = $flag_type->id; - my $has_all = 0; - my @exclusion_ids; - my @inclusion_ids; - foreach my $flag_type (@flag_types) { - if (scalar keys %{$flag_type->inclusions}) { - my $inclusions = $flag_type->inclusions; - foreach my $key (keys %$inclusions) { - push @inclusion_ids, ($inclusions->{$key} =~ /^(\d+)/); - } - } elsif (scalar keys %{$flag_type->exclusions}) { - my $exclusions = $flag_type->exclusions; - foreach my $key (keys %$exclusions) { - push @exclusion_ids, ($exclusions->{$key} =~ /^(\d+)/); - } - } else { - $has_all = 1; - last; - } - } - - if ($has_all) { - push @products, @$all_products; - } elsif (scalar @exclusion_ids) { - push @products, @$all_products; - foreach my $exclude_id (uniq @exclusion_ids) { - @products = grep { $_->id != $exclude_id } @products; - } - } else { - foreach my $include_id (uniq @inclusion_ids) { - push @products, grep { $_->id == $include_id } @$all_products; - } - } + # we need a list of products, based on inclusions/exclusions + my @products; + my %flag_types; + foreach my $flag_type (@flag_types) { + $flag_types{$flag_type->name} = $flag_type->id; + my $has_all = 0; + my @exclusion_ids; + my @inclusion_ids; + foreach my $flag_type (@flag_types) { + if (scalar keys %{$flag_type->inclusions}) { + my $inclusions = $flag_type->inclusions; + foreach my $key (keys %$inclusions) { + push @inclusion_ids, ($inclusions->{$key} =~ /^(\d+)/); + } } - @products = uniq @products; - push @usable_products, @products; - my @product_ids = map { $_->id } sort { lc($a->name) cmp lc($b->name) } @products; - - push @flags_json, { - name => $flag_name, - id => $flag_types{$flag_name} || 0, - products => \@product_ids, - fields => [], - }; - } - foreach my $flag_name (@invalid_flag_names) { - @flag_names = grep { $_ ne $flag_name } @flag_names; - } - @usable_products = uniq @usable_products; - - # build a list of tracking flags for each product - # also build the list of all fields - - my @unlink_products; - foreach my $product (@usable_products) { - my @fields = - sort { $a->sortkey <=> $b->sortkey } - grep { is_active_status_field($_) } - Bugzilla->active_custom_fields({ product => $product }); - my @field_ids = map { $_->id } @fields; - if (!scalar @fields) { - push @unlink_products, $product; - next; + elsif (scalar keys %{$flag_type->exclusions}) { + my $exclusions = $flag_type->exclusions; + foreach my $key (keys %$exclusions) { + push @exclusion_ids, ($exclusions->{$key} =~ /^(\d+)/); + } } - - # product - push @products_json, { - name => $product->name, - id => $product->id, - fields => \@field_ids, - }; - - # add fields to flags - foreach my $rh (@flags_json) { - if (grep { $_ eq $product->id } @{$rh->{products}}) { - push @{$rh->{fields}}, @field_ids; - } + else { + $has_all = 1; + last; } - - # add fields to fields_json - foreach my $field (@fields) { - my $existing = 0; - foreach my $rh (@fields_json) { - if ($rh->{id} == $field->id) { - $existing = 1; - last; - } - } - if (!$existing) { - push @fields_json, { - name => $field->name, - desc => $field->description, - id => $field->id, - }; - } + } + + if ($has_all) { + push @products, @$all_products; + } + elsif (scalar @exclusion_ids) { + push @products, @$all_products; + foreach my $exclude_id (uniq @exclusion_ids) { + @products = grep { $_->id != $exclude_id } @products; } + } + else { + foreach my $include_id (uniq @inclusion_ids) { + push @products, grep { $_->id == $include_id } @$all_products; + } + } } - foreach my $rh (@flags_json) { - my @fields = uniq @{$rh->{fields}}; - $rh->{fields} = \@fields; + @products = uniq @products; + push @usable_products, @products; + my @product_ids + = map { $_->id } sort { lc($a->name) cmp lc($b->name) } @products; + + push @flags_json, + { + name => $flag_name, + id => $flag_types{$flag_name} || 0, + products => \@product_ids, + fields => [], + }; + } + foreach my $flag_name (@invalid_flag_names) { + @flag_names = grep { $_ ne $flag_name } @flag_names; + } + @usable_products = uniq @usable_products; + + # build a list of tracking flags for each product + # also build the list of all fields + + my @unlink_products; + foreach my $product (@usable_products) { + my @fields + = sort { $a->sortkey <=> $b->sortkey } + grep { is_active_status_field($_) } + Bugzilla->active_custom_fields({product => $product}); + my @field_ids = map { $_->id } @fields; + if (!scalar @fields) { + push @unlink_products, $product; + next; } - # remove products which aren't linked with status fields + # product + push @products_json, + {name => $product->name, id => $product->id, fields => \@field_ids,}; + # add fields to flags foreach my $rh (@flags_json) { - my @product_ids; - foreach my $id (@{$rh->{products}}) { - unless (grep { $_->id == $id } @unlink_products) { - push @product_ids, $id; - } - $rh->{products} = \@product_ids; + if (grep { $_ eq $product->id } @{$rh->{products}}) { + push @{$rh->{fields}}, @field_ids; + } + } + + # add fields to fields_json + foreach my $field (@fields) { + my $existing = 0; + foreach my $rh (@fields_json) { + if ($rh->{id} == $field->id) { + $existing = 1; + last; } + } + if (!$existing) { + push @fields_json, + {name => $field->name, desc => $field->description, id => $field->id,}; + } } + } + foreach my $rh (@flags_json) { + my @fields = uniq @{$rh->{fields}}; + $rh->{fields} = \@fields; + } + + # remove products which aren't linked with status fields + + foreach my $rh (@flags_json) { + my @product_ids; + foreach my $id (@{$rh->{products}}) { + unless (grep { $_->id == $id } @unlink_products) { + push @product_ids, $id; + } + $rh->{products} = \@product_ids; + } + } - # - # run report - # + # + # run report + # - if ($input->{q} && !$input->{edit}) { - my $q = _parse_query($input->{q}); + if ($input->{q} && !$input->{edit}) { + my $q = _parse_query($input->{q}); - my @where; - my @params; - my $query = " + my @where; + my @params; + my $query = " SELECT DISTINCT b.bug_id FROM bugs b INNER JOIN flags f ON f.bug_id = b.bug_id\n"; - if ($q->{start_date}) { - $query .= "INNER JOIN bugs_activity a ON a.bug_id = b.bug_id\n"; - } + if ($q->{start_date}) { + $query .= "INNER JOIN bugs_activity a ON a.bug_id = b.bug_id\n"; + } - $query .= "WHERE "; + $query .= "WHERE "; - if ($q->{start_date}) { - push @where, "(a.fieldid = ?)"; - push @params, $q->{field_id}; + if ($q->{start_date}) { + push @where, "(a.fieldid = ?)"; + push @params, $q->{field_id}; - push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') >= ?)"; - push @params, $q->{start_date} . ' 00:00:00'; - push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') <= ?)"; - push @params, $q->{end_date} . ' 23:59:59'; + push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') >= ?)"; + push @params, $q->{start_date} . ' 00:00:00'; + push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') <= ?)"; + push @params, $q->{end_date} . ' 23:59:59'; - push @where, "(a.added LIKE ?)"; - push @params, '%' . $q->{flag_name} . $q->{flag_status} . '%'; - } + push @where, "(a.added LIKE ?)"; + push @params, '%' . $q->{flag_name} . $q->{flag_status} . '%'; + } - my ($type_id) = $dbh->selectrow_array( - "SELECT id FROM flagtypes WHERE name = ?", - undef, - $q->{flag_name} - ); - push @where, "(f.type_id = ?)"; - push @params, $type_id; + my ($type_id) = $dbh->selectrow_array("SELECT id FROM flagtypes WHERE name = ?", + undef, $q->{flag_name}); + push @where, "(f.type_id = ?)"; + push @params, $type_id; - push @where, "(f.status = ?)"; - push @params, $q->{flag_status}; + push @where, "(f.status = ?)"; + push @params, $q->{flag_status}; - if ($q->{product_id}) { - push @where, "(b.product_id = ?)"; - push @params, $q->{product_id}; - } + if ($q->{product_id}) { + push @where, "(b.product_id = ?)"; + push @params, $q->{product_id}; + } - if (scalar @{$q->{fields}}) { - my @fields; - foreach my $field (@{$q->{fields}}) { - my $field_sql = "("; - if ($field->{type} == FIELD_TYPE_EXTENSION) { - $field_sql .= " + if (scalar @{$q->{fields}}) { + my @fields; + foreach my $field (@{$q->{fields}}) { + my $field_sql = "("; + if ($field->{type} == FIELD_TYPE_EXTENSION) { + $field_sql .= " COALESCE( (SELECT tracking_flags_bugs.value FROM tracking_flags_bugs LEFT JOIN tracking_flags ON tracking_flags.id = tracking_flags_bugs.tracking_flag_id WHERE tracking_flags_bugs.bug_id = b.bug_id - AND tracking_flags.name = " . $dbh->quote($field->{name}) . ") + AND tracking_flags.name = " + . $dbh->quote($field->{name}) . ") , '') "; - } - else { - $field_sql .= "b." . $field->{name}; - } - $field_sql .= " " . ($field->{value} eq '+' ? '' : 'NOT ') . "IN ('fixed','verified'))"; - push(@fields, $field_sql); - } - my $join = uc $q->{join}; - push @where, '(' . join(" $join ", @fields) . ')'; } - - $query .= join("\nAND ", @where); - - my $bugs = $dbh->selectcol_arrayref($query, undef, @params); - push @$bugs, 0 unless @$bugs; - - my $urlbase = Bugzilla->localconfig->{urlbase}; - my $cgi = Bugzilla->cgi; - print $cgi->redirect( - -url => "${urlbase}buglist.cgi?bug_id=" . join(',', @$bugs) - ); - exit; + else { + $field_sql .= "b." . $field->{name}; + } + $field_sql + .= " " . ($field->{value} eq '+' ? '' : 'NOT ') . "IN ('fixed','verified'))"; + push(@fields, $field_sql); + } + my $join = uc $q->{join}; + push @where, '(' . join(" $join ", @fields) . ')'; } - # - # set template vars - # - - my $json = JSON->new()->shrink(1); - $vars->{flags_json} = $json->encode(\@flags_json); - $vars->{products_json} = $json->encode(\@products_json); - $vars->{fields_json} = $json->encode(\@fields_json); - $vars->{flag_names} = \@flag_names; - $vars->{ranges} = DATE_RANGES; - $vars->{default_query} = $input->{q}; - $vars->{is_custom} = $input->{is_custom}; - foreach my $field (qw(product flags range)) { - $vars->{$field} = $input->{$field}; - } + $query .= join("\nAND ", @where); + + my $bugs = $dbh->selectcol_arrayref($query, undef, @params); + push @$bugs, 0 unless @$bugs; + + my $urlbase = Bugzilla->localconfig->{urlbase}; + my $cgi = Bugzilla->cgi; + print $cgi->redirect( + -url => "${urlbase}buglist.cgi?bug_id=" . join(',', @$bugs)); + exit; + } + + # + # set template vars + # + + my $json = JSON->new()->shrink(1); + $vars->{flags_json} = $json->encode(\@flags_json); + $vars->{products_json} = $json->encode(\@products_json); + $vars->{fields_json} = $json->encode(\@fields_json); + $vars->{flag_names} = \@flag_names; + $vars->{ranges} = DATE_RANGES; + $vars->{default_query} = $input->{q}; + $vars->{is_custom} = $input->{is_custom}; + foreach my $field (qw(product flags range)) { + $vars->{$field} = $input->{$field}; + } } sub _parse_query { - my $q = shift; - my @query = split(/:/, $q); - my $query; - - # field_id for flag changes - $query->{field_id} = get_field_id('flagtypes.name'); - - # flag_name - my $flag_name = shift @query; - @{Bugzilla::FlagType::match({ name => $flag_name, is_active => 1 })} - or ThrowUserError('report_invalid_parameter', { name => 'flag_name' }); - trick_taint($flag_name); - $query->{flag_name} = $flag_name; - - # flag_status - my $flag_status = shift @query; - $flag_status =~ /^([\?\-\+])$/ - or ThrowUserError('report_invalid_parameter', { name => 'flag_status' }); - $query->{flag_status} = $1; - - # date_range -> from_ymd to_ymd - my $date_range = shift @query; - if ($date_range ne '*') { - $date_range =~ /^(\d\d\d\d)(\d\d)(\d\d)-(\d\d\d\d)(\d\d)(\d\d)$/ - or ThrowUserError('report_invalid_parameter', { name => 'date_range' }); - $query->{start_date} = "$1-$2-$3"; - $query->{end_date} = "$4-$5-$6"; - validate_date($query->{start_date}) - || ThrowUserError('illegal_date', { date => $query->{start_date}, - format => 'YYYY-MM-DD' }); - validate_date($query->{end_date}) - || ThrowUserError('illegal_date', { date => $query->{end_date}, - format => 'YYYY-MM-DD' }); - } - - # product_id - my $product_id = shift @query; - $product_id =~ /^(\d+)$/ - or ThrowUserError('report_invalid_parameter', { name => 'product_id' }); - $query->{product_id} = $1; - - # join - my $join = shift @query; - $join =~ /^(and|or)$/ - or ThrowUserError('report_invalid_parameter', { name => 'join' }); - $query->{join} = $1; - - # fields - my @fields; - foreach my $field (@query) { - $field =~ /^(\d+)([\-\+])$/ - or ThrowUserError('report_invalid_parameter', { name => 'fields' }); - my ($id, $value) = ($1, $2); - my $field_obj = Bugzilla::Field->new($id) - or ThrowUserError('report_invalid_parameter', { name => 'field_id' }); - push @fields, { id => $id, value => $value, - name => $field_obj->name, type => $field_obj->type }; - } - $query->{fields} = \@fields; - - return $query; + my $q = shift; + my @query = split(/:/, $q); + my $query; + + # field_id for flag changes + $query->{field_id} = get_field_id('flagtypes.name'); + + # flag_name + my $flag_name = shift @query; + @{Bugzilla::FlagType::match({name => $flag_name, is_active => 1})} + or ThrowUserError('report_invalid_parameter', {name => 'flag_name'}); + trick_taint($flag_name); + $query->{flag_name} = $flag_name; + + # flag_status + my $flag_status = shift @query; + $flag_status =~ /^([\?\-\+])$/ + or ThrowUserError('report_invalid_parameter', {name => 'flag_status'}); + $query->{flag_status} = $1; + + # date_range -> from_ymd to_ymd + my $date_range = shift @query; + if ($date_range ne '*') { + $date_range =~ /^(\d\d\d\d)(\d\d)(\d\d)-(\d\d\d\d)(\d\d)(\d\d)$/ + or ThrowUserError('report_invalid_parameter', {name => 'date_range'}); + $query->{start_date} = "$1-$2-$3"; + $query->{end_date} = "$4-$5-$6"; + validate_date($query->{start_date}) + || ThrowUserError('illegal_date', + {date => $query->{start_date}, format => 'YYYY-MM-DD'}); + validate_date($query->{end_date}) + || ThrowUserError('illegal_date', + {date => $query->{end_date}, format => 'YYYY-MM-DD'}); + } + + # product_id + my $product_id = shift @query; + $product_id =~ /^(\d+)$/ + or ThrowUserError('report_invalid_parameter', {name => 'product_id'}); + $query->{product_id} = $1; + + # join + my $join = shift @query; + $join =~ /^(and|or)$/ + or ThrowUserError('report_invalid_parameter', {name => 'join'}); + $query->{join} = $1; + + # fields + my @fields; + foreach my $field (@query) { + $field =~ /^(\d+)([\-\+])$/ + or ThrowUserError('report_invalid_parameter', {name => 'fields'}); + my ($id, $value) = ($1, $2); + my $field_obj = Bugzilla::Field->new($id) + or ThrowUserError('report_invalid_parameter', {name => 'field_id'}); + push @fields, + { + id => $id, + value => $value, + name => $field_obj->name, + type => $field_obj->type + }; + } + $query->{fields} = \@fields; + + return $query; } 1; diff --git a/extensions/BMO/lib/Reports/Triage.pm b/extensions/BMO/lib/Reports/Triage.pm index 55eeb17eb..0ccbbee6e 100644 --- a/extensions/BMO/lib/Reports/Triage.pm +++ b/extensions/BMO/lib/Reports/Triage.pm @@ -25,261 +25,279 @@ use List::MoreUtils qw(any); # set an upper limit on the *unfiltered* number of bugs to process use constant MAX_NUMBER_BUGS => 4000; -use constant DEFAULT_OWNER_PRODUCTS => ( - 'Core', - 'Firefox', - 'Firefox for Android', - 'Firefox for iOS', - 'Toolkit', -); +use constant DEFAULT_OWNER_PRODUCTS => + ('Core', 'Firefox', 'Firefox for Android', 'Firefox for iOS', 'Toolkit',); sub unconfirmed { - my ($vars, $filter) = @_; - my $dbh = Bugzilla->dbh; - my $input = Bugzilla->input_params; - my $user = Bugzilla->user; - - if (exists $input->{'action'} && $input->{'action'} eq 'run' && $input->{'product'}) { - - # load product and components from input - - my $product = Bugzilla::Product->new({ name => $input->{'product'} }) - || ThrowUserError('invalid_object', { object => 'Product', value => $input->{'product'} }); - - my @component_ids; - if ($input->{'component'} ne '') { - my $ra_components = ref($input->{'component'}) - ? $input->{'component'} : [ $input->{'component'} ]; - foreach my $component_name (@$ra_components) { - my $component = Bugzilla::Component->new({ name => $component_name, product => $product }) - || ThrowUserError('invalid_object', { object => 'Component', value => $component_name }); - push @component_ids, $component->id; - } - } + my ($vars, $filter) = @_; + my $dbh = Bugzilla->dbh; + my $input = Bugzilla->input_params; + my $user = Bugzilla->user; - # determine which comment filters to run + if ( exists $input->{'action'} + && $input->{'action'} eq 'run' + && $input->{'product'}) + { - my $filter_commenter = $input->{'filter_commenter'}; - my $filter_commenter_on = $input->{'commenter'}; - my $filter_last = $input->{'filter_last'}; - my $filter_last_period = $input->{'last'}; + # load product and components from input - if (!$filter_commenter || $filter_last) { - $filter_commenter = '1'; - $filter_commenter_on = 'reporter'; - } + my $product + = Bugzilla::Product->new({name => $input->{'product'}}) + || ThrowUserError('invalid_object', + {object => 'Product', value => $input->{'product'}}); - my $filter_commenter_id; - if ($filter_commenter && $filter_commenter_on eq 'is') { - Bugzilla::User::match_field({ 'commenter_is' => {'type' => 'single'} }); - my $user = Bugzilla::User->new({ name => $input->{'commenter_is'} }) - || ThrowUserError('invalid_object', { object => 'User', value => $input->{'commenter_is'} }); - $filter_commenter_id = $user ? $user->id : 0; - } + my @component_ids; + if ($input->{'component'} ne '') { + my $ra_components + = ref($input->{'component'}) + ? $input->{'component'} + : [$input->{'component'}]; + foreach my $component_name (@$ra_components) { + my $component + = Bugzilla::Component->new({name => $component_name, product => $product}) + || ThrowUserError('invalid_object', + {object => 'Component', value => $component_name}); + push @component_ids, $component->id; + } + } - my $filter_last_time; - if ($filter_last) { - if ($filter_last_period eq 'is') { - $filter_last_period = -1; - $filter_last_time = str2time($input->{'last_is'} . " 00:00:00") || 0; - } else { - detaint_natural($filter_last_period); - $filter_last_period = 14 if $filter_last_period < 14; - } - } + # determine which comment filters to run + + my $filter_commenter = $input->{'filter_commenter'}; + my $filter_commenter_on = $input->{'commenter'}; + my $filter_last = $input->{'filter_last'}; + my $filter_last_period = $input->{'last'}; + + if (!$filter_commenter || $filter_last) { + $filter_commenter = '1'; + $filter_commenter_on = 'reporter'; + } + + my $filter_commenter_id; + if ($filter_commenter && $filter_commenter_on eq 'is') { + Bugzilla::User::match_field({'commenter_is' => {'type' => 'single'}}); + my $user + = Bugzilla::User->new({name => $input->{'commenter_is'}}) + || ThrowUserError('invalid_object', + {object => 'User', value => $input->{'commenter_is'}}); + $filter_commenter_id = $user ? $user->id : 0; + } - # form sql queries + my $filter_last_time; + if ($filter_last) { + if ($filter_last_period eq 'is') { + $filter_last_period = -1; + $filter_last_time = str2time($input->{'last_is'} . " 00:00:00") || 0; + } + else { + detaint_natural($filter_last_period); + $filter_last_period = 14 if $filter_last_period < 14; + } + } + + # form sql queries - my $now = (time); - my $bugs_sql = " + my $now = (time); + my $bugs_sql = " SELECT bug_id, short_desc, reporter, creation_ts FROM bugs WHERE product_id = ? AND bug_status = 'UNCONFIRMED'"; - if (@component_ids) { - $bugs_sql .= " AND component_id IN (" . join(',', @component_ids) . ")"; - } - $bugs_sql .= " + if (@component_ids) { + $bugs_sql .= " AND component_id IN (" . join(',', @component_ids) . ")"; + } + $bugs_sql .= " ORDER BY creation_ts "; - my $comment_count_sql = " + my $comment_count_sql = " SELECT COUNT(*) FROM longdescs WHERE bug_id = ? "; - my $comment_sql = " + my $comment_sql = " SELECT who, bug_when, type, thetext, extra_data FROM longdescs WHERE bug_id = ? "; - if (!Bugzilla->user->is_insider) { - $comment_sql .= " AND isprivate = 0 "; - } - $comment_sql .= " + if (!Bugzilla->user->is_insider) { + $comment_sql .= " AND isprivate = 0 "; + } + $comment_sql .= " ORDER BY bug_when DESC LIMIT 1 "; - my $attach_sql = " + my $attach_sql = " SELECT description, isprivate FROM attachments WHERE attach_id = ? "; - # work on an initial list of bugs + # work on an initial list of bugs - my $list = $dbh->selectall_arrayref($bugs_sql, undef, $product->id); - my @bugs; + my $list = $dbh->selectall_arrayref($bugs_sql, undef, $product->id); + my @bugs; - # this can be slow to process, resulting in 'service unavailable' errors from zeus - # so if too many bugs are returned, throw an error + # this can be slow to process, resulting in 'service unavailable' errors from zeus + # so if too many bugs are returned, throw an error - if (scalar(@$list) > MAX_NUMBER_BUGS) { - ThrowUserError('report_too_many_bugs'); - } + if (scalar(@$list) > MAX_NUMBER_BUGS) { + ThrowUserError('report_too_many_bugs'); + } - foreach my $entry (@$list) { - my ($bug_id, $summary, $reporter_id, $creation_ts) = @$entry; - - next unless $user->can_see_bug($bug_id); - - # get last comment information - - my ($comment_count) = $dbh->selectrow_array($comment_count_sql, undef, $bug_id); - my ($commenter_id, $comment_ts, $type, $comment, $extra) - = $dbh->selectrow_array($comment_sql, undef, $bug_id); - my $commenter = 0; - - # apply selected filters - - if ($filter_commenter) { - next if $comment_count <= 1; - - if ($filter_commenter_on eq 'reporter') { - next if $commenter_id != $reporter_id; - - } elsif ($filter_commenter_on eq 'noconfirm') { - $commenter = Bugzilla::User->new({ id => $commenter_id, cache => 1 }); - next if $commenter_id != $reporter_id - || $commenter->in_group('canconfirm'); - - } elsif ($filter_commenter_on eq 'is') { - next if $commenter_id != $filter_commenter_id; - } - } else { - $input->{'commenter'} = ''; - $input->{'commenter_is'} = ''; - } - - if ($filter_last) { - my $comment_time = str2time($comment_ts) - or next; - if ($filter_last_period == -1) { - next if $comment_time >= $filter_last_time; - } else { - next if $now - $comment_time <= 60 * 60 * 24 * $filter_last_period; - } - } else { - $input->{'last'} = ''; - $input->{'last_is'} = ''; - } - - # get data for attachment comments - - if ($comment eq '' && $type == CMT_ATTACHMENT_CREATED) { - my ($description, $is_private) = $dbh->selectrow_array($attach_sql, undef, $extra); - next if $is_private && !Bugzilla->user->is_insider; - $comment = "(Attachment) " . $description; - } - - # truncate long comments - - if (length($comment) > 80) { - $comment = substr($comment, 0, 80) . '...'; - } - - # build bug hash for template - - my $bug = {}; - $bug->{id} = $bug_id; - $bug->{summary} = $summary; - $bug->{reporter} = Bugzilla::User->new({ id => $reporter_id, cache => 1 }); - $bug->{creation_ts} = $creation_ts; - $bug->{commenter} = $commenter || Bugzilla::User->new({ id => $commenter_id, cache => 1 }); - $bug->{comment_ts} = $comment_ts; - $bug->{comment} = $comment; - $bug->{comment_count} = $comment_count; - push @bugs, $bug; - } + foreach my $entry (@$list) { + my ($bug_id, $summary, $reporter_id, $creation_ts) = @$entry; - @bugs = sort { $b->{comment_ts} cmp $a->{comment_ts} } @bugs; + next unless $user->can_see_bug($bug_id); - $vars->{bugs} = \@bugs; - } else { - $input->{action} = ''; - } + # get last comment information - if (!$input->{filter_commenter} && !$input->{filter_last}) { - $input->{filter_commenter} = 1; - } + my ($comment_count) = $dbh->selectrow_array($comment_count_sql, undef, $bug_id); + my ($commenter_id, $comment_ts, $type, $comment, $extra) + = $dbh->selectrow_array($comment_sql, undef, $bug_id); + my $commenter = 0; - $vars->{'input'} = $input; -} + # apply selected filters -sub owners { - my ($vars, $filter) = @_; - my $dbh = Bugzilla->dbh; - my $input = Bugzilla->input_params; - my $user = Bugzilla->user; + if ($filter_commenter) { + next if $comment_count <= 1; - Bugzilla::User::match_field({ 'owner' => {'type' => 'multi'} }); + if ($filter_commenter_on eq 'reporter') { + next if $commenter_id != $reporter_id; - my @products; - if (!$input->{product} && $input->{owner}) { - @products = @{ $user->get_selectable_products }; - } - else { - my @product_names = $input->{product} ? ($input->{product}) : DEFAULT_OWNER_PRODUCTS; - foreach my $name (@product_names) { - push(@products, Bugzilla::Product->check({ name => $name })); } - } + elsif ($filter_commenter_on eq 'noconfirm') { + $commenter = Bugzilla::User->new({id => $commenter_id, cache => 1}); + next if $commenter_id != $reporter_id || $commenter->in_group('canconfirm'); - my @component_ids; - if (@products == 1 && $input->{'component'}) { - my $ra_components = ref($input->{'component'}) - ? $input->{'component'} - : [ $input->{'component'} ]; - foreach my $component_name (@$ra_components) { - my $component = Bugzilla::Component->check({ name => $component_name, product => $products[0] }); - push @component_ids, $component->id; } + elsif ($filter_commenter_on eq 'is') { + next if $commenter_id != $filter_commenter_id; + } + } + else { + $input->{'commenter'} = ''; + $input->{'commenter_is'} = ''; + } + + if ($filter_last) { + my $comment_time = str2time($comment_ts) or next; + if ($filter_last_period == -1) { + next if $comment_time >= $filter_last_time; + } + else { + next if $now - $comment_time <= 60 * 60 * 24 * $filter_last_period; + } + } + else { + $input->{'last'} = ''; + $input->{'last_is'} = ''; + } + + # get data for attachment comments + + if ($comment eq '' && $type == CMT_ATTACHMENT_CREATED) { + my ($description, $is_private) + = $dbh->selectrow_array($attach_sql, undef, $extra); + next if $is_private && !Bugzilla->user->is_insider; + $comment = "(Attachment) " . $description; + } + + # truncate long comments + + if (length($comment) > 80) { + $comment = substr($comment, 0, 80) . '...'; + } + + # build bug hash for template + + my $bug = {}; + $bug->{id} = $bug_id; + $bug->{summary} = $summary; + $bug->{reporter} = Bugzilla::User->new({id => $reporter_id, cache => 1}); + $bug->{creation_ts} = $creation_ts; + $bug->{commenter} + = $commenter || Bugzilla::User->new({id => $commenter_id, cache => 1}); + $bug->{comment_ts} = $comment_ts; + $bug->{comment} = $comment; + $bug->{comment_count} = $comment_count; + push @bugs, $bug; } - my @owner_names = split(/[,;]+/, $input->{owner}) if $input->{owner}; - my @owner_ids; - foreach my $name (@owner_names) { - $name = trim($name); - next unless $name; - push(@owner_ids, login_to_id($name, THROW_ERROR)); - } + @bugs = sort { $b->{comment_ts} cmp $a->{comment_ts} } @bugs; - my $sql = "SELECT products.name, components.name, components.id, components.triage_owner_id - FROM components JOIN products ON components.product_id = products.id - WHERE products.id IN (" . join(',', map { $_->id } @products) . ")"; - if (@component_ids) { - $sql .= " AND components.id IN (" . join(',', @component_ids) . ")"; - } - if (@owner_ids) { - $sql .= " AND components.triage_owner_id IN (" . join(',', @owner_ids) . ")"; - } - $sql .= " ORDER BY products.name, components.name"; + $vars->{bugs} = \@bugs; + } + else { + $input->{action} = ''; + } - my $rows = $dbh->selectall_arrayref($sql); + if (!$input->{filter_commenter} && !$input->{filter_last}) { + $input->{filter_commenter} = 1; + } - my $bug_count_sth = $dbh->prepare(" + $vars->{'input'} = $input; +} + +sub owners { + my ($vars, $filter) = @_; + my $dbh = Bugzilla->dbh; + my $input = Bugzilla->input_params; + my $user = Bugzilla->user; + + Bugzilla::User::match_field({'owner' => {'type' => 'multi'}}); + + my @products; + if (!$input->{product} && $input->{owner}) { + @products = @{$user->get_selectable_products}; + } + else { + my @product_names + = $input->{product} ? ($input->{product}) : DEFAULT_OWNER_PRODUCTS; + foreach my $name (@product_names) { + push(@products, Bugzilla::Product->check({name => $name})); + } + } + + my @component_ids; + if (@products == 1 && $input->{'component'}) { + my $ra_components + = ref($input->{'component'}) + ? $input->{'component'} + : [$input->{'component'}]; + foreach my $component_name (@$ra_components) { + my $component = Bugzilla::Component->check( + {name => $component_name, product => $products[0]}); + push @component_ids, $component->id; + } + } + + my @owner_names = split(/[,;]+/, $input->{owner}) if $input->{owner}; + my @owner_ids; + foreach my $name (@owner_names) { + $name = trim($name); + next unless $name; + push(@owner_ids, login_to_id($name, THROW_ERROR)); + } + + my $sql + = "SELECT products.name, components.name, components.id, components.triage_owner_id + FROM components JOIN products ON components.product_id = products.id + WHERE products.id IN (" + . join(',', map { $_->id } @products) . ")"; + if (@component_ids) { + $sql .= " AND components.id IN (" . join(',', @component_ids) . ")"; + } + if (@owner_ids) { + $sql .= " AND components.triage_owner_id IN (" . join(',', @owner_ids) . ")"; + } + $sql .= " ORDER BY products.name, components.name"; + + my $rows = $dbh->selectall_arrayref($sql); + + my $bug_count_sth = $dbh->prepare(" SELECT COUNT(bugs.bug_id) FROM bugs INNER JOIN components AS map_component ON bugs.component_id = map_component.id INNER JOIN bug_status AS map_bug_status ON bugs.bug_status = map_bug_status.value @@ -296,55 +314,57 @@ sub owners { WHERE bugs_1.bug_id = bugs.bug_id AND CONCAT(flagtypes_1.name, flags_1.status) = 'needinfo?'))) AND bugs.component_id = ?"); - my @results; - foreach my $row (@$rows) { - my ($product_name, $component_name, $component_id, $triage_owner_id) = @$row; - my $triage_owner = $triage_owner_id - ? Bugzilla::User->new({ id => $triage_owner_id, cache => 1 }) - : ""; - my $data = { - product => $product_name, - component => $component_name, - owner => $triage_owner, - }; - $data->{buglist_url} = 'priority=--&resolution=---&f1=creation_ts&o1=greaterthaneq&v1=2016-06-01'. - '&f2=flagtypes.name&o2=notequals&v2=needinfo%3F'; - if ($triage_owner) { - $data->{buglist_url} .= '&f3=triage_owner&o3=equals&v3=' . url_quote($triage_owner->login); - } - $bug_count_sth->execute($component_id); - ($data->{bug_count}) = $bug_count_sth->fetchrow_array(); - push @results, $data; + my @results; + foreach my $row (@$rows) { + my ($product_name, $component_name, $component_id, $triage_owner_id) = @$row; + my $triage_owner + = $triage_owner_id + ? Bugzilla::User->new({id => $triage_owner_id, cache => 1}) + : ""; + my $data = { + product => $product_name, + component => $component_name, + owner => $triage_owner, + }; + $data->{buglist_url} + = 'priority=--&resolution=---&f1=creation_ts&o1=greaterthaneq&v1=2016-06-01' + . '&f2=flagtypes.name&o2=notequals&v2=needinfo%3F'; + if ($triage_owner) { + $data->{buglist_url} + .= '&f3=triage_owner&o3=equals&v3=' . url_quote($triage_owner->login); } - $vars->{results} = \@results; - - my $json_data = { products => [] }; - foreach my $product (@{ $user->get_selectable_products }) { - my $prod_data = { - name => $product->name, - components => [], - }; - foreach my $component (@{ $product->components }) { - my $selected = 0; - if ($input->{product} - && $input->{product} eq $product->name - && $input->{component}) - { - $selected = 1 if (ref $input->{component} && any { $_ eq $component->name } @{ $input->{component} }); - $selected = 1 if (!ref $input->{componet} && $input->{component} eq $component->name); - } - my $comp_data = { - name => $component->name, - selected => $selected - }; - push(@{ $prod_data->{components} }, $comp_data); - } - push(@{ $json_data->{products} }, $prod_data); + $bug_count_sth->execute($component_id); + ($data->{bug_count}) = $bug_count_sth->fetchrow_array(); + push @results, $data; + } + $vars->{results} = \@results; + + my $json_data = {products => []}; + foreach my $product (@{$user->get_selectable_products}) { + my $prod_data = {name => $product->name, components => [],}; + foreach my $component (@{$product->components}) { + my $selected = 0; + if ( $input->{product} + && $input->{product} eq $product->name + && $input->{component}) + { + $selected = 1 + if ( + ref $input->{component} && any { $_ eq $component->name } + @{$input->{component}} + ); + $selected = 1 + if (!ref $input->{componet} && $input->{component} eq $component->name); + } + my $comp_data = {name => $component->name, selected => $selected}; + push(@{$prod_data->{components}}, $comp_data); } + push(@{$json_data->{products}}, $prod_data); + } - $vars->{product} = $input->{product}; - $vars->{owner} = $input->{owner}; - $vars->{json_data} = encode_json($json_data); + $vars->{product} = $input->{product}; + $vars->{owner} = $input->{owner}; + $vars->{json_data} = encode_json($json_data); } 1; diff --git a/extensions/BMO/lib/Reports/UserActivity.pm b/extensions/BMO/lib/Reports/UserActivity.pm index 8dfe0c5cd..3be6f74c9 100644 --- a/extensions/BMO/lib/Reports/UserActivity.pm +++ b/extensions/BMO/lib/Reports/UserActivity.pm @@ -18,104 +18,104 @@ use Bugzilla::Util qw(trim); use DateTime; sub report { - my ($vars) = @_; - my $dbh = Bugzilla->dbh; - my $input = Bugzilla->input_params; - - my @who = (); - my $from = trim($input->{'from'} || ''); - my $to = trim($input->{'to'} || ''); - my $action = $input->{'action'} || ''; - - # fix non-breaking hyphens - $from =~ s/\N{U+2011}/-/g; - $to =~ s/\N{U+2011}/-/g; - - if ($from eq '') { - my $dt = DateTime->now()->subtract('weeks' => 1); - $from = $dt->ymd('-'); + my ($vars) = @_; + my $dbh = Bugzilla->dbh; + my $input = Bugzilla->input_params; + + my @who = (); + my $from = trim($input->{'from'} || ''); + my $to = trim($input->{'to'} || ''); + my $action = $input->{'action'} || ''; + + # fix non-breaking hyphens + $from =~ s/\N{U+2011}/-/g; + $to =~ s/\N{U+2011}/-/g; + + if ($from eq '') { + my $dt = DateTime->now()->subtract('weeks' => 1); + $from = $dt->ymd('-'); + } + if ($to eq '') { + my $dt = DateTime->now(); + $to = $dt->ymd('-'); + } + + if ($action eq 'run') { + if (!exists $input->{'who'} || $input->{'who'} eq '') { + ThrowUserError('user_activity_missing_username'); } - if ($to eq '') { - my $dt = DateTime->now(); - $to = $dt->ymd('-'); - } - - if ($action eq 'run') { - if (!exists $input->{'who'} || $input->{'who'} eq '') { - ThrowUserError('user_activity_missing_username'); - } - Bugzilla::User::match_field({ 'who' => {'type' => 'multi'} }); + Bugzilla::User::match_field({'who' => {'type' => 'multi'}}); - my $from_dt = string_to_datetime($from); - $from = $from_dt->ymd(); + my $from_dt = string_to_datetime($from); + $from = $from_dt->ymd(); - my $to_dt = string_to_datetime($to); - $to = $to_dt->ymd(); + my $to_dt = string_to_datetime($to); + $to = $to_dt->ymd(); - my ($activity_joins, $activity_where) = ('', ''); - my ($attachments_joins, $attachments_where) = ('', ''); - my ($tags_activity_joins, $tags_activity_where) = ('', ''); - if (Bugzilla->params->{"insidergroup"} - && !Bugzilla->user->in_group(Bugzilla->params->{'insidergroup'})) - { - $activity_joins = "LEFT JOIN attachments + my ($activity_joins, $activity_where) = ('', ''); + my ($attachments_joins, $attachments_where) = ('', ''); + my ($tags_activity_joins, $tags_activity_where) = ('', ''); + if (Bugzilla->params->{"insidergroup"} + && !Bugzilla->user->in_group(Bugzilla->params->{'insidergroup'})) + { + $activity_joins = "LEFT JOIN attachments ON attachments.attach_id = bugs_activity.attach_id"; - $activity_where = "AND COALESCE(attachments.isprivate, 0) = 0"; - $attachments_where = $activity_where; + $activity_where = "AND COALESCE(attachments.isprivate, 0) = 0"; + $attachments_where = $activity_where; - $tags_activity_joins = 'LEFT JOIN longdescs + $tags_activity_joins = 'LEFT JOIN longdescs ON longdescs_tags_activity.comment_id = longdescs.comment_id'; - $tags_activity_where = 'AND COALESCE(longdescs.isprivate, 0) = 0'; - } + $tags_activity_where = 'AND COALESCE(longdescs.isprivate, 0) = 0'; + } - my @who_bits; - foreach my $who ( - ref $input->{'who'} - ? @{$input->{'who'}} - : $input->{'who'} - ) { - push @who, $who; - push @who_bits, '?'; - } - my $who_bits = join(',', @who_bits); - - if (!@who) { - my $template = Bugzilla->template; - my $cgi = Bugzilla->cgi; - my $vars = {}; - $vars->{'script'} = $cgi->url(-relative => 1); - $vars->{'fields'} = {}; - $vars->{'matches'} = []; - $vars->{'matchsuccess'} = 0; - $vars->{'matchmultiple'} = 1; - print $cgi->header(); - $template->process("global/confirm-user-match.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - exit; - } + my @who_bits; + foreach my $who (ref $input->{'who'} ? @{$input->{'who'}} : $input->{'who'}) { + push @who, $who; + push @who_bits, '?'; + } + my $who_bits = join(',', @who_bits); + + if (!@who) { + my $template = Bugzilla->template; + my $cgi = Bugzilla->cgi; + my $vars = {}; + $vars->{'script'} = $cgi->url(-relative => 1); + $vars->{'fields'} = {}; + $vars->{'matches'} = []; + $vars->{'matchsuccess'} = 0; + $vars->{'matchmultiple'} = 1; + print $cgi->header(); + $template->process("global/confirm-user-match.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } - $from_dt = $from_dt->ymd() . ' 00:00:00'; - $to_dt = $to_dt->ymd() . ' 23:59:59'; - my @params; - for (1..5) { - push @params, @who; - push @params, ($from_dt, $to_dt); - } + $from_dt = $from_dt->ymd() . ' 00:00:00'; + $to_dt = $to_dt->ymd() . ' 23:59:59'; + my @params; + for (1 .. 5) { + push @params, @who; + push @params, ($from_dt, $to_dt); + } - my $order = ($input->{'group'} && $input->{'group'} eq 'bug') - ? 'bug_id, bug_when' : 'bug_when'; + my $order + = ($input->{'group'} && $input->{'group'} eq 'bug') + ? 'bug_id, bug_when' + : 'bug_when'; - my $comment_filter = ''; - if (!Bugzilla->user->is_insider) { - $comment_filter = 'AND longdescs.isprivate = 0'; - } + my $comment_filter = ''; + if (!Bugzilla->user->is_insider) { + $comment_filter = 'AND longdescs.isprivate = 0'; + } - my $query = " + my $query = " SELECT fielddefs.name, bugs_activity.bug_id, bugs_activity.attach_id, - ".$dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s')." AS ts, + " + . $dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s') + . " AS ts, bugs_activity.removed, bugs_activity.added, profiles.login_name, @@ -138,8 +138,10 @@ sub report { 'comment_tag' AS name, longdescs_tags_activity.bug_id, NULL as attach_id, - ".$dbh->sql_date_format('longdescs_tags_activity.bug_when', - '%Y.%m.%d %H:%i:%s') . " AS bug_when, + " + . $dbh->sql_date_format('longdescs_tags_activity.bug_when', + '%Y.%m.%d %H:%i:%s') + . " AS bug_when, longdescs_tags_activity.removed, longdescs_tags_activity.added, profiles.login_name, @@ -160,7 +162,8 @@ sub report { 'bug_id' AS name, bugs.bug_id, NULL AS attach_id, - ".$dbh->sql_date_format('bugs.creation_ts', '%Y.%m.%d %H:%i:%s')." AS ts, + " + . $dbh->sql_date_format('bugs.creation_ts', '%Y.%m.%d %H:%i:%s') . " AS ts, '(new bug)' AS removed, bugs.short_desc AS added, profiles.login_name, @@ -199,7 +202,9 @@ sub report { 'attachments.description' AS name, attachments.bug_id, attachments.attach_id, - ".$dbh->sql_date_format('attachments.creation_ts', '%Y.%m.%d %H:%i:%s')." AS ts, + " + . $dbh->sql_date_format('attachments.creation_ts', '%Y.%m.%d %H:%i:%s') + . " AS ts, '(new attachment)' AS removed, attachments.description AS added, profiles.login_name, @@ -215,119 +220,118 @@ sub report { ORDER BY $order "; - my $list = $dbh->selectall_arrayref($query, undef, @params); + my $list = $dbh->selectall_arrayref($query, undef, @params); - if ($input->{debug}) { - while (my $param = shift @params) { - $query =~ s/\?/$dbh->quote($param)/e; - } - $vars->{debug_sql} = $query; + if ($input->{debug}) { + while (my $param = shift @params) { + $query =~ s/\?/$dbh->quote($param)/e; + } + $vars->{debug_sql} = $query; + } + + my @operations; + my $operation = {}; + my $changes = []; + my $incomplete_data = 0; + my %bug_ids; + + foreach my $entry (@$list) { + my ($fieldname, $bugid, $attachid, $when, $removed, $added, $who, $comment_id) + = @$entry; + my %change; + my $activity_visible = 1; + + next unless Bugzilla->user->can_see_bug($bugid); + + # check if the user should see this field's activity + if ( $fieldname eq 'remaining_time' + || $fieldname eq 'estimated_time' + || $fieldname eq 'work_time' + || $fieldname eq 'deadline') + { + $activity_visible = Bugzilla->user->is_timetracker; + } + elsif ($fieldname eq 'longdescs.isprivate' + && !Bugzilla->user->is_insider + && $added) + { + $activity_visible = 0; + } + else { + $activity_visible = 1; + } + + if ($activity_visible) { + + # Check for the results of an old Bugzilla data corruption bug + if ( ($added eq '?' && $removed eq '?') + || ($added =~ /^\? / || $removed =~ /^\? /)) + { + $incomplete_data = 1; } - my @operations; - my $operation = {}; - my $changes = []; - my $incomplete_data = 0; - my %bug_ids; - - foreach my $entry (@$list) { - my ($fieldname, $bugid, $attachid, $when, $removed, $added, $who, - $comment_id) = @$entry; - my %change; - my $activity_visible = 1; - - next unless Bugzilla->user->can_see_bug($bugid); - - # check if the user should see this field's activity - if ($fieldname eq 'remaining_time' - || $fieldname eq 'estimated_time' - || $fieldname eq 'work_time' - || $fieldname eq 'deadline') - { - $activity_visible = Bugzilla->user->is_timetracker; - } - elsif ($fieldname eq 'longdescs.isprivate' - && !Bugzilla->user->is_insider - && $added) - { - $activity_visible = 0; - } - else { - $activity_visible = 1; - } - - if ($activity_visible) { - # Check for the results of an old Bugzilla data corruption bug - if (($added eq '?' && $removed eq '?') - || ($added =~ /^\? / || $removed =~ /^\? /)) { - $incomplete_data = 1; - } - - # Start a new changeset if required (depends on the grouping type) - my $is_new_changeset; - if ($order eq 'bug_when') { - $is_new_changeset = - $operation->{'who'} && - ( - $who ne $operation->{'who'} - || $when ne $operation->{'when'} - || $bugid != $operation->{'bug'} - ); - } else { - $is_new_changeset = - $operation->{'bug'} && - $bugid != $operation->{'bug'}; - } - if ($is_new_changeset) { - $operation->{'changes'} = $changes; - push (@operations, $operation); - $operation = {}; - $changes = []; - } - - $bug_ids{$bugid} = 1; - - $operation->{'bug'} = $bugid; - $operation->{'who'} = $who; - $operation->{'when'} = $when; - - $change{'fieldname'} = $fieldname; - $change{'attachid'} = $attachid; - $change{'removed'} = $removed; - $change{'added'} = $added; - $change{'when'} = $when; - - if ($comment_id) { - $change{'comment'} = Bugzilla::Comment->new($comment_id); - next if $change{'comment'}->count == 0; - } - - if ($attachid) { - $change{'attach'} = Bugzilla::Attachment->new($attachid); - } - - push (@$changes, \%change); - } + # Start a new changeset if required (depends on the grouping type) + my $is_new_changeset; + if ($order eq 'bug_when') { + $is_new_changeset + = $operation->{'who'} + && ($who ne $operation->{'who'} + || $when ne $operation->{'when'} + || $bugid != $operation->{'bug'}); + } + else { + $is_new_changeset = $operation->{'bug'} && $bugid != $operation->{'bug'}; } + if ($is_new_changeset) { + $operation->{'changes'} = $changes; + push(@operations, $operation); + $operation = {}; + $changes = []; + } + + $bug_ids{$bugid} = 1; + + $operation->{'bug'} = $bugid; + $operation->{'who'} = $who; + $operation->{'when'} = $when; - if ($operation->{'who'}) { - $operation->{'changes'} = $changes; - push (@operations, $operation); + $change{'fieldname'} = $fieldname; + $change{'attachid'} = $attachid; + $change{'removed'} = $removed; + $change{'added'} = $added; + $change{'when'} = $when; + + if ($comment_id) { + $change{'comment'} = Bugzilla::Comment->new($comment_id); + next if $change{'comment'}->count == 0; + } + + if ($attachid) { + $change{'attach'} = Bugzilla::Attachment->new($attachid); } - $vars->{'incomplete_data'} = $incomplete_data; - $vars->{'operations'} = \@operations; + push(@$changes, \%change); + } + } - my @bug_ids = sort { $a <=> $b } keys %bug_ids; - $vars->{'bug_ids'} = \@bug_ids; + if ($operation->{'who'}) { + $operation->{'changes'} = $changes; + push(@operations, $operation); } - $vars->{'action'} = $action; - $vars->{'who'} = join(',', @who); - $vars->{'who_count'} = scalar @who; - $vars->{'from'} = $from; - $vars->{'to'} = $to; - $vars->{'group'} = $input->{'group'}; + $vars->{'incomplete_data'} = $incomplete_data; + $vars->{'operations'} = \@operations; + + my @bug_ids = sort { $a <=> $b } keys %bug_ids; + $vars->{'bug_ids'} = \@bug_ids; + } + + $vars->{'action'} = $action; + $vars->{'who'} = join(',', @who); + $vars->{'who_count'} = scalar @who; + $vars->{'from'} = $from; + $vars->{'to'} = $to; + $vars->{'group'} = $input->{'group'}; } 1; |