Bug 53617

Restricting custom field values to more than one value of controlling field


git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@389 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2009-09-07 16:47:01 +00:00
parent b553c8b25b
commit ed774aaf5f
10 changed files with 185 additions and 65 deletions

View File

@ -218,14 +218,12 @@ use constant FIELD_TABLE_SCHEMA => {
sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0},
isactive => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'TRUE'},
visibility_value_id => {TYPE => 'INT2'},
],
# 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'],
visibility_value_id_idx => ['visibility_value_id'],
],
};

View File

@ -17,9 +17,14 @@
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
# Vitaliy Filippov <vitalif@mail.ru>
use strict;
##############################################
# Class representing single value of a field #
##############################################
package Bugzilla::Field::Choice;
use base qw(Bugzilla::Object);
@ -28,7 +33,7 @@ use Bugzilla::Config qw(SetParam write_params);
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Field;
use Bugzilla::Util qw(trim detaint_natural);
use Bugzilla::Util qw(trim detaint_natural trick_taint diff_arrays);
use Scalar::Util qw(blessed);
@ -40,13 +45,11 @@ use constant DB_COLUMNS => qw(
id
value
sortkey
visibility_value_id
);
use constant UPDATE_COLUMNS => qw(
value
sortkey
visibility_value_id
);
use constant NAME_FIELD => 'value';
@ -57,7 +60,6 @@ use constant REQUIRED_CREATE_FIELDS => qw(value);
use constant VALIDATORS => {
value => \&_check_value,
sortkey => \&_check_sortkey,
visibility_value_id => \&_check_visibility_value_id,
};
use constant CLASS_MAP => {
@ -191,6 +193,7 @@ sub remove_from_db {
{ field => $self->field, value => $self });
}
$self->_check_if_controller();
$self->set_visibility_values([]);
$self->SUPER::remove_from_db();
}
@ -269,45 +272,85 @@ sub is_static {
sub controls_visibility_of_fields {
my $self = shift;
$self->{controls_visibility_of_fields} ||= Bugzilla::Field->match(
{ visibility_field_id => $self->field->id,
{ visibility_field_id => $self->field->id,
visibility_value_id => $self->id });
return $self->{controls_visibility_of_fields};
}
sub visibility_value {
my $self = shift;
if ($self->{visibility_value_id}) {
$self->{visibility_value} ||=
Bugzilla::Field::Choice->type($self->field->value_field)->new(
$self->{visibility_value_id});
}
return $self->{visibility_value};
}
sub controlled_values {
sub controlled_values
{
my $self = shift;
return $self->{controlled_values} if defined $self->{controlled_values};
my $fields = $self->field->controls_values_of;
my %controlled_values;
# TODO move this into Bugzilla::Field::Choice::match() with MATCH_JOIN
# but no MATCH_JOIN by now is available
foreach my $field (@$fields) {
$controlled_values{$field->name} =
Bugzilla::Field::Choice->type($field)
->match({ visibility_value_id => $self->id });
my $type = Bugzilla::Field::Choice->type($field);
my $sql =
"SELECT f.".join(", f.", @{$type->DB_COLUMNS}).
" FROM fieldvaluecontrol c, ".$type->DB_TABLE." f".
" WHERE c.field_id=? AND c.visibility_value_id=? AND c.value_id=f.id";
# Обходим самозарождение греха при конкатенации DB_COLUMNS и DB_TABLE
trick_taint($sql);
my $f = Bugzilla->dbh->selectall_arrayref($sql, {Slice=>{}}, $field->id, $self->id) || [];
$f = [ map { bless $_, $type } @$f ];
$controlled_values{$field->name} = $f;
}
$self->{controlled_values} = \%controlled_values;
return $self->{controlled_values};
}
sub visibility_values
{
my $self = shift;
my $f;
unless ($f = $self->{visibility_values})
{
# TODO move this into Bugzilla::Field::Choice::match() with MATCH_JOIN
# but no MATCH_JOIN by now is available
my $type = Bugzilla::Field::Choice->type($self->field->value_field);
my $sql =
"SELECT f.".join(", f.", $type->DB_COLUMNS).
" FROM fieldvaluecontrol c, ".$type->DB_TABLE." f".
" WHERE c.field_id=? AND c.visibility_value_id=f.id AND c.value_id=?";
# Обходим самозарождение греха при конкатенации DB_COLUMNS и DB_TABLE
trick_taint($sql);
$f = Bugzilla->dbh->selectall_arrayref($sql, {Slice=>{}}, $self->field->id, $self->id) || [];
$f = [ map { bless $_, $type } @$f ];
$self->{visibility_values} = $f;
}
return $f;
}
############
# Mutators #
############
sub set_name { $_[0]->set('value', $_[1]); }
sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
sub set_visibility_value {
my ($self, $value) = @_;
$self->set('visibility_value_id', $value);
delete $self->{visibility_value};
sub set_visibility_values
{
my $self = shift;
my ($value_ids) = @_;
return undef if !ref $value_ids || $value_ids !~ 'ARRAY';
my $type = Bugzilla::Field::Choice->type($self->field->value_field);
@$value_ids = map { $_ ? $type->check({ id => $_ }) : () } @$value_ids;
my ($a, $r) = diff_arrays([map { $_->id } @$value_ids], [map { $_->id } @{$self->visibility_values}]);
return undef unless @$a || @$r;
Bugzilla->dbh->do(
"DELETE FROM fieldvaluecontrol WHERE field_id=? AND value_id=?",
undef, $self->field->id, $self->id);
if (@$value_ids)
{
Bugzilla->dbh->do(
"INSERT INTO fieldvaluecontrol (field_id, value_id, visibility_value_id) VALUES ".
join(",", ("(?,?,?)") x @$value_ids),
undef, map { ($self->field->id, $self->id, $_->id) } @$value_ids);
}
delete $self->{visibility_values};
return @$value_ids;
}
##############
@ -355,16 +398,6 @@ sub _check_sortkey {
return $value;
}
sub _check_visibility_value_id {
my ($invocant, $value_id) = @_;
$value_id = trim($value_id);
my $field = $invocant->field->value_field;
return undef if !$field || !$value_id;
my $value_obj = Bugzilla::Field::Choice->type($field)
->check({ id => $value_id });
return $value_obj->id;
}
1;
__END__

View File

@ -553,8 +553,6 @@ sub update_table_definitions {
# 2008-09-07 LpSolit@gmail.com - Bug 452893
_fix_illegal_flag_modification_dates();
_add_visiblity_value_to_value_tables();
# 2009-03-02 arbingersys@gmail.com - Bug 423613
$dbh->bz_add_index('profiles', 'profiles_extern_id_idx',
{TYPE => 'UNIQUE', FIELDS => [qw(extern_id)]});
@ -3218,20 +3216,6 @@ sub _fix_illegal_flag_modification_dates {
print "$rows flags had an illegal modification date. Fixed!\n" if ($rows =~ /^\d+$/);
}
sub _add_visiblity_value_to_value_tables {
my $dbh = Bugzilla->dbh;
my @standard_fields =
qw(bug_status resolution priority bug_severity op_sys rep_platform);
my $custom_fields = $dbh->selectcol_arrayref(
'SELECT name FROM fielddefs WHERE custom = 1 AND type IN(?,?)',
undef, FIELD_TYPE_SINGLE_SELECT, FIELD_TYPE_MULTI_SELECT);
foreach my $field (@standard_fields, @$custom_fields) {
$dbh->bz_add_column($field, 'visibility_value_id', {TYPE => 'INT2'});
$dbh->bz_add_index($field, "${field}_visibility_value_id_idx",
['visibility_value_id']);
}
}
1;
__END__

View File

@ -119,11 +119,11 @@ if ($action eq 'new') {
check_token_data($token, 'add_field_value');
my $created_value = Bugzilla::Field::Choice->type($field)->create({
value => scalar $cgi->param('value'),
value => scalar $cgi->param('value'),
sortkey => scalar $cgi->param('sortkey'),
is_open => scalar $cgi->param('is_open'),
visibility_value_id => scalar $cgi->param('visibility_value_id'),
});
$created_value->set_visibility_values([ $cgi->param('visibility_value_id') ]);
delete_token($token);
@ -188,8 +188,9 @@ if ($action eq 'update') {
$vars->{'value_old'} = $value->name;
$value->set_name($cgi->param('value_new'));
$value->set_sortkey($cgi->param('sortkey'));
$value->set_visibility_value($cgi->param('visibility_value_id'));
$vars->{'changes'} = $value->update();
my $ch = $value->set_visibility_values([ $cgi->param('visibility_value_id') ]);
$vars->{'changes'}->{'visibility_values'} = $ch if defined $ch;
delete_token($token);
$vars->{'message'} = 'field_value_updated';
display_field_values($vars);

View File

@ -44,3 +44,17 @@ push @{$schema->{products}->{FIELDS}}, wiki_url => {TYPE => 'varchar(255)', NOTN
# Bug 53725 - Версия по умолчанию
push @{$schema->{components}->{FIELDS}}, default_version => {TYPE => 'varchar(64)', NOTNULL => 1, DEFAULT => "''"};
# Bug 53617 - Ограничение Custom Fields двумя и более значениями контролирующего поля
$schema->{fieldvaluecontrol} = {
FIELDS => [
field_id => {TYPE => 'INT3', NOTNULL => 1},
value_id => {TYPE => 'INT2', NOTNULL => 1},
visibility_value_id => {TYPE => 'INT2', NOTNULL => 1},
],
INDEXES => [
fieldvaluecontrol_primary_idx =>
{FIELDS => ['field_id', 'visibility_value_id', 'value_id'],
TYPE => 'UNIQUE'},
],
};

View File

@ -103,3 +103,24 @@ if (!$dbh->bz_column_info('components', 'default_version'))
{
$dbh->bz_add_column('components', default_version => {TYPE => 'varchar(64)', NOTNULL => 1, DEFAULT => "''"});
}
# Bug 53617 - Ограничение Custom Fields двумя и более значениями контролирующего поля
my @standard_fields =
qw(bug_status resolution priority bug_severity op_sys rep_platform);
my $custom_fields = $dbh->selectcol_arrayref(
'SELECT name FROM fielddefs WHERE custom = 1 AND type IN (?,?)',
undef, FIELD_TYPE_SINGLE_SELECT, FIELD_TYPE_MULTI_SELECT);
foreach my $field (@standard_fields, @$custom_fields)
{
next unless $dbh->bz_table_info($field);
if ($dbh->bz_column_info($field, 'visibility_value_id'))
{
print "Migrating $field visibility_value_id into fieldvaluecontrol\n";
$dbh->do(
"REPLACE INTO fieldvaluecontrol (field_id, visibility_value_id, value_id)".
" SELECT f.id, v.visibility_value_id, v.id FROM fielddefs f, $field v".
" WHERE f.name=? AND v.visibility_value_id IS NOT NULL", undef, $field);
# Пока не удаляем никаких колонок
#$dbh->bz_drop_column($field, 'visibility_value_id');
}
}

View File

@ -524,3 +524,5 @@ table.alcontent { width: 100%; }
#field_container_see_also #see_also { max-width: 500px; }
table.flag_table select { max-width: 400px; }
#visibility_values_td select { margin-bottom: 2px; }

View File

@ -70,7 +70,13 @@
</label>
</th>
<td>
<select name="visibility_value_id" id="visibility_value_id">
<input type="button" onclick="add_visibility_value()" value="Add an option" />
(Leave unset to have this value always appear.)
</td>
</tr>
<tr>
<td></td><td id="visibility_values_td">
<select name="visibility_value_id" id="visibility_value_id_0">
<option></option>
[% FOREACH field_value = field.value_field.legal_values %]
[% NEXT IF field_value.name == '' %]
@ -79,9 +85,31 @@
</option>
[% END %]
</select>
<small>(Leave unset to have this value always appear.)</small>
</td>
</tr>
<script language="JavaScript">
<!--
function add_visibility_value()
{
var td = document.getElementById('visibility_values_td');
var sel = document.getElementById('visibility_value_id_0');
var o;
var n = document.createElement('span');
n.innerHTML = ' or <br>';
td.appendChild(n);
n = document.createElement('select');
n.name = "visibility_value_id";
for (var i = 0; i < sel.options.length; i++)
{
o = document.createElement('option');
o.value = sel.options[i].value;
o.text = sel.options[i].text;
n.options.add(o);
}
td.appendChild(n);
}
//-->
</script>
[% END %]
</table>
<input type="submit" id="create" value="Add">

View File

@ -61,25 +61,58 @@
[% IF field.value_field %]
<tr>
<th align="right">
<label for="visibility_value_id">Only appears when
[%+ field.value_field.description FILTER html %] is set to:
</label>
Only appears when [%+ field.value_field.description FILTER html %] is set to:
</th>
<td>
<select name="visibility_value_id" id="visibility_value_id">
<input type="button" onclick="add_visibility_value()" value="Add an option" />
(Leave unset to have this value always appear.)
</td>
</tr>
[% vis_values = value.visibility_values %]
[% SET vis_values = [{ id => -1 }] IF !vis_values OR vis_values.size <= 0 %]
[% SET vv_i = 0 %]
<tr>
<td></td><td id="visibility_values_td">
[% FOR vv = vis_values %]
[% " or <br>" IF vv_i %]
<select name="visibility_value_id" id="visibility_value_id_[% vv_i %]">
<option></option>
[% FOREACH field_value = field.value_field.legal_values %]
[% NEXT IF field_value.name == '' %]
<option value="[% field_value.id FILTER none %]"
[% ' selected="selected"'
IF field_value.id == value.visibility_value.id %]>
IF field_value.id == vv.id %]>
[%- field_value.name FILTER html -%]
</option>
[% END %]
</select>
<small>(Leave unset to have this value always appear.)</small>
[% SET vv_i = vv_i + 1 %]
[% END %]
</td>
</tr>
<script language="JavaScript">
<!--
function add_visibility_value()
{
var td = document.getElementById('visibility_values_td');
var sel = document.getElementById('visibility_value_id_0');
var o;
var n = document.createElement('span');
n.innerHTML = ' or <br>';
td.appendChild(n);
n = document.createElement('select');
n.name = "visibility_value_id";
for (var i = 0; i < sel.options.length; i++)
{
o = document.createElement('option');
o.value = sel.options[i].value;
o.text = sel.options[i].text;
n.options.add(o);
}
td.appendChild(n);
}
//-->
</script>
[% END %]
</table>

View File

@ -328,11 +328,17 @@
<li>Sortkey updated to
<em>[% changes.sortkey.1 FILTER html %]</em>.</li>
[% END %]
[% IF changes.visibility_value_id %]
[% IF value.visibility_value.defined %]
[% IF changes.visibility_values.defined %]
[% IF value.visibility_values.size > 0 %]
<li>It only appears when
[%+ value.field.value_field.description FILTER html %] is set to
'[%+ value.visibility_value.name FILTER html %]'.</li>
[% SET vv_i = 0 %]
[% FOR vv = value.visibility_values %]
[% IF vv_i %] or [% END %]
'[%+ vv.name FILTER html %]'
[% SET vv_i = vv_i+1 %]
[% END %]
</li>
[% ELSE %]
<li>It now always appears, no matter what
[%+ value.field.value_field.description FILTER html %] is set to.