diff options
Diffstat (limited to 'Bugzilla/Field/ChoiceInterface.pm')
-rw-r--r-- | Bugzilla/Field/ChoiceInterface.pm | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/Bugzilla/Field/ChoiceInterface.pm b/Bugzilla/Field/ChoiceInterface.pm new file mode 100644 index 0000000..b2f1bff --- /dev/null +++ b/Bugzilla/Field/ChoiceInterface.pm @@ -0,0 +1,271 @@ +# 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 strict; + +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 |