From a806b298f5bfe5914f27a1419d27366fe59da449 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Sat, 9 Sep 2006 06:11:40 +0000 Subject: Bug 287326: Ability to add custom single-select fields to a bug - Patch by Frédéric Buclin and Max Kanat-Alexander r=mkanat a=myk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/Bug.pm | 46 +++++++++++++++++++--------- Bugzilla/Constants.pm | 2 ++ Bugzilla/DB.pm | 17 ++++++++++- Bugzilla/DB/Schema.pm | 17 +++++++++++ Bugzilla/Field.pm | 84 ++++++++++++++++++++++++++++++++++++++++++--------- Bugzilla/Object.pm | 8 +++-- Bugzilla/Search.pm | 38 ++++++++++++++++------- 7 files changed, 169 insertions(+), 43 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 03a28bf5d..6e8079d27 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -110,20 +110,30 @@ use constant REQUIRED_CREATE_FIELDS => qw( # There are also other, more complex validators that are called # from run_create_validators. -use constant VALIDATORS => { - alias => \&_check_alias, - bug_file_loc => \&_check_bug_file_loc, - bug_severity => \&_check_bug_severity, - cc => \&_check_cc, - deadline => \&_check_deadline, - estimated_time => \&_check_estimated_time, - op_sys => \&_check_op_sys, - priority => \&_check_priority, - product => \&_check_product, - remaining_time => \&_check_remaining_time, - rep_platform => \&_check_rep_platform, - short_desc => \&_check_short_desc, - status_whiteboard => \&_check_status_whiteboard, +sub VALIDATORS { + my $validators = { + alias => \&_check_alias, + bug_file_loc => \&_check_bug_file_loc, + bug_severity => \&_check_bug_severity, + cc => \&_check_cc, + deadline => \&_check_deadline, + estimated_time => \&_check_estimated_time, + op_sys => \&_check_op_sys, + priority => \&_check_priority, + product => \&_check_product, + remaining_time => \&_check_remaining_time, + rep_platform => \&_check_rep_platform, + short_desc => \&_check_short_desc, + status_whiteboard => \&_check_status_whiteboard, + }; + + my @select_fields = Bugzilla->get_fields({custom => 1, obsolete => 0, + type => FIELD_TYPE_SINGLE_SELECT}); + + foreach my $field (@select_fields) { + $validators->{$field->name} = \&_check_select_field; + } + return $validators; }; # Used in LogActivityEntry(). Gives the max length of lines in the @@ -279,7 +289,7 @@ sub run_create_validators { $params->{remaining_time} = $params->{estimated_time}; $class->_check_strict_isolation($product, $params->{cc}, - $params->{assigned_to}, $params->{qa_contact}); + $params->{assigned_to}, $params->{qa_contact}); return $params; } @@ -657,6 +667,12 @@ sub _check_version { return $version; } +sub _check_select_field { + my ($invocant, $value, $field) = @_; + $value = trim($value); + check_field($field, $value); + return $value; +} ##################################################################### # Class Accessors diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index a0e869c33..9559dcae3 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -106,6 +106,7 @@ use File::Basename; FIELD_TYPE_UNKNOWN FIELD_TYPE_FREETEXT + FIELD_TYPE_SINGLE_SELECT BUG_STATE_OPEN @@ -296,6 +297,7 @@ use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe'; use constant FIELD_TYPE_UNKNOWN => 0; use constant FIELD_TYPE_FREETEXT => 1; +use constant FIELD_TYPE_SINGLE_SELECT => 2; # The maximum number of days a token will remain valid. use constant MAX_TOKEN_AGE => 3; diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 077f93cf7..5fceb961d 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -577,10 +577,25 @@ sub bz_add_table { sub _bz_add_table_raw { my ($self, $name) = @_; my @statements = $self->_bz_schema->get_table_ddl($name); - print "Adding new table $name ...\n"; + print "Adding new table $name ...\n" unless i_am_cgi(); $self->do($_) foreach (@statements); } +sub bz_add_field_table { + my ($self, $name) = @_; + my $table_schema = $self->_bz_schema->FIELD_TABLE_SCHEMA; + my $indexes = $table_schema->{INDEXES}; + # $indexes is an arrayref, not a hash. In order to fix the keys, + # we have to fix every other item. + for (my $i = 0; $i < scalar @$indexes; $i++) { + next if ($i % 2 && $i != 0); # We skip 1, 3, 5, 7, etc. + $indexes->[$i] = $name . "_" . $indexes->[$i]; + } + # We add this to the abstract schema so that bz_add_table can find it. + $self->_bz_schema->add_table($name, $table_schema); + $self->bz_add_table($name); +} + sub bz_drop_column { my ($self, $table, $column) = @_; diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 4c270e68c..384fb478b 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -1090,6 +1090,23 @@ use constant ABSTRACT_SCHEMA => { }, }; + +use constant FIELD_TABLE_SCHEMA => { + FIELDS => [ + id => {TYPE => 'SMALLSERIAL', NOTNULL => 1, + PRIMARYKEY => 1}, + value => {TYPE => 'varchar(64)', NOTNULL => 1}, + sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0}, + isactive => {TYPE => 'BOOLEAN', NOTNULL => 1, + DEFAULT => 'TRUE'}, + ], + # Note that bz_add_field_table should prepend the table name + # to these index names. + INDEXES => [ + value_idx => {FIELDS => ['value'], TYPE => 'UNIQUE'}, + sortkey_idx => ['sortkey', 'value'], + ], +}; #-------------------------------------------------------------------------- =head1 METHODS diff --git a/Bugzilla/Field.pm b/Bugzilla/Field.pm index 870e93221..2dfd8aa6e 100644 --- a/Bugzilla/Field.pm +++ b/Bugzilla/Field.pm @@ -103,7 +103,9 @@ use constant DB_COLUMNS => ( use constant SQL_DEFINITIONS => { # Using commas because these are constants and they shouldn't # be auto-quoted by the "=>" operator. - FIELD_TYPE_FREETEXT, { TYPE => 'varchar(255)' }, + FIELD_TYPE_FREETEXT, { TYPE => 'varchar(255)' }, + FIELD_TYPE_SINGLE_SELECT, { TYPE => 'varchar(64)', NOTNULL => 1, + DEFAULT => "'---'" }, }; # Field definitions for the fields that ship with Bugzilla. @@ -266,6 +268,24 @@ enter_bug.cgi sub enter_bug { return $_[0]->{enter_bug} } +=over + +=item C + +A reference to an array with valid active values for this field. + +=back + +=cut + +sub legal_values { + my $self = shift; + + if (!defined $self->{'legal_values'}) { + $self->{'legal_values'} = get_legal_field_values($self->name); + } + return $self->{'legal_values'}; +} =pod @@ -305,9 +325,7 @@ sub create_or_update { my $name = $params->{name}; my $custom = $params->{custom} ? 1 : 0; my $in_new_bugmail = $params->{in_new_bugmail} ? 1 : 0; - # Some day we'll allow invocants to specify the field type. - # We don't care about $params->{type} yet. - my $type = $custom ? FIELD_TYPE_FREETEXT : FIELD_TYPE_UNKNOWN; + my $type = $params->{type} || FIELD_TYPE_UNKNOWN; my $field = new Bugzilla::Field({name => $name}); if ($field) { @@ -353,6 +371,15 @@ sub create_or_update { $dbh->bz_add_column('bugs', $name, SQL_DEFINITIONS->{$type}); } + if ($custom && !$dbh->bz_table_info($name) + && $type eq FIELD_TYPE_SINGLE_SELECT) + { + # Create the table that holds the legal values for this field. + $dbh->bz_add_field_table($name); + # And insert a default value of "---" into it. + $dbh->do("INSERT INTO $name (value) VALUES ('---')"); + } + return new Bugzilla::Field({name => $name}); } @@ -361,19 +388,45 @@ sub create_or_update { =over -=item C +=item C + +=over + +=item B -Description: returns a list of fields that match the specified criteria. +Returns a list of fields that match the specified criteria. -Params: C<$criteria> - hash reference - the criteria to match against. - Hash keys represent field properties; hash values represent - their values. All criteria are optional. Valid criteria are - "custom" and "obsolete", and both take boolean values. +You should be using L and +L instead of this function. - Note: Bugzilla->get_fields() and Bugzilla->custom_field_names - wrap this method for most callers. +=item B -Returns: A reference to an array of C objects. +Takes named parameters in a hashref: + +=over + +=item C - The name of the field. + +=item C - Boolean. True to only return custom fields. False +to only return non-custom fields. + +=item C - Boolean. True to only return obsolete fields. +False to not return obsolete fields. + +=item C - The type of the field. A C constant from +L. + +=item C - Boolean. True to only return fields that appear +on F. False to only return fields that I appear +on F. + +=back + +=item B + +A reference to an array of C objects. + +=back =back @@ -395,8 +448,11 @@ sub match { if (defined $criteria->{enter_bug}) { push(@terms, "enter_bug=" . ($criteria->{enter_bug} ? '1' : '0')); } + if (defined $criteria->{type}) { + push(@terms, "type = " . $criteria->{type}); + } my $where = (scalar(@terms) > 0) ? "WHERE " . join(" AND ", @terms) : ""; - + my $ids = Bugzilla->dbh->selectcol_arrayref( "SELECT id FROM fielddefs $where", {Slice => {}}); diff --git a/Bugzilla/Object.pm b/Bugzilla/Object.pm index 8de20d5f3..5d80a9d0f 100644 --- a/Bugzilla/Object.pm +++ b/Bugzilla/Object.pm @@ -17,6 +17,7 @@ # Everything Solved. All Rights Reserved. # # Contributor(s): Max Kanat-Alexander +# Frédéric Buclin use strict; @@ -136,7 +137,7 @@ sub set { my $validators = $self->VALIDATORS; if (exists $validators->{$field}) { my $validator = $validators->{$field}; - $value = $self->$validator($value); + $value = $self->$validator($value, $field); } $self->{$field} = $value; @@ -196,7 +197,7 @@ sub run_create_validators { my $value; if (exists $validators->{$field}) { my $validator = $validators->{$field}; - $value = $class->$validator($params->{$field}); + $value = $class->$validator($params->{$field}, $field); } else { $value = $params->{$field}; @@ -328,6 +329,9 @@ a reference to the current object (what we normally call C<$self>). The second argument will be the value passed to L or Lfor that field. +The third argument will be the name of the field being validated. +This may be required by validators which validate several distinct fields. + These functions should call L if they fail. The validator must return the validated value. diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index d2d3b4978..0d72b5873 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -55,7 +55,7 @@ use constant SUMMARY_RELEVANCE_FACTOR => 7; # We need to have a list of these fields and what they map to. # Each field points to an array that contains the fields mapped # to, in order. -our %specialorder = ( +use constant SPECIAL_ORDER => { 'bugs.target_milestone' => [ 'ms_order.sortkey','ms_order.value' ], 'bugs.bug_status' => [ 'bug_status.sortkey','bug_status.value' ], 'bugs.rep_platform' => [ 'rep_platform.sortkey','rep_platform.value' ], @@ -63,12 +63,12 @@ our %specialorder = ( 'bugs.op_sys' => [ 'op_sys.sortkey','op_sys.value' ], 'bugs.resolution' => [ 'resolution.sortkey', 'resolution.value' ], 'bugs.bug_severity' => [ 'bug_severity.sortkey','bug_severity.value' ] -); +}; # When we add certain fields to the ORDER BY, we need to then add a # table join to the FROM statement. This hash maps input fields to # the join statements that need to be added. -our %specialorderjoin = ( +use constant SPECIAL_ORDER_JOIN => { 'bugs.target_milestone' => 'LEFT JOIN milestones AS ms_order ON ms_order.value = bugs.target_milestone AND ms_order.product_id = bugs.product_id', 'bugs.bug_status' => 'LEFT JOIN bug_status ON bug_status.value = bugs.bug_status', 'bugs.rep_platform' => 'LEFT JOIN rep_platform ON rep_platform.value = bugs.rep_platform', @@ -76,7 +76,7 @@ our %specialorderjoin = ( 'bugs.op_sys' => 'LEFT JOIN op_sys ON op_sys.value = bugs.op_sys', 'bugs.resolution' => 'LEFT JOIN resolution ON resolution.value = bugs.resolution', 'bugs.bug_severity' => 'LEFT JOIN bug_severity ON bug_severity.value = bugs.bug_severity' -); +}; # Create a new Search # Note that the param argument may be modified by Bugzilla::Search @@ -117,6 +117,18 @@ sub init { my @andlist; my %chartfields; + my %special_order = %{SPECIAL_ORDER()}; + my %special_order_join = %{SPECIAL_ORDER_JOIN()}; + + my @select_fields = Bugzilla->get_fields({ type => FIELD_TYPE_SINGLE_SELECT, + obsolete => 0 }); + foreach my $field (@select_fields) { + my $name = $field->name; + $special_order{"bugs.$name"} = [ "$name.sortkey", "$name.value" ], + $special_order_join{"bugs.$name"} = + "LEFT JOIN $name ON $name.value = bugs.$name"; + } + my $dbh = Bugzilla->dbh; # First, deal with all the old hard-coded non-chart-based poop. @@ -213,6 +225,9 @@ sub init { "assigned_to", "reporter", "component", "classification", "target_milestone", "bug_group"); + # Include custom select fields. + push(@legal_fields, map { $_->name } @select_fields); + foreach my $field ($params->param()) { if (lsearch(\@legal_fields, $field) != -1) { push(@specialchart, [$field, "anyexact", @@ -1368,7 +1383,7 @@ sub init { if ($orderitem =~ /\s+AS\s+(.+)$/i) { $orderitem = $1; } - BuildOrderBy($orderitem, \@orderby); + BuildOrderBy(\%special_order, $orderitem, \@orderby); } # Now JOIN the correct tables in the FROM clause. # This is done separately from the above because it's @@ -1376,8 +1391,8 @@ sub init { foreach my $orderitem (@inputorder) { # Grab the part without ASC or DESC. my @splitfield = split(/\s+/, $orderitem); - if ($specialorderjoin{$splitfield[0]}) { - push(@supptables, $specialorderjoin{$splitfield[0]}); + if ($special_order_join{$splitfield[0]}) { + push(@supptables, $special_order_join{$splitfield[0]}); } } @@ -1677,7 +1692,7 @@ sub IsValidQueryType # BuildOrderBy recursively, to let it know that we're "reversing" the # order. That is, that we wanted "A DESC", not "A". sub BuildOrderBy { - my ($orderitem, $stringlist, $reverseorder) = (@_); + my ($special_order, $orderitem, $stringlist, $reverseorder) = (@_); my @twopart = split(/\s+/, $orderitem); my $orderfield = $twopart[0]; @@ -1695,11 +1710,12 @@ sub BuildOrderBy { } # Handle fields that have non-standard sort orders, from $specialorder. - if ($specialorder{$orderfield}) { - foreach my $subitem (@{$specialorder{$orderfield}}) { + if ($special_order->{$orderfield}) { + foreach my $subitem (@{$special_order->{$orderfield}}) { # DESC on a field with non-standard sort order means # "reverse the normal order for each field that we map to." - BuildOrderBy($subitem, $stringlist, $orderdirection =~ m/desc/i); + BuildOrderBy($special_order, $subitem, $stringlist, + $orderdirection =~ m/desc/i); } return; } -- cgit v1.2.3-24-g4f1b