aboutsummaryrefslogtreecommitdiff
path: root/Bugzilla/Field/ChoiceInterface.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla/Field/ChoiceInterface.pm')
-rw-r--r--Bugzilla/Field/ChoiceInterface.pm271
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