diff options
Diffstat (limited to 'extensions/RuleEngine/lib/RuleGroup.pm')
-rw-r--r-- | extensions/RuleEngine/lib/RuleGroup.pm | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/extensions/RuleEngine/lib/RuleGroup.pm b/extensions/RuleEngine/lib/RuleGroup.pm new file mode 100644 index 000000000..65d9b1809 --- /dev/null +++ b/extensions/RuleEngine/lib/RuleGroup.pm @@ -0,0 +1,861 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package Bugzilla::Extension::RuleEngine::RuleGroup; + +use strict; +use warnings; +use 5.10.1; +use base qw(Bugzilla::Object Exporter); +@Bugzilla::Extension::RuleEngine::RuleGroup::EXPORT = qw(sort_rules_by_group); + +use Bugzilla::Constants; +use Bugzilla::Field; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Group; +use Bugzilla::Product; +use Bugzilla::User; +use Scalar::Util qw(blessed); +use List::MoreUtils qw(any none); +use Bugzilla::Extension::RuleEngine::RuleState; + +############################### +#### Initialization #### +############################### + +use constant DB_TABLE => 'rh_rule_group'; +use constant LIST_ORDER => 'name'; + +use constant DB_COLUMNS => qw( + id + name + description + isactive + user_id +); + +use constant UPDATE_COLUMNS => qw( + name + description + isactive + user_id +); + +use constant VALIDATORS => { + name => \&_check_name, + description => \&_check_description, + isactive => \&Bugzilla::Object::check_boolean, + user_id => \&_check_user_id +}; + +############################### +#### Validators #### +############################### + +sub _check_name { + my ($invocant, $name) = @_; + + $name = trim($name); + $name || ThrowUserError('rule_group_not_specified'); + + if (length($name) > 64) { + ThrowUserError('rule_group_name_too_long', {'name' => $name}); + } + + my $rule_group + = Bugzilla::Extension::RuleEngine::RuleGroup->new({name => $name}); + if ($rule_group && (!ref $invocant || $rule_group->id != $invocant->id)) { + ThrowUserError("rule_group_already_exists", {name => $rule_group->name}); + } + return $name; +} + +sub _check_description { + my ($invocant, $description) = @_; + + $description = trim($description || ''); + return $description; +} + +sub _check_user_id { + my ($invocant, $id) = @_; + + my $user; + + if ($id && $id ne '') { + $user = get_user($id); + } + + return ($user ? $user->id : undef); +} + +############################### +#### Methods #### +############################### + +sub set_name { $_[0]->set('name', $_[1]); return; } +sub set_description { $_[0]->set('description', $_[1]); return; } +sub set_is_active { $_[0]->set('isactive', $_[1]); return; } +sub set_user_id { $_[0]->set('user_id', $_[1]); return; } + +sub set_maintainers { + my ($self, $names) = @_; + + $self->{'_old_maintainers'} = $self->maintainers; + $self->{maintainers} = undef; + + my $dbh = Bugzilla->dbh; + $dbh->do("DELETE FROM rh_rule_group_maintainers WHERE rule_group_id = ?", + undef, ($self->id)); + + foreach my $name (@$names) { + next unless ($name); + my $user = get_user($name); + next unless ($user); + $dbh->do( + "INSERT INTO rh_rule_group_maintainers (rule_group_id, user_id) VALUES (?, ?)", + undef, + ($self->id, $user->id) + ); + } + + $self->maintainers; + + return; +} + +sub rule_count { + my $self = shift; + my $dbh = Bugzilla->dbh; + + if (!defined $self->{'rule_count'}) { + $self->{'rule_count'} = $dbh->selectrow_array( + q{ + SELECT COUNT(*) FROM rh_rule + WHERE rule_group_id = ?}, undef, $self->id + ) || 0; + } + return $self->{'rule_count'}; +} + +############################### +#### Accessors #### +############################### + +sub description { return $_[0]->{'description'}; } +sub is_active { return $_[0]->{'isactive'}; } +sub user_id { return $_[0]->{'user_id'}; } + +sub user_name { + my $self = shift; + my $name = ''; + if ($self->user_id()) { + my $user = get_user($self->user_id()); + $name = $user->login; + } + + return ($name); +} + +sub maintainer_names { + my $self = shift; + my $str = ''; + my $maintainers = $self->maintainers; + + $str = join(', ', map { $_->login } @$maintainers); + + return ($str); +} + +sub maintainers { + my $self = shift; + my @users = (); + + if (!defined $self->{maintainers}) { + my $ids = Bugzilla->dbh->selectcol_arrayref( + "SELECT user_id FROM rh_rule_group_maintainers + WHERE rule_group_id = ?", undef, $self->id + ); + + if (@$ids) { + $self->{maintainers} = Bugzilla::User->new_from_list($ids); + } + } + + return $self->{maintainers}; +} + +############################### +#### Helpers #### +############################### + +# This function is a helper to sort rules to be listed +# in global/choose-rule.html.tmpl. + +sub sort_rules_by_group { + my ($self, $rules) = @_; + + my $group = {}; + + # Get all rule groups with at least one rule. + foreach my $rule (@$rules) { + $group->{$rule->rule_group_id}->{'object'} + ||= new Bugzilla::Extension::RuleEngine::RuleGroup($rule->rule_group_id); + + # Nice way to group rules per rule group, without querying + # the DB again. + push(@{$group->{$rule->rule_group_id}->{'rules'}}, $rule); + } + return [sort { lc($a->{'object'}->name) cmp lc($b->{'object'}->name) } + (values %$group)]; + +} + +## RED HAT EXTENSION START 1291476 + +sub get_group { + my $group = shift; + if (!blessed $group) { + if ($group =~ /^\d+$/) { + $group = Bugzilla::Group->check({id => $group}); + } + else { + $group = Bugzilla::Group->check({name => $group}); + } + } + return $group; +} + +sub get_user { + my $user = shift; + if (!blessed $user) { + if ($user =~ /^\d+$/) { + $user = Bugzilla::User->check({id => $user}); + } + else { + $user = Bugzilla::User->check($user); + } + } + + return $user; +} + +sub view_groups { + my $self = shift; + return [] unless $self->id; + if (!defined $self->{view_groups}) { + my $gids = Bugzilla->dbh->selectcol_arrayref( + "SELECT group_id FROM rh_rule_group_view_groups + WHERE rule_group_id = ?", undef, $self->id + ); + + if (@$gids) { + $self->{view_groups} = Bugzilla::Group->new_from_list($gids); + } + } + return $self->{view_groups}; +} + +sub add_view_group { + my ($self, $group) = @_; + + $group = get_group($group); + + return unless ($self->user_can_edit); + + return + if ( + $self->view_groups && any { $_->id == $group->id } + @{$self->view_groups} + ); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "INSERT INTO rh_rule_group_view_groups (rule_group_id, group_id) VALUES (?, ?)", + undef, + ($self->id, $group->id) + ); + + return; +} + +sub remove_view_group { + my ($self, $group) = @_; + $group = get_group($group); + + return unless ($self->user_can_edit); + return if (!view_groups || none { $_->id == $group->id } @{$self->view_groups}); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "DELETE FROM rh_rule_group_view_groups WHERE rule_group_id = ? AND group_id = ?", + undef, + ($self->id, $group->id) + ); + + return; +} + +sub _set_groups { + my ($self, $type, $groups) = @_; + + my $old_key = "_old_${type}_groups"; + my $new_key = "${type}_groups"; + + unless (defined($self->{$old_key})) { + my $method = "${type}_groups"; + my $rc = $self->$method; + $self->{$old_key} = $self->$method // []; + } + + $self->{$new_key} = undef; + + my $dbh = Bugzilla->dbh; + + $dbh->do("DELETE FROM rh_rule_group_${type}_groups WHERE rule_group_id = ?", + undef, ($self->id)); + + my $add_method = "add_${type}_group"; + foreach my $grp (@$groups) { + $self->$add_method($grp); + } + + $self->{$new_key} = $groups; + + return; +} + +sub set_view_groups { + my ($self, $groups) = @_; + + $groups //= []; + + $self->_set_groups('view', $groups); + + return; +} + +sub set_edit_groups { + my ($self, $groups) = @_; + + $groups //= []; + + $self->_set_groups('edit', $groups); + + return; +} + +sub edit_groups { + my $self = shift; + + return [] unless $self->id; + if (!defined $self->{edit_groups}) { + my $gids = Bugzilla->dbh->selectcol_arrayref( + "SELECT group_id FROM rh_rule_group_edit_groups + WHERE rule_group_id = ?", undef, $self->id + ); + + if (@$gids) { + $self->{edit_groups} = Bugzilla::Group->new_from_list($gids); + } + } + return $self->{edit_groups}; +} + +sub add_edit_group { + my ($self, $group) = @_; + $group = get_group($group); + + return unless ($self->user_can_edit); + + return + if ( + $self->edit_groups && any { $_->id == $group->id } + @{$self->edit_groups} + ); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "INSERT INTO rh_rule_group_edit_groups (rule_group_id, group_id) VALUES (?, ?)", + undef, + ($self->id, $group->id) + ); + + return; +} + +sub remove_edit_group { + my ($self, $group) = @_; + $group = get_group($group); + + return unless ($self->user_can_edit); + return + if (!$self->edit_groups + || none { $_->id == $group->id } @{$self->edit_groups}); + + my $dbh = Bugzilla->dbh; + $dbh->do( + "DELETE FROM rh_rule_group_edit_groups WHERE rule_group_id = ? AND group_id = ?", + undef, + ($self->id, $group->id) + ); + + return; +} + +sub user_can_view { + my ($self, $user) = @_; + + $user //= Bugzilla->user; + $user = get_user($user); + + return 0 unless $user->id; + + $self->{user_can_view} = {} unless defined $self->{user_can_view}; + + if (!defined $self->{user_can_view}->{$user->id}) { + my $can_view = 0; + if ($self->user_can_edit($user)) { + $can_view = 1; + } + elsif ( + $self->view_groups && any { $user->in_group_id($_->id) } + @{$self->view_groups} + ) + { + $can_view = 1; + } + $self->{user_can_view}->{$user->id} = $can_view; + } + return $self->{user_can_view}->{$user->id}; +} + +sub user_can_edit { + my ($self, $user) = @_; + + $user //= Bugzilla->user; + $user = get_user($user); + + return 0 unless $user->id; + + $self->{user_can_edit} = {} unless defined $self->{user_can_edit}; + + if (!defined $self->{user_can_edit}->{$user->id}) { + my $can_edit = 0; + if ($user->in_group("admin")) { + $can_edit = 1; + } + elsif ( + $self->edit_groups && any { $user->in_group_id($_->id) } + @{$self->edit_groups} + ) + { + $can_edit = 1; + } + + $self->{user_can_edit}->{$user->id} = $can_edit; + } + + return $self->{user_can_edit}->{$user->id}; +} + +sub products { + my ($self) = @_; + + my $products = []; + + return $products unless $self->id; + + my $ids + = Bugzilla->dbh->selectcol_arrayref( + "SELECT id FROM products WHERE rule_group = ?", + undef, $self->id); + + if (@$ids) { + $products = Bugzilla::Product->new_from_list($ids); + } + return $products; +} + +sub classifications { + my ($self) = @_; + + my $classifications = []; + return $classifications unless $self->id; + + my $ids + = Bugzilla->dbh->selectcol_arrayref( + "SELECT DISTINCT classification_id FROM products WHERE rule_group = ?", + undef, $self->id); + + if (@$ids) { + $classifications = Bugzilla::Classification->new_from_list($ids); + } + + return $classifications; +} + +sub all_user_can_edit { + my ($invocant, $user) = @_; + + $user //= Bugzilla->user; + my @rule_groups; + + foreach my $rg (Bugzilla::Extension::RuleEngine::RuleGroup->get_all()) { + push(@rule_groups, $rg) if ($rg->user_can_edit($user)); + } + + return (@rule_groups); +} + +sub all_user_can_view { + my ($invocant, $user) = @_; + + $user //= Bugzilla->user; + my @rule_groups = (); + + foreach my $rg (Bugzilla::Extension::RuleEngine::RuleGroup->get_all()) { + push(@rule_groups, $rg) + if ($rg->user_can_edit($user) || $rg->user_can_view($user)); + } + + return (@rule_groups); +} + +## RED HAT EXTENSION END 1291476 + +sub disabled_count { + my ($self) = @_; + + return (Bugzilla::Extension::RuleEngine::RuleState->disabled_count($self->id)); +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Extension::RuleEngine::RuleGroup - Bugzilla Rule Group class. + +=head1 SYNOPSIS + + use Bugzilla::Extension::RuleEngine::RuleGroup; + + my $rule_group = new Bugzilla::Extension::RuleEngine::RuleGroup(1); + my $rule_group = new Bugzilla::Extension::RuleEngine::RuleGroup({name => 'Acme'}); + + my $id = $rule_group->id; + my $name = $rule_group->name; + my $description = $rule_group->description; + my $is_active = $rule_group->is_active; + +=head1 DESCRIPTION + +Bugzilla::Extension::RuleEngine::RuleGroup represents a rule group object. It is an +implementation of L<Bugzilla::Object>, and thus provides all methods +that L<Bugzilla::Object> provides. + +The methods that are specific to C<Bugzilla::Extension::RuleEngine::RuleGroup> are listed +below. + +A Rule Group is a higher-level grouping of Rule (from the Bugzilla Rules Engine). + +=head1 METHODS + +=over + +=item C<rule_count> + +=over + +=item B<Description> + +Returns the total number of rules that belong to the rule group. + +=item B<Params> + +none. + +=item B<Returns> + +Integer - The total of rules inside the rule group. + +=back + +=item C<sort_rules_by_group> + +=over + +=item B<Description> + +This is a helper which returns a list of rules sorted +by rule group in a form suitable to be passed to the +main rules page template. + +=item B<Params> + +An arrayref of rule objects. + +=item B<Returns> + +An arrayref of hashes suitable to be passed to template. + +Each arrayref has two keys, object is the +Bugzilla::Extension::RuleEngine::RuleGroup object, and +rules are the list of rules (Bugzilla::Extension::RuleEngine::Rule +object) + +=back + +=item C<user_can_edit> + +=over + +=item B<Description> + +can the specified user edit this RuleGroup? + +=item B<Params> + +=over + +=item C<$user> (optional) L<Bugzilla::User> object. + +Current logged in user is used if not given. + +=back + +=item B<Returns> + +Boolean, true if user is allowed to edit the team. + +=back + +=item C<classifications> + +=over + +=item B<Description> + +What classifications are using this BRE group? + +=item B<Params> + +None. + +=item B<Returns> + +An arrayref of L<Bugzilla::Classification> objects. + +=back + +=item C<remove_edit_group> + +=over + +=item B<Description> + +Removes a edit group from the team. + +=item B<Params> + +=over + +=item C<$group> Group object, name or id + +=back + +=item B<Returns> + +Nothing. + +=back + +=item C<add_edit_group> + +=over + +=item B<Description> + +Adds a new edit_group to the team. + +=item B<Params> + +=over + +=item C<$group> Group object, name or id + +=back + +=item B<Returns> + +Nothing. + +=back + +=item C<edit_groups> + +=over + +=item B<Description> + + +List all edit groups for thie RuleGroup. + +=item B<Params> + +None. + +=item B<Returns> + +Array ref of L<Bugzilla::Group> objects containing all edit groups. + +=back + +=item C<view_groups> + +=over + +=item B<Description> + +List view groups for this RuleGroup. + +=item B<Params> + +None. + +=item B<Returns> + +An array ref of L<Bugzilla::Group> objects. + +=back + +=item C<add_view_group> + +=over + +=item B<Description> + +Adds a new view_group to the team. + +=item B<Params> + +=over + +=item C<$group> Group object, name or id + +=back + +=item B<Returns> + +Nothing. + +=back + +=item C<remove_view_group> + +=over + +=item B<Description> + +Removes a view group from the team. + +=item B<Params> + +=over + +=item C<$group> Group object, name or id + +=back + +=item B<Returns> + +Nothing. + +=back + +=item C<get_user> + +=over + +=item B<Description> + +Gets user object or throws error if user is not found. Without +parameters returns the logged in user + +=item B<Params> + +$user -> User ID or login name + +=item B<Returns> + +L<Bugzilla::User> object + +=back + +=item C<get_group> + +=over + +=item B<Description> + +Gets group object or throws error if group is not found. + +=item B<Params> + +$group -> Group ID or name + +=item B<Returns> + +L<Bugzilla::Group> object + +=back + +=item C<products> + +=over + +=item B<Description> + +What products are using this BRE group? + +=item B<Params> + +None. + +=item B<Returns> + +An array ref of L<Bugzilla::Product> objects. + +=back + +=item C<user_can_view> + +=over + +=item B<Description> + +Can the user view the RuleGroup and it's Rules? + +=item B<Params> + +=over + +=item C<$user> (optional) L<Bugzilla::User> object. + +Current logged in user is used if not given. + +=back + +=item B<Returns> + +Boolean, true if user can view RuleGroup. + +=back + +=back + +=cut |