Move data validation and update/insert code into Bugzilla::FlagType

hinted-selects
Vitaliy Filippov 2014-10-08 15:56:39 +04:00
parent fed3567b82
commit 112eb10a5f
3 changed files with 160 additions and 177 deletions

View File

@ -30,6 +30,9 @@ use Bugzilla::Group;
use base qw(Bugzilla::Object);
use constant DB_TABLE => 'flagtypes';
use constant LIST_ORDER => 'sortkey, name';
use constant DB_COLUMNS => qw(
id
name
@ -43,9 +46,22 @@ use constant DB_COLUMNS => qw(
grant_group_id
request_group_id
);
use constant UPDATE_COLUMNS => grep { $_ ne 'id' } DB_COLUMNS;
use constant REQUIRED_CREATE_FIELDS => UPDATE_COLUMNS;
use constant DB_TABLE => 'flagtypes';
use constant LIST_ORDER => 'sortkey, name';
use constant VALIDATORS => {
name => \&_check_name,
description => \&_check_description,
target_type => \&_check_target_type,
sortkey => \&_check_sortkey,
is_active => \&Bugzilla::Object::check_boolean,
is_requestable => \&Bugzilla::Object::check_boolean,
is_requesteeble => \&Bugzilla::Object::check_boolean,
is_multiplicable => \&Bugzilla::Object::check_boolean,
grant_group_id => \&_check_group,
request_group_id => \&_check_group,
cc_list => \&_check_cc_list,
};
=head1 NAME
@ -164,7 +180,7 @@ sub cc_list_obj
sub cc_list_str
{
my $self = shift;
return join(', ', map { $_->login } @{$self->cc_list});
return join(', ', map { $_->login } @{$self->cc_list_obj});
}
sub grant_group
@ -232,6 +248,37 @@ Returns the total number of flag types matching the given criteria.
=cut
sub create
{
my ($class, $params) = @_;
my $cc = $class->_check_cc_list(delete $params->{cc_list});
my $self = $class->SUPER::create($params);
$self->{cc_list} = $cc;
$self->save_cc_list;
return $self;
}
sub update
{
my $self = shift;
my $ch = $self->SUPER::update(@_);
$self->save_cc_list;
return $ch;
}
sub save_cc_list
{
my $self = shift;
Bugzilla->dbh->do("DELETE FROM flagtype_cc_list WHERE object_id=?", undef, $self->id);
if (@{$self->{cc_list}})
{
Bugzilla->dbh->do(
"INSERT INTO flagtype_cc_list (object_id, value_id) VALUES ".
join(', ', map { "(?, ?)" } @{$self->{cc_list}}), undef, map { ($self->id, $_->id) } @{$self->{cc_list}}
);
}
}
sub match
{
my ($criteria) = @_;
@ -263,6 +310,72 @@ sub count
return $count;
}
######################################################################
# Validators
######################################################################
sub _check_name
{
my ($invocant, $name) = @_;
($name && $name !~ /[ ,]/ && length($name) <= 255)
|| ThrowUserError("flag_type_name_invalid", { name => $name });
return $name;
}
sub _check_description
{
my ($invocant, $description) = @_;
$description = trim($description);
length($description) < 2**16-1
|| ThrowUserError("flag_type_description_invalid");
return $description;
}
sub _check_target_type
{
my ($invocant, $type) = @_;
unless ($type eq 'bug' || $type eq 'attachment' || $type eq 'b' || $type eq 'a')
{
ThrowCodeError("flag_type_target_type_invalid", { target_type => $type });
}
return $type eq 'bug' || $type eq 'b' ? 'b' : 'a';
}
sub _check_sortkey
{
my ($invocant, $sortkey) = @_;
my $k = $sortkey;
if (!detaint_natural($sortkey))
{
ThrowUserError("flag_type_sortkey_invalid", { sortkey => $k });
}
return $sortkey;
}
sub _check_group
{
my ($invocant, $group, $field) = @_;
# Convert group names to group IDs
if ($group)
{
trick_taint($group);
my $gid = Bugzilla->dbh->selectrow_array('SELECT id FROM groups WHERE name=?', undef, $group);
$gid || ThrowUserError("group_unknown", { name => $group });
$group = $gid;
}
else
{
$group = undef;
}
return $group;
}
sub _check_cc_list
{
my ($invocant, $cc_list) = @_;
return Bugzilla::User->match({ login_name => [ split /[\s,]*,[\s,]*/, $cc_list ] });
}
######################################################################
# Private Functions
######################################################################

View File

@ -93,10 +93,22 @@ exit;
# Functions
################################################################################
sub get_prod_comp
{
my $product = scalar $cgi->param('product') || undef;
my $component;
if ($product)
{
$product = Bugzilla::Product::check_product($product);
$component = scalar $cgi->param('component');
$component = Bugzilla::Component->check({ product => $product, name => $component }) if $component;
}
return ($product, $component);
}
sub ft_list
{
my $product = validateProduct(scalar $cgi->param('product'));
my $component = validateComponent($product, scalar $cgi->param('component'));
my ($product, $component) = get_prod_comp();
my $product_id = $product ? $product->id : 0;
my $component_id = $component ? $component->id : 0;
my $show_flag_counts = (defined $cgi->param('show_flag_counts')) ? 1 : 0;
@ -168,9 +180,10 @@ sub edit
{
my ($action) = @_;
my $flag_type;
my $target_type;
if ($action eq 'enter')
{
validateTargetType();
$target_type = scalar $cgi->param('target_type') eq 'attachment' ? 'attachment' : 'bug';
}
else
{
@ -202,7 +215,7 @@ sub edit
my %inclusions;
$inclusions{"__Any__:__Any__"} = "0:0";
$vars->{type} = {
target_type => scalar $cgi->param('target_type'),
target_type => $target_type,
inclusions => \%inclusions,
};
}
@ -218,24 +231,18 @@ sub edit
sub processCategoryChange
{
my ($categoryAction, $token) = @_;
validateIsActive();
validateIsRequestable();
validateIsRequesteeble();
validateAllowMultiple();
my @inclusions = $cgi->param('inclusions');
my @exclusions = $cgi->param('exclusions');
if ($categoryAction eq 'include')
{
my $product = validateProduct(scalar $cgi->param('product'));
my $component = validateComponent($product, scalar $cgi->param('component'));
my ($product, $component) = get_prod_comp();
my $category = ($product ? $product->id : 0) . ":" . ($component ? $component->id : 0);
push(@inclusions, $category) unless grep($_ eq $category, @inclusions);
}
elsif ($categoryAction eq 'exclude')
{
my $product = validateProduct(scalar $cgi->param('product'));
my $component = validateComponent($product, scalar $cgi->param('component'));
my ($product, $component) = get_prod_comp();
my $category = ($product ? $product->id : 0) . ":" . ($component ? $component->id : 0);
push(@exclusions, $category) unless grep($_ eq $category, @exclusions);
}
@ -314,43 +321,19 @@ sub insert
{
my $token = shift;
check_token_data($token, 'add_flagtype');
my $name = validateName();
my $description = validateDescription();
validateTargetType();
validateSortKey();
validateIsActive();
validateIsRequestable();
validateIsRequesteeble();
validateAllowMultiple();
validateGroups();
my $dbh = Bugzilla->dbh;
Bugzilla->dbh->bz_start_transaction;
my $target_type = $cgi->param('target_type') eq "bug" ? "b" : "a";
$dbh->bz_start_transaction();
# Insert a record for the new flag type into the database.
$dbh->do(
'INSERT INTO flagtypes (name, description, target_type, sortkey, is_active, is_requestable,'.
' is_requesteeble, is_multiplicable, grant_group_id, request_group_id)'.
' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', undef,
$name, $description, $target_type,
scalar $cgi->param('sortkey'), scalar $cgi->param('is_active'),
scalar $cgi->param('is_requestable'), scalar $cgi->param('is_requesteeble'),
scalar $cgi->param('is_multiplicable'), scalar $cgi->param('grant_gid'),
scalar $cgi->param('request_gid')
);
# Get the ID of the new flag type.
my $id = $dbh->bz_last_key('flagtypes', 'id');
my $ft = Bugzilla::FlagType->create({
map { ($_ => scalar $cgi->param($_)) } (Bugzilla::FlagType->UPDATE_COLUMNS, 'cc_list')
});
# Populate the list of inclusions/exclusions for this flag type.
validateAndSubmit($id);
validateAndSubmit($ft->id);
$dbh->bz_commit_transaction();
Bugzilla->dbh->bz_commit_transaction;
$vars->{name} = $name;
$vars->{name} = $ft->name;
$vars->{message} = "flag_type_created";
delete_token($token);
@ -365,32 +348,20 @@ sub update
{
my $token = shift;
check_token_data($token, 'edit_flagtype');
my $flag_type = validateID();
my $id = $flag_type->id;
my $name = validateName();
my $description = validateDescription();
validateTargetType();
validateSortKey();
validateIsActive();
validateIsRequestable();
validateIsRequesteeble();
validateAllowMultiple();
validateGroups();
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
$dbh->bz_start_transaction();
$dbh->do(
'UPDATE flagtypes SET name = ?, description = ?, sortkey = ?, is_active = ?, is_requestable = ?,'.
' is_requesteeble = ?, is_multiplicable = ?, grant_group_id = ?, request_group_id = ?'.
' WHERE id = ?', undef,
$name, $description, scalar $cgi->param('sortkey'), scalar $cgi->param('is_active'),
scalar $cgi->param('is_requestable'), scalar $cgi->param('is_requesteeble'), scalar $cgi->param('is_multiplicable'),
scalar $cgi->param('grant_gid'), scalar $cgi->param('request_gid'), $id
);
my $flag_type = validateID();
for (Bugzilla::FlagType->UPDATE_COLUMNS, 'cc_list')
{
$flag_type->set($_, scalar $cgi->param($_));
}
$flag_type->update;
# Update the list of inclusions/exclusions for this flag type.
validateAndSubmit($id);
validateAndSubmit($flag_type->id);
$dbh->bz_commit_transaction();
@ -403,7 +374,7 @@ sub update
' AND (bugs.product_id = i.product_id OR i.product_id IS NULL)'.
' AND (bugs.component_id = i.component_id OR i.component_id IS NULL))'.
' WHERE flags.type_id = ? AND i.type_id IS NULL',
undef, $id
undef, $flag_type->id
);
Bugzilla::Flag->force_retarget($flag_ids);
@ -414,23 +385,23 @@ sub update
' WHERE flags.type_id = ?'.
' AND (bugs.product_id = e.product_id OR e.product_id IS NULL)'.
' AND (bugs.component_id = e.component_id OR e.component_id IS NULL)',
undef, $id
undef, $flag_type->id
);
Bugzilla::Flag->force_retarget($flag_ids);
# Now silently remove requestees from flags which are no longer
# specifically requestable.
if (!$cgi->param('is_requesteeble'))
if (!$flag_type->is_requesteeble)
{
$dbh->do('UPDATE flags SET requestee_id = NULL WHERE type_id = ?', undef, $id);
$dbh->do('UPDATE flags SET requestee_id = NULL WHERE type_id = ?', undef, $flag_type->id);
}
$vars->{name} = $name;
$vars->{name} = $flag_type->name;
$vars->{message} = "flag_type_changes_saved";
delete_token($token);
$vars->{bug_types} = Bugzilla::FlagType::match({'target_type' => 'bug'});
$vars->{attachment_types} = Bugzilla::FlagType::match({'target_type' => 'attachment'});
$vars->{bug_types} = Bugzilla::FlagType::match({ target_type => 'bug' });
$vars->{attachment_types} = Bugzilla::FlagType::match({ target_type => 'attachment' });
$template->process("admin/flag-type/list.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
@ -533,97 +504,6 @@ sub validateID
return $flag_type;
}
sub validateName
{
my $name = $cgi->param('name');
($name && $name !~ /[ ,]/ && length($name) <= 50)
|| ThrowUserError("flag_type_name_invalid", { name => $name });
trick_taint($name);
return $name;
}
sub validateDescription
{
my $description = $cgi->param('description');
length($description) < 2**16-1
|| ThrowUserError("flag_type_description_invalid");
trick_taint($description);
return $description;
}
sub validateProduct
{
my $product_name = shift;
return unless $product_name;
my $product = Bugzilla::Product::check_product($product_name);
return $product;
}
sub validateComponent
{
my ($product, $component_name) = @_;
return unless $component_name;
($product && $product->id)
|| ThrowUserError("flag_type_component_without_product");
my $component = Bugzilla::Component->check({ product => $product, name => $component_name });
return $component;
}
sub validateSortKey
{
# $sortkey is destroyed if detaint_natural fails.
my $sortkey = $cgi->param('sortkey');
detaint_natural($sortkey)
|| ThrowUserError("flag_type_sortkey_invalid", { sortkey => scalar $cgi->param('sortkey') });
$cgi->param('sortkey', $sortkey);
}
sub validateTargetType
{
grep($cgi->param('target_type') eq $_, ("bug", "attachment"))
|| ThrowCodeError("flag_type_target_type_invalid", { target_type => scalar $cgi->param('target_type') });
}
sub validateIsActive
{
$cgi->param('is_active', $cgi->param('is_active') ? 1 : 0);
}
sub validateIsRequestable
{
$cgi->param('is_requestable', $cgi->param('is_requestable') ? 1 : 0);
}
sub validateIsRequesteeble
{
$cgi->param('is_requesteeble', $cgi->param('is_requesteeble') ? 1 : 0);
}
sub validateAllowMultiple
{
$cgi->param('is_multiplicable', $cgi->param('is_multiplicable') ? 1 : 0);
}
sub validateGroups
{
my $dbh = Bugzilla->dbh;
# Convert group names to group IDs
foreach my $col ('grant', 'request')
{
my $name = $cgi->param($col . '_group');
if ($name)
{
trick_taint($name);
my $gid = $dbh->selectrow_array('SELECT id FROM groups WHERE name = ?', undef, $name);
$gid || ThrowUserError("group_unknown", { name => $name });
$cgi->param($col . '_gid', $gid);
}
}
}
# At this point, values either come the DB itself or have been recently
# added by the user and have passed all validation tests.
# The only way to have invalid product/component combinations is to
@ -666,16 +546,6 @@ sub validateAndSubmit
$sth->execute($id, $product_id, $component_id);
}
}
my $cc_list = Bugzilla::User->match({ login_name => [ split /[\s,]*,[\s,]*/, scalar $cgi->param('cc_list') ] });
$dbh->do("DELETE FROM flagtype_cc_list WHERE object_id=?", undef, $id);
if (@$cc_list)
{
$dbh->do(
"INSERT INTO flagtype_cc_list (object_id, value_id) VALUES ".
join(', ', map { "(?, ?)" } @$cc_list), undef, map { ($id, $_->id) } @$cc_list
);
}
}
sub filter_group

View File

@ -203,7 +203,7 @@
<td>
the group allowed to grant/deny flags of this type
(to allow all users to grant/deny these flags, select no group)<br>
[% PROCESS select selname = "grant_group" %]
[% PROCESS select selname = "grant_group_id" %]
</td>
</tr>
@ -213,7 +213,7 @@
if flags of this type are requestable, the group allowed to request them
(to allow all users to request these flags, select no group)<br>
Note that the request group alone has no effect if the grant group is not defined!<br>
[% PROCESS select selname = "request_group" %]
[% PROCESS select selname = "request_group_id" %]
</td>
</tr>