# 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::Bug;
use 5.10.1;
use strict;
use warnings;
use Bugzilla::Attachment;
use Bugzilla::Constants;
use Bugzilla::Field;
use Bugzilla::Flag;
use Bugzilla::FlagType;
use Bugzilla::Hook;
use Bugzilla::Keyword;
use Bugzilla::Milestone;
use Bugzilla::User;
use Bugzilla::Util;
use Bugzilla::Version;
use Bugzilla::Error;
use Bugzilla::Product;
use Bugzilla::Component;
use Bugzilla::Group;
use Bugzilla::Status;
use Bugzilla::Comment;
use Bugzilla::BugUrl;
use Bugzilla::BugUserLastVisit;
use List::MoreUtils qw(firstidx uniq part);
use List::Util qw(min max first);
use Storable qw(dclone);
use Scalar::Util qw(blessed);
use parent qw(Bugzilla::Object Exporter);
@Bugzilla::Bug::EXPORT = qw(
bug_alias_to_id
LogActivityEntry
editable_bug_fields
);
#####################################################################
# Constants
#####################################################################
use constant DB_TABLE => 'bugs';
use constant ID_FIELD => 'bug_id';
use constant NAME_FIELD => 'bug_id';
use constant LIST_ORDER => ID_FIELD;
# Bugs have their own auditing table, bugs_activity.
use constant AUDIT_CREATES => 0;
use constant AUDIT_UPDATES => 0;
# This will be enabled later
use constant USE_MEMCACHED => 0;
# This is a sub because it needs to call other subroutines.
sub DB_COLUMNS {
my $dbh = Bugzilla->dbh;
my @custom = grep {$_->type != FIELD_TYPE_MULTI_SELECT}
Bugzilla->active_custom_fields;
my @custom_names = map {$_->name} @custom;
my @columns = (qw(
assigned_to
bug_file_loc
bug_id
bug_severity
bug_status
cclist_accessible
component_id
creation_ts
delta_ts
estimated_time
everconfirmed
lastdiffed
op_sys
priority
product_id
qa_contact
remaining_time
rep_platform
reporter_accessible
resolution
short_desc
status_whiteboard
target_milestone
version
),
'reporter AS reporter_id',
$dbh->sql_date_format('deadline', '%Y-%m-%d') . ' AS deadline',
@custom_names);
Bugzilla::Hook::process("bug_columns", { columns => \@columns });
return @columns;
}
sub VALIDATORS {
my $validators = {
alias => \&_check_alias,
assigned_to => \&_check_assigned_to,
blocked => \&_check_dependencies,
bug_file_loc => \&_check_bug_file_loc,
bug_severity => \&_check_select_field,
bug_status => \&_check_bug_status,
cc => \&_check_cc,
comment => \&_check_comment,
component => \&_check_component,
creation_ts => \&_check_creation_ts,
deadline => \&_check_deadline,
dependson => \&_check_dependencies,
dup_id => \&_check_dup_id,
estimated_time => \&_check_time_field,
everconfirmed => \&Bugzilla::Object::check_boolean,
groups => \&_check_groups,
keywords => \&_check_keywords,
op_sys => \&_check_select_field,
priority => \&_check_priority,
product => \&_check_product,
qa_contact => \&_check_qa_contact,
remaining_time => \&_check_time_field,
rep_platform => \&_check_select_field,
resolution => \&_check_resolution,
short_desc => \&_check_short_desc,
status_whiteboard => \&_check_status_whiteboard,
target_milestone => \&_check_target_milestone,
version => \&_check_version,
cclist_accessible => \&Bugzilla::Object::check_boolean,
reporter_accessible => \&Bugzilla::Object::check_boolean,
};
# Set up validators for custom fields.
foreach my $field (Bugzilla->active_custom_fields) {
my $validator;
if ($field->type == FIELD_TYPE_SINGLE_SELECT) {
$validator = \&_check_select_field;
}
elsif ($field->type == FIELD_TYPE_MULTI_SELECT) {
$validator = \&_check_multi_select_field;
}
elsif ($field->type == FIELD_TYPE_DATETIME) {
$validator = \&_check_datetime_field;
}
elsif ($field->type == FIELD_TYPE_DATE) {
$validator = \&_check_date_field;
}
elsif ($field->type == FIELD_TYPE_FREETEXT) {
$validator = \&_check_freetext_field;
}
elsif ($field->type == FIELD_TYPE_BUG_ID) {
$validator = \&_check_bugid_field;
}
elsif ($field->type == FIELD_TYPE_TEXTAREA) {
$validator = \&_check_textarea_field;
}
elsif ($field->type == FIELD_TYPE_INTEGER) {
$validator = \&_check_integer_field;
}
else {
$validator = \&_check_default_field;
}
$validators->{$field->name} = $validator;
}
return $validators;
};
sub VALIDATOR_DEPENDENCIES {
my $cache = Bugzilla->request_cache;
return $cache->{bug_validator_dependencies}
if $cache->{bug_validator_dependencies};
my %deps = (
assigned_to => ['component'],
blocked => ['product'],
bug_status => ['product', 'comment', 'target_milestone'],
cc => ['component'],
comment => ['creation_ts'],
component => ['product'],
dependson => ['product'],
dup_id => ['bug_status', 'resolution'],
groups => ['product'],
keywords => ['product'],
resolution => ['bug_status', 'dependson'],
qa_contact => ['component'],
target_milestone => ['product'],
version => ['product'],
);
foreach my $field (@{ Bugzilla->fields }) {
$deps{$field->name} = [ $field->visibility_field->name ]
if $field->{visibility_field_id};
}
$cache->{bug_validator_dependencies} = \%deps;
return \%deps;
};
sub UPDATE_COLUMNS {
my @custom = grep {$_->type != FIELD_TYPE_MULTI_SELECT}
Bugzilla->active_custom_fields;
my @custom_names = map {$_->name} @custom;
my @columns = qw(
assigned_to
bug_file_loc
bug_severity
bug_status
cclist_accessible
component_id
deadline
estimated_time
everconfirmed
op_sys
priority
product_id
qa_contact
remaining_time
rep_platform
reporter_accessible
resolution
short_desc
status_whiteboard
target_milestone
version
);
push(@columns, @custom_names);
return @columns;
};
use constant NUMERIC_COLUMNS => qw(
estimated_time
remaining_time
);
sub DATE_COLUMNS {
my @fields = (@{ Bugzilla->fields({ type => [FIELD_TYPE_DATETIME,
FIELD_TYPE_DATE] })
});
return map { $_->name } @fields;
}
# Used in LogActivityEntry(). Gives the max length of lines in the
# activity table.
use constant MAX_LINE_LENGTH => 254;
# This maps the names of internal Bugzilla bug fields to things that would
# make sense to somebody who's not intimately familiar with the inner workings
# of Bugzilla. (These are the field names that the WebService and email_in.pl
# use.)
use constant FIELD_MAP => {
blocks => 'blocked',
commentprivacy => 'comment_is_private',
creation_time => 'creation_ts',
creator => 'reporter',
description => 'comment',
depends_on => 'dependson',
dupe_of => 'dup_id',
id => 'bug_id',
is_confirmed => 'everconfirmed',
is_cc_accessible => 'cclist_accessible',
is_creator_accessible => 'reporter_accessible',
last_change_time => 'delta_ts',
platform => 'rep_platform',
severity => 'bug_severity',
status => 'bug_status',
summary => 'short_desc',
url => 'bug_file_loc',
whiteboard => 'status_whiteboard',
};
use constant REQUIRED_FIELD_MAP => {
product_id => 'product',
component_id => 'component',
};
# Creation timestamp is here because it needs to be validated
# but it can be NULL in the database (see comments in create above)
#
# Target Milestone is here because it has a default that the validator
# creates (product.defaultmilestone) that is different from the database
# default.
#
# CC is here because it is a separate table, and has a validator-created
# default of the component initialcc.
#
# QA Contact is allowed to be NULL in the database, so it wouldn't normally
# be caught by _required_create_fields. However, it always has to be validated,
# because it has a default of the component.defaultqacontact.
#
# Groups are in a separate table, but must always be validated so that
# mandatory groups get set on bugs.
|