| # 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/. |
| # |
| # This Source Code Form is "Incompatible With Secondary Licenses", as |
| # defined by the Mozilla Public License, v. 2.0. |
| |
| package Bugzilla::Field::ChoiceInterface; |
| |
| use 5.10.1; |
| use strict; |
| use warnings; |
| |
| use Bugzilla::Constants; |
| use Bugzilla::Error; |
| use Bugzilla::Field; |
| |
| use Scalar::Util qw(blessed); |
| |
| # Helps implement the "field" accessor without subclasses having to |
| # write code. |
| sub FIELD_NAME { return $_[0]->DB_TABLE; } |
| |
| #################### |
| # Subclass Helpers # |
| #################### |
| |
| sub _check_if_controller { |
| my $self = shift; |
| my $vis_fields = $self->controls_visibility_of_fields; |
| my $values = $self->controlled_values_array; |
| if (@$vis_fields || @$values) { |
| ThrowUserError('fieldvalue_is_controller', |
| { value => $self, fields => [map($_->name, @$vis_fields)], |
| vals => $self->controlled_values }); |
| } |
| } |
| |
| |
| ############# |
| # Accessors # |
| ############# |
| |
| sub is_active { return $_[0]->{'isactive'}; } |
| sub sortkey { return $_[0]->{'sortkey'}; } |
| |
| sub bug_count { |
| my $self = shift; |
| return $self->{bug_count} if defined $self->{bug_count}; |
| my $dbh = Bugzilla->dbh; |
| my $fname = $self->field->name; |
| my $count; |
| if ($self->field->type == FIELD_TYPE_MULTI_SELECT) { |
| $count = $dbh->selectrow_array("SELECT COUNT(*) FROM bug_$fname |
| WHERE value = ?", undef, $self->name); |
| } |
| else { |
| $count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs |
| WHERE $fname = ?", |
| undef, $self->name); |
| } |
| $self->{bug_count} = $count; |
| return $count; |
| } |
| |
| sub field { |
| my $invocant = shift; |
| my $class = ref $invocant || $invocant; |
| my $cache = Bugzilla->request_cache; |
| # This is just to make life easier for subclasses. Our auto-generated |
| # subclasses from Bugzilla::Field::Choice->type() already have this set. |
| $cache->{"field_$class"} ||= |
| new Bugzilla::Field({ name => $class->FIELD_NAME }); |
| return $cache->{"field_$class"}; |
| } |
| |
| sub is_default { |
| my $self = shift; |
| my $name = $self->DEFAULT_MAP->{$self->field->name}; |
| # If it doesn't exist in DEFAULT_MAP, then there is no parameter |
| # related to this field. |
| return 0 unless $name; |
| return ($self->name eq Bugzilla->params->{$name}) ? 1 : 0; |
| } |
| |
| sub is_static { |
| my $self = shift; |
| # If we need to special-case Resolution for *anything* else, it should |
| # get its own subclass. |
| if ($self->field->name eq 'resolution') { |
| return grep($_ eq $self->name, ('', 'FIXED', 'DUPLICATE')) |
| ? 1 : 0; |
| } |
| elsif ($self->field->custom) { |
| return $self->name eq '---' ? 1 : 0; |
| } |
| return 0; |
| } |
| |
| sub controls_visibility_of_fields { |
| my $self = shift; |
| my $dbh = Bugzilla->dbh; |
| |
| if (!$self->{controls_visibility_of_fields}) { |
| my $ids = $dbh->selectcol_arrayref( |
| "SELECT id FROM fielddefs |
| INNER JOIN field_visibility |
| ON fielddefs.id = field_visibility.field_id |
| WHERE value_id = ? AND visibility_field_id = ?", undef, |
| $self->id, $self->field->id); |
| |
| $self->{controls_visibility_of_fields} = |
| Bugzilla::Field->new_from_list($ids); |
| } |
| |
| return $self->{controls_visibility_of_fields}; |
| } |
| |
| sub visibility_value { |
| my $self = shift; |
| if ($self->{visibility_value_id}) { |
| require Bugzilla::Field::Choice; |
| $self->{visibility_value} ||= |
| Bugzilla::Field::Choice->type($self->field->value_field)->new( |
| $self->{visibility_value_id}); |
| } |
| return $self->{visibility_value}; |
| } |
| |
| sub controlled_values { |
| my $self = shift; |
| return $self->{controlled_values} if defined $self->{controlled_values}; |
| my $fields = $self->field->controls_values_of; |
| my %controlled_values; |
| require Bugzilla::Field::Choice; |
| foreach my $field (@$fields) { |
| $controlled_values{$field->name} = |
| Bugzilla::Field::Choice->type($field) |
| ->match({ visibility_value_id => $self->id }); |
| } |
| $self->{controlled_values} = \%controlled_values; |
| return $self->{controlled_values}; |
| } |
| |
| sub controlled_values_array { |
| my ($self) = @_; |
| my $values = $self->controlled_values; |
| return [map { @{ $values->{$_} } } keys %$values]; |
| } |
| |
| sub is_visible_on_bug { |
| my ($self, $bug) = @_; |
| |
| # Values currently set on the bug are always shown. |
| return 1 if $self->is_set_on_bug($bug); |
| |
| # Inactive values are, otherwise, never shown. |
| return 0 if !$self->is_active; |
| |
| # Values without a visibility value are, otherwise, always shown. |
| my $visibility_value = $self->visibility_value; |
| return 1 if !$visibility_value; |
| |
| # Values with a visibility value are only shown if the visibility |
| # value is set on the bug. |
| return $visibility_value->is_set_on_bug($bug); |
| } |
| |
| sub is_set_on_bug { |
| my ($self, $bug) = @_; |
| my $field_name = $self->FIELD_NAME; |
| # This allows bug/create/create.html.tmpl to pass in a hashref that |
| # looks like a bug object. |
| my $value = blessed($bug) ? $bug->$field_name : $bug->{$field_name}; |
| $value = $value->name if blessed($value); |
| return 0 if !defined $value; |
| |
| if ($self->field->type == FIELD_TYPE_BUG_URLS |
| or $self->field->type == FIELD_TYPE_MULTI_SELECT) |
| { |
| return grep($_ eq $self->name, @$value) ? 1 : 0; |
| } |
| return $value eq $self->name ? 1 : 0; |
| } |
| |
| 1; |
| |
| __END__ |
| |
| =head1 NAME |
| |
| Bugzilla::Field::ChoiceInterface - Makes an object act like a |
| Bugzilla::Field::Choice. |
| |
| =head1 DESCRIPTION |
| |
| This is an "interface", in the Java sense (sometimes called a "Role" |
| or a "Mixin" in other languages). L<Bugzilla::Field::Choice> is the |
| primary implementor of this interface, but other classes also implement |
| it if they want to "act like" L<Bugzilla::Field::Choice>. |
| |
| =head1 METHODS |
| |
| =head2 Accessors |
| |
| These are in addition to the standard L<Bugzilla::Object> accessors. |
| |
| =over |
| |
| =item C<sortkey> |
| |
| The key that determines the sort order of this item. |
| |
| =item C<field> |
| |
| The L<Bugzilla::Field> object that this field value belongs to. |
| |
| =item C<is_active> |
| |
| Whether or not this value should appear as an option on bugs that do |
| not already have it set as the current value. |
| |
| =item C<is_static> |
| |
| C<0> if this field value can be renamed or deleted, C<1> otherwise. |
| |
| =item C<is_default> |
| |
| C<1> if this is the default value for this field, C<0> otherwise. |
| |
| =item C<bug_count> |
| |
| An integer count of the number of bugs that have this value set. |
| |
| =item C<controls_visibility_of_fields> |
| |
| Returns an arrayref of L<Bugzilla::Field> objects, representing any |
| fields whose visibility are controlled by this field value. |
| |
| =item C<controlled_values> |
| |
| Tells you which values in B<other> fields appear (become visible) when this |
| value is set in its field. |
| |
| Returns a hashref of arrayrefs. The hash keys are the names of fields, |
| and the values are arrays of objects that implement |
| C<Bugzilla::Field::ChoiceInterface>, representing values that this value |
| controls the visibility of, for that field. |
| |
| =item C<visibility_value> |
| |
| Returns an object that implements C<Bugzilla::Field::ChoiceInterface>, |
| which represents the value that needs to be set in order for this |
| value to appear in the UI. |
| |
| =item C<is_visible_on_bug> |
| |
| Returns C<1> if, according to the settings of C<is_active> and |
| C<visibility_value>, this value should be displayed as an option |
| when viewing a bug. Returns C<0> otherwise. |
| |
| Takes a single argument, a L<Bugzilla::Bug> object or a hash with |
| similar fields to a L<Bugzilla::Bug> object. |
| |
| =item C<is_set_on_bug> |
| |
| Returns C<1> if this value is the current value set for its field on |
| the passed-in L<Bugzilla::Bug> object (or a hash that looks like a |
| L<Bugzilla::Bug>). For multi-valued fields, we return C<1> if |
| I<any> of the currently selected values are this value. |
| |
| Returns C<0> otherwise. |
| |
| =back |
| |
| =head1 B<Methods in need of POD> |
| |
| =over |
| |
| =item FIELD_NAME |
| |
| =item controlled_values_array |
| |
| =back |