diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 9a84fb4467..7e382a1463 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -2700,7 +2700,9 @@ sub set_all { # And set custom fields. my @custom_fields - = grep { $_->type != FIELD_TYPE_EXTENSION } Bugzilla->active_custom_fields; + = grep { $_->type != FIELD_TYPE_EXTENSION } Bugzilla->active_custom_fields( + {product => $self->product_obj, component => $self->component_obj} + ); foreach my $field (@custom_fields) { my $fname = $field->name; if (exists $params->{$fname}) { diff --git a/Bugzilla/WebService/Bug.pm b/Bugzilla/WebService/Bug.pm index 812a117ccd..fe5dda2b72 100644 --- a/Bugzilla/WebService/Bug.pm +++ b/Bugzilla/WebService/Bug.pm @@ -1731,20 +1731,26 @@ sub _bug_to_hash { foreach my $field (@custom_fields) { my $name = $field->name; next if !filter_wants($params, $name, ['default', 'custom']); - if ($field->type == FIELD_TYPE_BUG_ID) { - $item{$name} = $self->type('int', $bug->$name); - } - elsif ($field->type == FIELD_TYPE_DATETIME || $field->type == FIELD_TYPE_DATE) { - my $value = $bug->$name; - $item{$name} = defined($value) ? $self->type('dateTime', $value) : undef; - } - elsif ($field->type == FIELD_TYPE_MULTI_SELECT) { - my @values = map { $self->type('string', $_) } @{$bug->$name}; - $item{$name} = \@values; - } - else { - $item{$name} = $self->type('string', $bug->$name); - } + $item{$name} = $self->_format_cf_value($field, $bug->$name); + } + + # Include stored values for CFs not enabled for this product/component, + # so callers aren't silently missing data when a bug retains a value after + # its product/component changed. Multi-select CFs are excluded: they are + # not preloaded by DB_COLUMNS and each accessor fires a separate SELECT, + # making them too expensive to include for hidden fields across many bugs. + my %seen_cf = map { $_->name => 1 } @custom_fields; + my @hidden_cfs = grep { + !$seen_cf{$_->name} + && $_->type != FIELD_TYPE_EXTENSION + && $_->type != FIELD_TYPE_MULTI_SELECT + } Bugzilla->active_custom_fields({skip_extensions => 1}); + foreach my $field (@hidden_cfs) { + my $name = $field->name; + next if !filter_wants($params, $name, ['default', 'custom']); + my $raw = $bug->$name; + next if !defined($raw) || $raw eq ''; + $item{$name} = $self->_format_cf_value($field, $raw); } # Timetracking fields are only sent if the user can see them. @@ -1800,6 +1806,22 @@ sub _bug_to_hash { return \%item; } +sub _format_cf_value { + my ($self, $field, $value) = @_; + if ($field->type == FIELD_TYPE_BUG_ID) { + return $self->type('int', $value); + } + elsif ($field->type == FIELD_TYPE_DATETIME || $field->type == FIELD_TYPE_DATE) { + return defined($value) ? $self->type('dateTime', $value) : undef; + } + elsif ($field->type == FIELD_TYPE_MULTI_SELECT) { + return [map { $self->type('string', $_) } @{$value}]; + } + else { + return $self->type('string', $value); + } +} + sub _user_to_hash { my ($self, $user, $filters, $types, $prefix) = @_; my $item = filter $filters, diff --git a/extensions/BMO/lib/Data.pm b/extensions/BMO/lib/Data.pm index 2e2e8a893e..adf8b28afe 100644 --- a/extensions/BMO/lib/Data.pm +++ b/extensions/BMO/lib/Data.pm @@ -149,6 +149,7 @@ tie( "Testing" => [], "Thunderbird" => [], "Toolkit" => [], + "Web Compatibility" => [], "WebExtensions" => [], }, qr/^cf_due_date$/ => {