# -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public # License Version 1.1 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of # the License at http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or # implied. See the License for the specific language governing # rights and limitations under the License. # # The Initial Developer of the Original Code is NASA. # Portions created by NASA are Copyright (C) 2006 San Jose State # University Foundation. All Rights Reserved. # # The Original Code is the Bugzilla Bug Tracking System. # # Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> # Greg Hendricks <ghendricks@novell.com> 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}; 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