Turn Product into a GenericObject and finally remove Bugzilla::Field::Choice
parent
3daa7b59a1
commit
ba0745839e
|
@ -2125,7 +2125,7 @@ sub _set_product
|
|||
# Check that the product exists and that the user
|
||||
# is allowed to enter bugs into this product.
|
||||
Bugzilla->user->can_enter_product($name, THROW_ERROR);
|
||||
# can_enter_product already does everything that check_product
|
||||
# can_enter_product already does everything that Bugzilla::Product->check
|
||||
# would do for us, so we don't need to use it.
|
||||
my $product = new Bugzilla::Product({ name => $name });
|
||||
if (($self->product_id || 0) != $product->id)
|
||||
|
|
|
@ -28,7 +28,6 @@ use constant OVERRIDE_SETTERS => {
|
|||
};
|
||||
|
||||
use constant is_active => 1;
|
||||
use constant is_default => 0;
|
||||
use constant bug_count => 0;
|
||||
|
||||
sub _set_name
|
||||
|
|
|
@ -619,7 +619,9 @@ sub db_column
|
|||
{
|
||||
my $self = shift;
|
||||
return undef if $self->{type} == FIELD_TYPE_MULTI || $self->{type} == FIELD_TYPE_REVERSE;
|
||||
return $self->{name}.'_id' if $self->class->name eq 'bug' && ($self->{name} eq 'component' || $self->{name} eq 'product');
|
||||
return $self->{name}.'_id' if
|
||||
$self->class->name eq 'bug' && ($self->{name} eq 'component' || $self->{name} eq 'product') ||
|
||||
$self->class->name eq 'product' && $self->{name} eq 'classification';
|
||||
return $self->{name};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,538 +0,0 @@
|
|||
# Class representing single value of a field
|
||||
# Nearly 100% refactored
|
||||
# Author(s): Vitaliy Filippov <vitalif@mail.ru>, Max Kanat-Alexander <mkanat@bugzilla.org>, Greg Hendricks <ghendricks@novell.com>
|
||||
# License: Dual-license GPL 3.0+ or MPL 1.1+
|
||||
|
||||
use strict;
|
||||
|
||||
package Bugzilla::Field::Choice;
|
||||
|
||||
use base qw(Bugzilla::Object);
|
||||
|
||||
use Bugzilla::Config qw(SetParam write_params);
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Util qw(trim detaint_natural trick_taint diff_arrays);
|
||||
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
##################
|
||||
# Initialization #
|
||||
##################
|
||||
|
||||
use constant DB_COLUMNS => qw(
|
||||
id
|
||||
value
|
||||
sortkey
|
||||
isactive
|
||||
);
|
||||
|
||||
use constant UPDATE_COLUMNS => qw(
|
||||
value
|
||||
sortkey
|
||||
isactive
|
||||
);
|
||||
|
||||
use constant NAME_FIELD => 'value';
|
||||
use constant LIST_ORDER => 'sortkey, value';
|
||||
use constant CUSTOM_SORT => undef;
|
||||
|
||||
# Table storing many-to-many relationship for this field
|
||||
sub REL_TABLE { 'bug_'.$_[0]->FIELD_NAME }
|
||||
|
||||
use constant REQUIRED_CREATE_FIELDS => qw(value);
|
||||
|
||||
use constant VALIDATORS => {
|
||||
value => \&_check_value,
|
||||
sortkey => \&_check_sortkey,
|
||||
isactive => \&Bugzilla::Object::check_boolean,
|
||||
};
|
||||
|
||||
use constant CLASS_MAP => {
|
||||
bug_status => 'Bugzilla::Status',
|
||||
product => 'Bugzilla::Product',
|
||||
component => 'Bugzilla::Component',
|
||||
version => 'Bugzilla::Version',
|
||||
target_milestone => 'Bugzilla::Milestone',
|
||||
classification => 'Bugzilla::Classification',
|
||||
op_sys => 'Bugzilla::OS',
|
||||
rep_platform => 'Bugzilla::Platform',
|
||||
keywords => 'Bugzilla::Keyword',
|
||||
};
|
||||
|
||||
#################
|
||||
# Class Factory #
|
||||
#################
|
||||
|
||||
# Bugzilla::Field::Choice is actually an abstract base class. Every field
|
||||
# type has its own dynamically-generated class for its values. This allows
|
||||
# certain fields to have special types, like how bug_status's values
|
||||
# are Bugzilla::Status objects.
|
||||
|
||||
sub type
|
||||
{
|
||||
my ($class, $field) = @_;
|
||||
my $field_obj = blessed $field ? $field : Bugzilla->get_field($field, THROW_ERROR);
|
||||
my $field_name = $field_obj->name;
|
||||
|
||||
my $package;
|
||||
if ($class->CLASS_MAP->{$field_name})
|
||||
{
|
||||
$package = $class->CLASS_MAP->{$field_name};
|
||||
if (!defined *{"${package}::DB_TABLE"})
|
||||
{
|
||||
eval "require $package";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# For generic classes, we use a lowercase class name, so as
|
||||
# not to interfere with any real subclasses we might make some day.
|
||||
$package = "Bugzilla::Field::Choice::$field_name";
|
||||
|
||||
# The package only needs to be created once. We check if the DB_TABLE
|
||||
# glob for this package already exists, which tells us whether or not
|
||||
# we need to create the package (this works even under mod_perl, where
|
||||
# this package definition will persist across requests)).
|
||||
if (!defined *{"${package}::DB_TABLE"})
|
||||
{
|
||||
my $code = "package $package;
|
||||
use base qw(Bugzilla::Field::Choice);
|
||||
use constant DB_TABLE => '$field_name';
|
||||
use constant FIELD_NAME => '$field_name';";
|
||||
eval $code;
|
||||
}
|
||||
}
|
||||
|
||||
return $package;
|
||||
}
|
||||
|
||||
################
|
||||
# Constructors #
|
||||
################
|
||||
|
||||
# We just make new() enforce this, which should give developers
|
||||
# the understanding that you can't use Bugzilla::Field::Choice
|
||||
# without calling type().
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
if ($class eq 'Bugzilla::Field::Choice')
|
||||
{
|
||||
ThrowCodeError('field_choice_must_use_type');
|
||||
}
|
||||
$class->SUPER::new(@_);
|
||||
}
|
||||
|
||||
#########################
|
||||
# Database Manipulation #
|
||||
#########################
|
||||
|
||||
# vitalif@mail.ru 2010-11-11 //
|
||||
# This is incorrect in create() to remove arguments that are not valid DB columns
|
||||
# BEFORE calling run_create_validators etc, as these methods can change
|
||||
# params hash (for example turn Bugzilla::Product to product_id field)
|
||||
|
||||
sub create
|
||||
{
|
||||
my $self = shift;
|
||||
$self = $self->SUPER::create(@_);
|
||||
$self->field->touch;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub update
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $fname = $self->field->name;
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
my ($changes, $old_self) = $self->SUPER::update(@_);
|
||||
|
||||
$self->field->touch;
|
||||
$dbh->bz_commit_transaction();
|
||||
return wantarray ? ($changes, $old_self) : $changes;
|
||||
}
|
||||
|
||||
sub remove_from_db
|
||||
{
|
||||
my $self = shift;
|
||||
if ($self->is_default)
|
||||
{
|
||||
ThrowUserError('fieldvalue_is_default', {
|
||||
field => $self->field,
|
||||
value => $self,
|
||||
});
|
||||
}
|
||||
if ($self->bug_count)
|
||||
{
|
||||
ThrowUserError('fieldvalue_still_has_bugs', {
|
||||
field => $self->field,
|
||||
value => $self,
|
||||
});
|
||||
}
|
||||
# Delete visibility values
|
||||
$self->set_visibility_values(undef);
|
||||
# Delete controlled value records
|
||||
$self->field->update_control_lists($self->id, {});
|
||||
# Delete records about this value used as default
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("DELETE FROM field_defaults WHERE field_id=? AND default_value=?", undef, $self->field->id, $self->id);
|
||||
if ($self->field->type == FIELD_TYPE_MULTI_SELECT)
|
||||
{
|
||||
$dbh->do(
|
||||
"UPDATE field_defaults SET default_value=REPLACE(default_value, ?, ?) WHERE field_id=?".
|
||||
" AND default_value LIKE ?", undef, ','.$self->id.',', ',', $self->field->id, '%,'.$self->id.',%'
|
||||
);
|
||||
$dbh->do(
|
||||
"UPDATE field_defaults SET default_value=SUBSTR(default_value, ?) WHERE field_id=?".
|
||||
" AND default_value LIKE ?", undef, length($self->id)+2, $self->field->id, $self->id.',%'
|
||||
);
|
||||
$dbh->do(
|
||||
"UPDATE field_defaults SET default_value=SUBSTR(default_value, 1, LENGTH(default_value)-?) WHERE field_id=?".
|
||||
" AND default_value LIKE ?", undef, length($self->id)+1, $self->field->id, '%,'.$self->id
|
||||
);
|
||||
}
|
||||
$self->field->touch;
|
||||
$self->SUPER::remove_from_db();
|
||||
}
|
||||
|
||||
# Default implementation of get_all for choice fields
|
||||
# Returns all values (active+inactive), enabled for products that current user can see
|
||||
sub get_all
|
||||
{
|
||||
my $class = shift;
|
||||
my ($include_disabled) = @_;
|
||||
my $rc_cache = Bugzilla->rc_cache_fields;
|
||||
if ($rc_cache->{get_all}->{$class}->{$include_disabled ? 1 : 0})
|
||||
{
|
||||
# Filtered lists are cached for a single request
|
||||
return @{$rc_cache->{get_all}->{$class}->{$include_disabled ? 1 : 0}};
|
||||
}
|
||||
my $f = $class->field;
|
||||
my $all;
|
||||
if (!defined $f->{legal_values})
|
||||
{
|
||||
# Only full unfiltered list of active values is cached between requests
|
||||
$all = [ $class->SUPER::get_all() ];
|
||||
$f->{legal_values} = $all;
|
||||
}
|
||||
else
|
||||
{
|
||||
$all = $f->{legal_values};
|
||||
}
|
||||
if (!$include_disabled)
|
||||
{
|
||||
$all = [ grep { $_->is_active } @$all ];
|
||||
}
|
||||
if (!$f->value_field_id || $f->value_field->name ne 'product')
|
||||
{
|
||||
# Just return unfiltered list
|
||||
return @$all;
|
||||
}
|
||||
# Product field is a special case: it has access controls applied.
|
||||
# So if our values are controlled by product field value,
|
||||
# return only ones visible inside products visible to current user.
|
||||
my $h = Bugzilla->fieldvaluecontrol
|
||||
->{Bugzilla->get_field('product')->id}
|
||||
->{values}
|
||||
->{$f->id};
|
||||
my $visible_ids = { map { $_->id => 1 } Bugzilla::Product->get_all };
|
||||
my $vis;
|
||||
my $filtered = [];
|
||||
for my $value (@$all)
|
||||
{
|
||||
$vis = !$h->{$value->id} || !%{$h->{$value->id}};
|
||||
for (keys %{$h->{$value->id}})
|
||||
{
|
||||
if ($visible_ids->{$_})
|
||||
{
|
||||
$vis = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
push @$filtered, $value if $vis;
|
||||
}
|
||||
# CUSTOM_SORT means the class has manual sorting in new_from_list, like Bugzilla::Version
|
||||
if (!$class->CUSTOM_SORT)
|
||||
{
|
||||
my $order = $class->LIST_ORDER;
|
||||
$order =~ s/(\s+(A|DE)SC)(?!\w)//giso;
|
||||
$order = [ split /[\s,]*,[\s,]*/, $order ];
|
||||
$filtered = [ sort
|
||||
{
|
||||
# FIXME Think about "natural sort"?
|
||||
my $t;
|
||||
for (@$order)
|
||||
{
|
||||
if ($a->{$_} =~ /^-?\d+(\.\d+)?$/so && $b->{$_} =~ /^-?\d+(\.\d+)?$/so)
|
||||
{
|
||||
$t = $a->{$_} <=> $b->{$_};
|
||||
}
|
||||
else
|
||||
{
|
||||
$t = lc $a->{$_} cmp lc $b->{$_};
|
||||
}
|
||||
return $t if $t;
|
||||
}
|
||||
return 0;
|
||||
} @$filtered ];
|
||||
}
|
||||
$rc_cache->{get_all}->{$class}->{$include_disabled ? 1 : 0} = $filtered;
|
||||
return @$filtered;
|
||||
}
|
||||
|
||||
# Returns names of all _active_ values, enabled for products that current user can see
|
||||
sub get_all_names
|
||||
{
|
||||
my $class = shift;
|
||||
my $dup = {};
|
||||
my $names = [];
|
||||
my $idf = $class->ID_FIELD;
|
||||
my $namef = $class->NAME_FIELD;
|
||||
# Remember IDs of each name
|
||||
for ($class->get_all())
|
||||
{
|
||||
if (!$dup->{$_->{$namef}})
|
||||
{
|
||||
push @$names, ($dup->{$_->{$namef}} = { name => $_->{$namef}, ids => [ $_->{$idf} ] });
|
||||
}
|
||||
else
|
||||
{
|
||||
push @{$dup->{$_->{$namef}}->{ids}}, $_->{$idf};
|
||||
}
|
||||
}
|
||||
return $names;
|
||||
}
|
||||
|
||||
#############
|
||||
# Accessors #
|
||||
#############
|
||||
|
||||
sub is_active { return $_[0]->{isactive}; }
|
||||
sub sortkey { return $_[0]->{sortkey}; }
|
||||
|
||||
# FIXME Never use bug_count() on a copy from legal_values, as the result will be cached...
|
||||
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 ".$self->REL_TABLE." WHERE value_id = ?", undef, $self->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs WHERE $fname = ?", undef, $self->id);
|
||||
}
|
||||
$self->{bug_count} = $count;
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub field
|
||||
{
|
||||
my $invocant = shift;
|
||||
return Bugzilla->get_field($invocant->FIELD_NAME);
|
||||
}
|
||||
|
||||
sub is_default
|
||||
{
|
||||
my $self = shift;
|
||||
return $self->field->default_value_hash->{$self->id};
|
||||
}
|
||||
|
||||
sub controls_visibility_of_fields
|
||||
{
|
||||
my $self = shift;
|
||||
my $vid = $self->id;
|
||||
my $fid = $self->field->id;
|
||||
return [
|
||||
map { Bugzilla->get_field($_) }
|
||||
grep { Bugzilla->fieldvaluecontrol->{$fid}->{fields}->{$_}->{$vid} }
|
||||
keys %{Bugzilla->fieldvaluecontrol->{$fid}->{fields}}
|
||||
];
|
||||
}
|
||||
|
||||
sub controls_visibility_of_field_values
|
||||
{
|
||||
my $self = shift;
|
||||
my $vid = $self->id;
|
||||
my $fid = $self->field->id;
|
||||
if (!$self->{controls_visibility_of_field_values})
|
||||
{
|
||||
my $h = Bugzilla->fieldvaluecontrol->{$fid}->{values};
|
||||
my $r = {};
|
||||
for my $f (keys %$h)
|
||||
{
|
||||
my $t = [ grep { $h->{$f}->{$_}->{$vid} } keys %{$h->{$f}} ];
|
||||
$f = Bugzilla->get_field($f);
|
||||
$r->{$f->name} = $f->value_type->new_from_list($t) if @$t;
|
||||
}
|
||||
$self->{controls_visibility_of_field_values} = $r;
|
||||
}
|
||||
return $self->{controls_visibility_of_field_values};
|
||||
}
|
||||
|
||||
sub visibility_values
|
||||
{
|
||||
my $self = shift;
|
||||
my $f;
|
||||
if ($self->field->value_field_id && !($f = $self->{visibility_values}))
|
||||
{
|
||||
my $hash = Bugzilla->fieldvaluecontrol
|
||||
->{$self->field->value_field_id}
|
||||
->{values}
|
||||
->{$self->field->id}
|
||||
->{$self->id};
|
||||
$f = $hash ? [ keys %$hash ] : [];
|
||||
if (@$f)
|
||||
{
|
||||
$f = $self->field->value_field->value_type->new_from_list($f);
|
||||
}
|
||||
$self->{visibility_values} = $f;
|
||||
}
|
||||
return $f;
|
||||
}
|
||||
|
||||
############
|
||||
# Mutators #
|
||||
############
|
||||
|
||||
sub set_is_active { $_[0]->set('isactive', $_[1]); }
|
||||
*set_isactive = *set_is_active;
|
||||
|
||||
sub set_name { $_[0]->set('value', $_[1]); }
|
||||
sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
|
||||
|
||||
sub set_visibility_values
|
||||
{
|
||||
my $self = shift;
|
||||
my ($value_ids, $skip_invisible) = @_;
|
||||
$self->field->update_visibility_values($self->id, $value_ids, $skip_invisible);
|
||||
delete $self->{visibility_values};
|
||||
return $value_ids;
|
||||
}
|
||||
|
||||
##############
|
||||
# Validators #
|
||||
##############
|
||||
|
||||
sub _check_value
|
||||
{
|
||||
my ($invocant, $value) = @_;
|
||||
|
||||
my $field = $invocant->field;
|
||||
|
||||
$value = trim($value);
|
||||
|
||||
ThrowUserError('fieldvalue_undefined') if !defined $value || $value eq "";
|
||||
ThrowUserError('fieldvalue_name_too_long', { value => $value })
|
||||
if length($value) > MAX_FIELD_VALUE_SIZE;
|
||||
|
||||
my $exists = $invocant->type($field)->new({ name => $value });
|
||||
if ($exists && (!blessed($invocant) || $invocant->id != $exists->id))
|
||||
{
|
||||
ThrowUserError('fieldvalue_already_exists', { field => $field, value => $exists });
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub _check_sortkey
|
||||
{
|
||||
my ($invocant, $value) = @_;
|
||||
$value = trim($value);
|
||||
return 0 if !$value;
|
||||
# Store for the error message in case detaint_natural clears it.
|
||||
my $orig_value = $value;
|
||||
detaint_natural($value) || ThrowUserError('fieldvalue_sortkey_invalid', {
|
||||
sortkey => $orig_value,
|
||||
field => $invocant->field,
|
||||
});
|
||||
return $value;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Field::Choice - A legal value for a <select>-type field.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $field = new Bugzilla::Field({name => 'bug_status'});
|
||||
|
||||
my $choice = $field->value_type->new(1);
|
||||
|
||||
my $choices = $field->value_type->new_from_list([1,2,3]);
|
||||
my $choices = $field->value_type->get_all();
|
||||
my $choices = $field->value_type->match({ sortkey => 10 });
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is an implementation of L<Bugzilla::Object>, but with a twist.
|
||||
You can't call any class methods (such as C<new>, C<create>, etc.)
|
||||
directly on C<Bugzilla::Field::Choice> itself. Instead, you have to
|
||||
call C<Bugzilla::Field::Choice-E<gt>type($field)> to get the class
|
||||
you're going to instantiate, and then you call the methods on that.
|
||||
|
||||
We do that because each field has its own database table for its values, so
|
||||
each value type needs its own class.
|
||||
|
||||
See the L</SYNOPSIS> for examples of how this works.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 Class Factory
|
||||
|
||||
In object-oriented design, a "class factory" is a method that picks
|
||||
and returns the right class for you, based on an argument that you pass.
|
||||
|
||||
=over
|
||||
|
||||
=item C<type>
|
||||
|
||||
Takes a single argument, which is either the name of a field from the
|
||||
C<fielddefs> table, or a L<Bugzilla::Field> object representing a field.
|
||||
|
||||
Returns an appropriate subclass of C<Bugzilla::Field::Choice> that you
|
||||
can now call class methods on (like C<new>, C<create>, C<match>, etc.)
|
||||
|
||||
B<NOTE>: YOU CANNOT CALL CLASS METHODS ON C<Bugzilla::Field::Choice>. You
|
||||
must call C<type> to get a class you can call methods on.
|
||||
|
||||
=back
|
||||
|
||||
=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<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 C<Bugzilla::Field::Choice> objects,
|
||||
representing values that this value controls the visibility of, for
|
||||
that field.
|
||||
|
||||
=back
|
|
@ -95,7 +95,11 @@ sub SETTERS
|
|||
{
|
||||
$s->{$field->name} = CUSTOM_FIELD_VALIDATORS->{$field->type};
|
||||
}
|
||||
$class->OVERRIDE_SETTERS($s);
|
||||
my $ns = $class->OVERRIDE_SETTERS($s);
|
||||
if ($ns ne $s && ref $ns)
|
||||
{
|
||||
$s = { %$s, %$ns };
|
||||
}
|
||||
|
||||
$cache->{setters}->{$class} = $s;
|
||||
return $cache->{setters}->{$class};
|
||||
|
|
|
@ -196,13 +196,14 @@ sub update_system_groups
|
|||
}
|
||||
}
|
||||
|
||||
sub create_default_classification {
|
||||
sub create_default_classification
|
||||
{
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# Make the default Classification if it doesn't already exist.
|
||||
if (!$dbh->selectrow_array('SELECT 1 FROM classifications')) {
|
||||
print get_text('install_default_classification',
|
||||
{ name => DEFAULT_CLASSIFICATION->{name} }) . "\n";
|
||||
if (!$dbh->selectrow_array('SELECT 1 FROM classifications'))
|
||||
{
|
||||
print get_text('install_default_classification', { name => DEFAULT_CLASSIFICATION->{name} }) . "\n";
|
||||
Bugzilla::Classification->create(DEFAULT_CLASSIFICATION);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4251,8 +4251,11 @@ sub _add_class_schema
|
|||
user => [
|
||||
[ 'login_name', 'Login', FIELD_TYPE_FREETEXT ],
|
||||
[ 'realname', 'Real Name', FIELD_TYPE_FREETEXT ],
|
||||
[ 'mybugslink', 'Show "My Bugs" link', FIELD_TYPE_BOOLEAN ],
|
||||
[ 'disabledtext', 'Disabled Text', FIELD_TYPE_TEXTAREA ],
|
||||
[ 'disable_mail', 'Disable Bugmail', FIELD_TYPE_BOOLEAN ],
|
||||
[ 'is_enabled', 'Is Enabled', FIELD_TYPE_BOOLEAN ],
|
||||
[ 'last_seen_date', 'Last seen date', FIELD_TYPE_DATETIME ],
|
||||
],
|
||||
classification => [
|
||||
[ 'name', 'Name', FIELD_TYPE_FREETEXT ],
|
||||
|
@ -4261,7 +4264,7 @@ sub _add_class_schema
|
|||
],
|
||||
product => [
|
||||
[ 'name', 'Name', FIELD_TYPE_FREETEXT ],
|
||||
[ 'classification_id', 'Classification', FIELD_TYPE_SINGLE, 'classification' ],
|
||||
[ 'classification', 'Classification', FIELD_TYPE_SINGLE, 'classification' ],
|
||||
[ 'description', 'Description', FIELD_TYPE_TEXTAREA ],
|
||||
[ 'entryheaderhtml', 'Bug entry header HTML', FIELD_TYPE_TEXTAREA ],
|
||||
[ 'isactive', 'Open for bug entry', FIELD_TYPE_BOOLEAN ],
|
||||
|
@ -4333,7 +4336,7 @@ sub _add_class_schema
|
|||
[ 'type_id', 'Flag Type', FIELD_TYPE_SINGLE, 'flagtype' ],
|
||||
[ 'status', 'Status', FIELD_TYPE_FREETEXT ],
|
||||
[ 'bug_id', 'Bug', FIELD_TYPE_SINGLE, 'bug' ],
|
||||
[ 'attach_id', 'ID', FIELD_TYPE_SINGLE, 'attachment' ],
|
||||
[ 'attach_id', 'Attachment', FIELD_TYPE_SINGLE, 'attachment' ],
|
||||
[ 'creation_date', 'Creation Time', FIELD_TYPE_DATETIME ],
|
||||
[ 'modification_date', 'Modification Time', FIELD_TYPE_DATETIME ],
|
||||
[ 'setter_id', 'Setter', FIELD_TYPE_SINGLE, 'user' ],
|
||||
|
|
|
@ -207,8 +207,8 @@ sub before_insert {
|
|||
# anymore now.
|
||||
delete $_->{gnats_id} foreach @{ $self->users };
|
||||
|
||||
# Grab a version out of a bug for each product, so that there is a
|
||||
# valid "version" argument for Bugzilla::Product->create.
|
||||
# Grab a version out of a bug for each product
|
||||
# FIXME: Create versions AFTER creating products
|
||||
foreach my $product (@{ $self->products }) {
|
||||
my $bug = first { $_->{product} eq $product->{name} and $_->{version} }
|
||||
@{ $self->bugs };
|
||||
|
|
|
@ -26,12 +26,16 @@ sub new
|
|||
return $class->SUPER::new($param);
|
||||
}
|
||||
|
||||
# Shorthand create() method - creates empty object, fills it with setters and saves into DB
|
||||
sub create
|
||||
{
|
||||
my $class = shift;
|
||||
$class = ref($class) || $class;
|
||||
die("$class has no create() method. You should create an empty $class".
|
||||
" object with new(), fill it using setters and call update().");
|
||||
my ($params) = @_;
|
||||
my $self = $class->new;
|
||||
$self->set_all($params);
|
||||
$self->update;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub update
|
||||
|
|
|
@ -32,108 +32,39 @@ use Bugzilla::Series;
|
|||
use Bugzilla::FlagType::UserList;
|
||||
use Bugzilla::Hook;
|
||||
|
||||
use base qw(Bugzilla::Field::Choice);
|
||||
use base qw(Bugzilla::GenericObject);
|
||||
|
||||
use constant DB_TABLE => 'products';
|
||||
use constant NAME_FIELD => 'name';
|
||||
use constant LIST_ORDER => 'name';
|
||||
use constant CLASS_NAME => 'product';
|
||||
|
||||
use constant DEFAULT_CLASSIFICATION_ID => 1;
|
||||
|
||||
###############################
|
||||
#### Initialization ####
|
||||
###############################
|
||||
|
||||
use constant DB_TABLE => 'products';
|
||||
use constant FIELD_NAME => 'product';
|
||||
# Reset these back to the Bugzilla::Object defaults, instead of the
|
||||
# Bugzilla::Field::Choice defaults.
|
||||
use constant NAME_FIELD => 'name';
|
||||
use constant LIST_ORDER => 'name';
|
||||
|
||||
# Workaround mysterious taint issue:
|
||||
# join('', DB_COLUMNS) will be tainted if defined as 'use constant'
|
||||
# (although none of the columns themselves will be tainted in that case)
|
||||
sub DB_COLUMNS() { qw(
|
||||
id
|
||||
name
|
||||
wiki_url
|
||||
notimetracking
|
||||
extproduct
|
||||
classification_id
|
||||
description
|
||||
isactive
|
||||
votesperuser
|
||||
maxvotesperbug
|
||||
votestoconfirm
|
||||
allows_unconfirmed
|
||||
cc_group
|
||||
entryheaderhtml
|
||||
) }
|
||||
|
||||
use constant REQUIRED_CREATE_FIELDS => qw(
|
||||
name
|
||||
description
|
||||
);
|
||||
|
||||
# Allow to update every valid DB column
|
||||
*UPDATE_COLUMNS = *DB_COLUMNS;
|
||||
|
||||
use constant VALIDATORS => {
|
||||
allows_unconfirmed => \&Bugzilla::Object::check_boolean,
|
||||
classification_id => \&_check_classification_id,
|
||||
name => \&_check_name,
|
||||
description => \&_check_description,
|
||||
version => \&_check_version,
|
||||
isactive => \&Bugzilla::Object::check_boolean,
|
||||
votesperuser => \&_check_votes_per_user,
|
||||
maxvotesperbug => \&_check_votes_per_bug,
|
||||
votestoconfirm => \&_check_votes_to_confirm,
|
||||
notimetracking => \&Bugzilla::Object::check_boolean,
|
||||
extproduct => \&_check_extproduct,
|
||||
# CustIS Bug 38616 - CC list restriction
|
||||
# FIXME: Maybe also disallow bug access for these users?
|
||||
cc_group => \&_check_cc_group,
|
||||
use constant OVERRIDE_SETTERS => {
|
||||
classification => \&_set_classification,
|
||||
name => \&_set_name,
|
||||
description => \&_set_description,
|
||||
votesperuser => \&_set_votes_per_user,
|
||||
maxvotesperbug => \&_set_votes_per_bug,
|
||||
votestoconfirm => \&_set_votes_to_confirm,
|
||||
extproduct => \&_set_extproduct,
|
||||
};
|
||||
|
||||
###############################
|
||||
#### Constructors #####
|
||||
###############################
|
||||
|
||||
sub create
|
||||
sub check
|
||||
{
|
||||
my $class = shift;
|
||||
my ($params) = @_;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->bz_start_transaction();
|
||||
|
||||
$class->check_required_create_fields($params);
|
||||
|
||||
# Some fields do not exist in the DB as is.
|
||||
if (defined $params->{classification})
|
||||
my ($class, $params) = @_;
|
||||
$params = { name => $params } if !ref $params;
|
||||
$params->{_error} = 'product_access_denied';
|
||||
my $product = $class->SUPER::check($params);
|
||||
if (!Bugzilla->user->can_see_product($product))
|
||||
{
|
||||
$params->{classification_id} = delete $params->{classification};
|
||||
ThrowUserError('product_access_denied', $params);
|
||||
}
|
||||
my $version = delete $params->{version};
|
||||
|
||||
my $field_values = $class->run_create_validators($params);
|
||||
my $product = $class->insert_create_data($field_values);
|
||||
Bugzilla->user->clear_product_cache();
|
||||
|
||||
# Add the new version into the DB as valid values.
|
||||
if ($version)
|
||||
{
|
||||
Bugzilla::Version->create({
|
||||
name => $version,
|
||||
product => $product,
|
||||
});
|
||||
}
|
||||
|
||||
# Fill visibility values
|
||||
$product->set_visibility_values([ $product->classification_id ]);
|
||||
|
||||
Bugzilla::Hook::process('product_end_of_create', { product => $product });
|
||||
|
||||
Bugzilla->get_field(FIELD_NAME)->touch;
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
return $product;
|
||||
}
|
||||
|
||||
|
@ -170,9 +101,8 @@ sub update
|
|||
|
||||
# Don't update the DB if something goes wrong below -> transaction.
|
||||
$dbh->bz_start_transaction();
|
||||
# Bugzilla::Field::Choice is not a threat as we don't have 'value' field
|
||||
# Yet do not call its update() for the future
|
||||
my ($changes, $old_self) = Bugzilla::Object::update($self, @_);
|
||||
my $old_self = $self->{_old_self};
|
||||
my $changes = $self->SUPER::update(@_);
|
||||
|
||||
# FIXME when renaming a product, try to rename it in all named queries
|
||||
|
||||
|
@ -396,9 +326,10 @@ sub update
|
|||
}
|
||||
|
||||
# Fill visibility values
|
||||
$self->set_visibility_values([ $self->classification_id ]);
|
||||
Bugzilla->get_field('product')->update_visibility_values($self->id, [ $self->classification_id ]);
|
||||
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
# Changes have been committed.
|
||||
delete $self->{check_group_controls};
|
||||
Bugzilla->user->clear_product_cache();
|
||||
|
@ -406,8 +337,6 @@ sub update
|
|||
# Now that changes have been committed, we can send emails to voters.
|
||||
Bugzilla->send_mail;
|
||||
|
||||
Bugzilla->get_field(FIELD_NAME)->touch;
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
|
@ -479,26 +408,10 @@ sub remove_from_db
|
|||
#### Validators ####
|
||||
###############################
|
||||
|
||||
sub _check_extproduct
|
||||
sub _set_classification
|
||||
{
|
||||
my ($invocant, $product) = @_;
|
||||
$product = $product ? Bugzilla::Product->check({ id => $product }) : undef;
|
||||
return $product ? $product->id : undef;
|
||||
}
|
||||
|
||||
sub _check_cc_group
|
||||
{
|
||||
my ($invocant, $cc_group) = @_;
|
||||
$cc_group = trim($cc_group);
|
||||
$cc_group = $cc_group ? Bugzilla::Group->check({ name => $cc_group })->id : undef;
|
||||
return $cc_group;
|
||||
}
|
||||
|
||||
sub _check_classification_id
|
||||
{
|
||||
my ($invocant, $classification_name) = @_;
|
||||
|
||||
my $classification_id = 1;
|
||||
my ($self, $classification_name) = @_;
|
||||
my $classification_id = DEFAULT_CLASSIFICATION_ID;
|
||||
if (Bugzilla->get_field('classification')->enabled)
|
||||
{
|
||||
my $classification = ref $classification_name
|
||||
|
@ -506,13 +419,13 @@ sub _check_classification_id
|
|||
: Bugzilla::Classification->check($classification_name);
|
||||
$classification_id = $classification->id;
|
||||
}
|
||||
delete $invocant->{classification_obj} if ref $invocant;
|
||||
delete $self->{classification_obj};
|
||||
return $classification_id;
|
||||
}
|
||||
|
||||
sub _check_name
|
||||
sub _set_name
|
||||
{
|
||||
my ($invocant, $name) = @_;
|
||||
my ($self, $name) = @_;
|
||||
|
||||
$name = trim($name);
|
||||
$name || ThrowUserError('product_blank_name');
|
||||
|
@ -523,7 +436,7 @@ sub _check_name
|
|||
}
|
||||
|
||||
my $product = new Bugzilla::Product({ name => $name });
|
||||
if ($product && (!ref $invocant || $product->id != $invocant->id))
|
||||
if ($product && (!ref $self || $product->id != $self->id))
|
||||
{
|
||||
# Check for exact case sensitive match:
|
||||
if ($product->name eq $name)
|
||||
|
@ -541,34 +454,38 @@ sub _check_name
|
|||
return $name;
|
||||
}
|
||||
|
||||
sub _check_description
|
||||
sub _set_description
|
||||
{
|
||||
my ($invocant, $description) = @_;
|
||||
|
||||
my ($self, $description) = @_;
|
||||
$description = trim($description);
|
||||
$description || ThrowUserError('product_must_have_description');
|
||||
return $description;
|
||||
}
|
||||
|
||||
sub _check_version
|
||||
sub _set_extproduct
|
||||
{
|
||||
my ($invocant, $version) = @_;
|
||||
$version = trim($version);
|
||||
# We will check the version length when Bugzilla::Version->create will do it.
|
||||
return $version;
|
||||
my ($self, $product) = @_;
|
||||
$product = Bugzilla::Product->check({ id => $product }) if $product && !ref $product;
|
||||
if ($self->{extproduct_obj})
|
||||
{
|
||||
delete $self->{extproduct_obj}->{intproduct_name};
|
||||
}
|
||||
delete $self->{extproduct_name};
|
||||
delete $self->{extproduct_obj};
|
||||
$self->{extproduct} = $product ? $product->id : undef;
|
||||
}
|
||||
|
||||
sub _check_votes_per_user
|
||||
sub _set_votes_per_user
|
||||
{
|
||||
return _check_votes(@_, 0);
|
||||
}
|
||||
|
||||
sub _check_votes_per_bug
|
||||
sub _set_votes_per_bug
|
||||
{
|
||||
return _check_votes(@_, 10000);
|
||||
}
|
||||
|
||||
sub _check_votes_to_confirm
|
||||
sub _set_votes_to_confirm
|
||||
{
|
||||
return _check_votes(@_, 0);
|
||||
}
|
||||
|
@ -576,15 +493,14 @@ sub _check_votes_to_confirm
|
|||
# This subroutine is only used internally by other _check_votes_* validators.
|
||||
sub _check_votes
|
||||
{
|
||||
my ($invocant, $votes, $field, $default) = @_;
|
||||
|
||||
my ($self, $votes, $field, $default) = @_;
|
||||
detaint_natural($votes);
|
||||
# On product creation, if the number of votes is not a valid integer,
|
||||
# we silently fall back to the given default value.
|
||||
# If the product already exists and the change is illegal, we complain.
|
||||
if (!defined $votes)
|
||||
{
|
||||
if (ref $invocant)
|
||||
if (ref $self)
|
||||
{
|
||||
ThrowUserError('product_illegal_votes', { field => $field, votes => $_[1] });
|
||||
}
|
||||
|
@ -596,12 +512,6 @@ sub _check_votes
|
|||
return $votes;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Implement Bugzilla::Field::Choice #
|
||||
#####################################
|
||||
|
||||
use constant is_default => 0;
|
||||
|
||||
###############################
|
||||
#### Methods ####
|
||||
###############################
|
||||
|
@ -678,38 +588,6 @@ sub _create_series
|
|||
}
|
||||
}
|
||||
|
||||
sub set_name { $_[0]->set('name', $_[1]); }
|
||||
sub set_wiki_url { $_[0]->set('wiki_url', $_[1]); }
|
||||
sub set_notimetracking { $_[0]->set('notimetracking', $_[1]); }
|
||||
sub set_description { $_[0]->set('description', $_[1]); }
|
||||
sub set_is_active { $_[0]->set('isactive', $_[1]); }
|
||||
sub set_votes_per_user { $_[0]->set('votesperuser', $_[1]); }
|
||||
sub set_votes_per_bug { $_[0]->set('maxvotesperbug', $_[1]); }
|
||||
sub set_votes_to_confirm { $_[0]->set('votestoconfirm', $_[1]); }
|
||||
sub set_allows_unconfirmed { $_[0]->set('allows_unconfirmed', $_[1]); }
|
||||
sub set_classification { $_[0]->set('classification_id', $_[1]); }
|
||||
sub set_entryheaderhtml { $_[0]->set('entryheaderhtml', $_[1]); }
|
||||
|
||||
sub set_cc_group
|
||||
{
|
||||
my ($self, $g) = @_;
|
||||
$self->set('cc_group', $g);
|
||||
delete $self->{cc_group_obj};
|
||||
}
|
||||
|
||||
sub set_extproduct
|
||||
{
|
||||
my ($self, $product) = @_;
|
||||
$product = Bugzilla::Product->check({ id => $product }) if $product && !ref $product;
|
||||
$self->set('extproduct', $product ? $product->id : undef);
|
||||
if ($self->{extproduct_obj})
|
||||
{
|
||||
delete $self->{extproduct_obj}->{intproduct_name};
|
||||
}
|
||||
delete $self->{extproduct_name};
|
||||
delete $self->{extproduct_obj};
|
||||
}
|
||||
|
||||
sub set_group_controls
|
||||
{
|
||||
my ($self, $group, $settings) = @_;
|
||||
|
@ -900,15 +778,9 @@ sub groups_valid
|
|||
sub versions
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{versions})
|
||||
{
|
||||
my $ids = $dbh->selectcol_arrayref(
|
||||
'SELECT id FROM versions WHERE product_id = ?',
|
||||
undef, $self->id
|
||||
);
|
||||
$self->{versions} = Bugzilla::Version->new_from_list($ids);
|
||||
$self->{versions} = Bugzilla::Version->match({ product_id => $self->id });
|
||||
}
|
||||
return $self->{versions};
|
||||
}
|
||||
|
@ -916,15 +788,9 @@ sub versions
|
|||
sub milestones
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{milestones})
|
||||
{
|
||||
my $ids = $dbh->selectcol_arrayref(
|
||||
'SELECT id FROM milestones WHERE product_id = ?',
|
||||
undef, $self->id
|
||||
);
|
||||
$self->{milestones} = Bugzilla::Milestone->new_from_list($ids);
|
||||
$self->{milestones} = Bugzilla::Milestone->match({ product_id => $self->id });
|
||||
}
|
||||
return $self->{milestones};
|
||||
}
|
||||
|
@ -932,11 +798,9 @@ sub milestones
|
|||
sub bug_count
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{bug_count})
|
||||
{
|
||||
$self->{bug_count} = $dbh->selectrow_array(
|
||||
$self->{bug_count} = Bugzilla->dbh->selectrow_array(
|
||||
'SELECT COUNT(bug_id) FROM bugs WHERE product_id = ?',
|
||||
undef, $self->id
|
||||
);
|
||||
|
@ -947,11 +811,9 @@ sub bug_count
|
|||
sub bug_ids
|
||||
{
|
||||
my $self = shift;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!defined $self->{bug_ids})
|
||||
{
|
||||
$self->{bug_ids} = $dbh->selectcol_arrayref(
|
||||
$self->{bug_ids} = Bugzilla->dbh->selectcol_arrayref(
|
||||
'SELECT bug_id FROM bugs WHERE product_id = ?',
|
||||
undef, $self->id
|
||||
);
|
||||
|
@ -1012,57 +874,11 @@ sub flag_types
|
|||
return $self->{flag_types};
|
||||
}
|
||||
|
||||
###############################
|
||||
#### Accessors ######
|
||||
###############################
|
||||
|
||||
sub allows_unconfirmed { return $_[0]->{allows_unconfirmed}; }
|
||||
sub description { return $_[0]->{description}; }
|
||||
sub isactive { return $_[0]->{isactive}; }
|
||||
sub is_active { return $_[0]->{isactive}; }
|
||||
sub votesperuser { return $_[0]->{votesperuser}; }
|
||||
sub maxvotesperbug { return $_[0]->{maxvotesperbug}; }
|
||||
sub votestoconfirm { return $_[0]->{votestoconfirm}; }
|
||||
sub votes_per_user { return $_[0]->{votesperuser}; }
|
||||
sub max_votes_per_bug { return $_[0]->{maxvotesperbug}; }
|
||||
sub votes_to_confirm { return $_[0]->{votestoconfirm}; }
|
||||
sub classification_id { return $_[0]->{classification_id}; }
|
||||
sub wiki_url { return $_[0]->{wiki_url}; }
|
||||
sub notimetracking { return $_[0]->{notimetracking}; }
|
||||
sub extproduct { return $_[0]->{extproduct}; }
|
||||
sub cc_group { return $_[0]->{cc_group}; }
|
||||
sub entryheaderhtml { return $_[0]->{entryheaderhtml}; }
|
||||
|
||||
###############################
|
||||
#### Subroutines ######
|
||||
###############################
|
||||
|
||||
sub classification_obj
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{classification_obj} ||= Bugzilla::Classification->new($self->classification_id);
|
||||
return $self->{classification_obj};
|
||||
}
|
||||
|
||||
sub extproduct_obj
|
||||
{
|
||||
my $self = shift;
|
||||
if (!exists $self->{extproduct_obj})
|
||||
{
|
||||
$self->{extproduct_obj} = $self->{extproduct} ? $self->new($self->{extproduct}) : undef;
|
||||
}
|
||||
return $self->{extproduct_obj};
|
||||
}
|
||||
|
||||
sub cc_group_obj
|
||||
{
|
||||
my $self = shift;
|
||||
if (!exists $self->{cc_group_obj})
|
||||
{
|
||||
$self->{cc_group_obj} = $self->{cc_group} ? Bugzilla::Group->new($self->{cc_group}) : undef;
|
||||
}
|
||||
return $self->{cc_group_obj};
|
||||
}
|
||||
sub is_active { $_[0]->isactive }
|
||||
|
||||
sub enterable_extproduct_name
|
||||
{
|
||||
|
@ -1089,35 +905,6 @@ sub enterable_intproduct_name
|
|||
return $self->{intproduct_name};
|
||||
}
|
||||
|
||||
sub check_product
|
||||
{
|
||||
my ($product_name) = @_;
|
||||
|
||||
unless ($product_name)
|
||||
{
|
||||
ThrowUserError('product_not_specified');
|
||||
}
|
||||
my $product = new Bugzilla::Product({ name => $product_name });
|
||||
unless ($product)
|
||||
{
|
||||
ThrowUserError('product_doesnt_exist', { product => $product_name });
|
||||
}
|
||||
return $product;
|
||||
}
|
||||
|
||||
sub check
|
||||
{
|
||||
my ($class, $params) = @_;
|
||||
$params = { name => $params } if !ref $params;
|
||||
$params->{_error} = 'product_access_denied';
|
||||
my $product = $class->SUPER::check($params);
|
||||
if (!Bugzilla->user->can_see_product($product))
|
||||
{
|
||||
ThrowUserError('product_access_denied', $params);
|
||||
}
|
||||
return $product;
|
||||
}
|
||||
|
||||
# Product is a special case: it has access controls applied.
|
||||
# So return all products visible to current user.
|
||||
sub get_all { @{ Bugzilla->user->get_selectable_products } }
|
||||
|
@ -1371,15 +1158,6 @@ than calling those accessors on every item in the array individually.
|
|||
This function is not exported, so must be called like
|
||||
C<Bugzilla::Product::preload($products)>.
|
||||
|
||||
=item C<check_product($product_name)>
|
||||
|
||||
Description: Checks if the product name was passed in and if is a valid
|
||||
product.
|
||||
|
||||
Params: $product_name - String with a product name.
|
||||
|
||||
Returns: Bugzilla::Product object.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
|
|
@ -968,7 +968,7 @@ sub check_can_admin_product
|
|||
my ($self, $product_name) = @_;
|
||||
|
||||
# First make sure the product name is valid.
|
||||
my $product = ref $product_name ? $product_name : Bugzilla::Product::check_product($product_name);
|
||||
my $product = ref $product_name ? $product_name : Bugzilla::Product->check({ name => $product_name });
|
||||
|
||||
$self->in_group('editcomponents', $product->id)
|
||||
|| $self->in_group('editcomponents')
|
||||
|
|
|
@ -7,7 +7,6 @@ package Bugzilla::WebService::Field;
|
|||
|
||||
use strict;
|
||||
use base qw(Bugzilla::WebService);
|
||||
use Bugzilla::Field::Choice;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::WebService::Util qw(validate);
|
||||
|
|
|
@ -178,7 +178,7 @@ sub collect_stats {
|
|||
|
||||
my $product_id;
|
||||
if ($product ne '-All-') {
|
||||
my $prod = Bugzilla::Product::check_product($product);
|
||||
my $prod = Bugzilla::Product->check($product);
|
||||
$product_id = $prod->id;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ if ($ARGS->{product})
|
|||
{
|
||||
foreach my $product_name (list $ARGS->{product})
|
||||
{
|
||||
# We don't use check_product because config.cgi outputs mostly
|
||||
# We don't use check() because config.cgi outputs mostly
|
||||
# in XML and JS and we don't want to display an HTML error
|
||||
# instead of that.
|
||||
my $product = new Bugzilla::Product({ name => $product_name });
|
||||
|
|
|
@ -99,7 +99,7 @@ sub get_prod_comp
|
|||
my $component;
|
||||
if ($product)
|
||||
{
|
||||
$product = Bugzilla::Product::check_product($product);
|
||||
$product = Bugzilla::Product->check($product);
|
||||
$component = $ARGS->{component} || undef;
|
||||
$component = Bugzilla::Component->check({ product => $product, name => $component }) if $component;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,6 @@ if ($action eq 'new')
|
|||
name => $product_name,
|
||||
description => $ARGS->{description},
|
||||
entryheaderhtml => $ARGS->{entryheaderhtml},
|
||||
version => $ARGS->{version},
|
||||
isactive => $ARGS->{is_active},
|
||||
allows_unconfirmed => $ARGS->{allows_unconfirmed},
|
||||
wiki_url => $ARGS->{wiki_url},
|
||||
|
@ -198,6 +197,14 @@ if ($action eq 'new')
|
|||
$product->_create_bug_group(1);
|
||||
}
|
||||
|
||||
if ($ARGS->{version})
|
||||
{
|
||||
Bugzilla::Version->create({
|
||||
name => $ARGS->{version},
|
||||
product => $product,
|
||||
});
|
||||
}
|
||||
|
||||
# Create groups and series for the new product, if requested.
|
||||
$product->_create_bug_group() if $ARGS->{makeproductgroup};
|
||||
$product->_create_series() if $ARGS->{createseries};
|
||||
|
@ -313,27 +320,29 @@ if ($action eq 'update')
|
|||
if ($useclassification)
|
||||
{
|
||||
$vars->{old_classification} = $product->classification_obj;
|
||||
$product->set_classification($ARGS->{classification});
|
||||
$product->set('classification', $ARGS->{classification});
|
||||
}
|
||||
$product->set_name($product_name);
|
||||
$product->set_wiki_url($ARGS->{wiki_url});
|
||||
$product->set_notimetracking($ARGS->{notimetracking});
|
||||
$product->set_extproduct($ARGS->{extproduct});
|
||||
$product->set_cc_group($ARGS->{cc_group} || '');
|
||||
$product->set_description($ARGS->{description});
|
||||
$product->set_entryheaderhtml($ARGS->{entryheaderhtml});
|
||||
$product->set_is_active($ARGS->{is_active});
|
||||
$product->set_all({
|
||||
name => $product_name,
|
||||
wiki_url => $ARGS->{wiki_url},
|
||||
notimetracking => $ARGS->{notimetracking},
|
||||
extproduct => $ARGS->{extproduct},
|
||||
cc_group => $ARGS->{cc_group} || '',
|
||||
description => $ARGS->{description},
|
||||
entryheaderhtml => $ARGS->{entryheaderhtml},
|
||||
isactive => $ARGS->{is_active},
|
||||
allows_unconfirmed => $ARGS->{allows_unconfirmed},
|
||||
});
|
||||
if (Bugzilla->get_field('votes')->enabled)
|
||||
{
|
||||
$product->set_votes_per_user($ARGS->{votesperuser});
|
||||
$product->set_votes_per_bug($ARGS->{maxvotesperbug});
|
||||
$product->set_votes_to_confirm($ARGS->{votestoconfirm});
|
||||
$product->set('votesperuser', $ARGS->{votesperuser});
|
||||
$product->set('maxvotesperbug', $ARGS->{maxvotesperbug});
|
||||
$product->set('votestoconfirm', $ARGS->{votestoconfirm});
|
||||
}
|
||||
$product->set_allows_unconfirmed($ARGS->{allows_unconfirmed});
|
||||
|
||||
my $changes = $product->update();
|
||||
|
||||
$changes->{control_lists} = 1 if $product->field->update_control_lists($product->id, $ARGS);
|
||||
$changes->{control_lists} = 1 if Bugzilla->get_field('product')->update_control_lists($product->id, $ARGS);
|
||||
|
||||
delete_token($token);
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ use Bugzilla::Util;
|
|||
use Bugzilla::Error;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Field::Choice;
|
||||
use Bugzilla::Token;
|
||||
|
||||
my $ARGS = Bugzilla->input_params;
|
||||
|
|
|
@ -75,7 +75,7 @@ if ($product_name eq '')
|
|||
}
|
||||
else
|
||||
{
|
||||
# Do not use Bugzilla::Product::check_product() here, else the user
|
||||
# Do not use Bugzilla::Product->check() here, else the user
|
||||
# could know whether the product doesn't exist or is not accessible.
|
||||
$product = new Bugzilla::Product({ name => $product_name });
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
</tr>
|
||||
[% END %]
|
||||
|
||||
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=product.field this_value=product %]
|
||||
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=Bugzilla.get_field('product') this_value=product %]
|
||||
|
||||
<tr>
|
||||
<th align="left" valign="top">
|
||||
|
|
|
@ -137,7 +137,7 @@ function showbugcomma()
|
|||
<ul class="zero_result_links">
|
||||
<li><a href="enter_bug.cgi?[% urlquerypart | html %]">Create new [% terms.bug %] with the same fields</a></li>
|
||||
<li>[% PROCESS enter_bug_link %]</li>
|
||||
[% IF one_product.defined %]
|
||||
[% IF one_product %]
|
||||
<li><a href="enter_bug.cgi">File a new [% terms.bug %] in a
|
||||
different product</a></li>
|
||||
[% END %]
|
||||
|
@ -265,7 +265,7 @@ function showbugcomma()
|
|||
|
||||
<p>
|
||||
Query executed in [% query_sql_time %] seconds. Page generated in $_query_template_time seconds.
|
||||
[%+ IF one_product.defined && bugs.size %][% PROCESS enter_bug_link %][% END %]
|
||||
[%+ IF one_product && bugs.size %][% PROCESS enter_bug_link %][% END %]
|
||||
</p>
|
||||
|
||||
[%############################################################################%]
|
||||
|
@ -281,10 +281,10 @@ function showbugcomma()
|
|||
|
||||
[% BLOCK enter_bug_link %]
|
||||
<a href="enter_bug.cgi
|
||||
[%- IF one_product.defined %]?product=
|
||||
[%- IF one_product %]?product=
|
||||
[%- one_product.name FILTER url_quote %][% END %]">File
|
||||
a new [% terms.bug %]
|
||||
[% IF one_product.defined %]
|
||||
[% IF one_product %]
|
||||
in the "[% one_product.name FILTER html %]" product
|
||||
[% END %]</a>
|
||||
[% END %]
|
||||
|
|
Loading…
Reference in New Issue