Add UI for editing visibilility/nullable/cloned/defaults from value pages

hinted-selects
Vitaliy Filippov 2014-07-11 18:06:58 +04:00
parent 04da6a883e
commit 82b268483c
17 changed files with 235 additions and 79 deletions

View File

@ -751,7 +751,7 @@ sub check_dependent_fields
if (!$nullable || !$self->id)
{
# Try to select default value
my $default = $field_obj->get_default($self);
my $default = $field_obj->get_default_value($self, 1);
if ($default)
{
$self->set($field_obj->name, $default);
@ -786,7 +786,7 @@ sub check_dependent_fields
if (!$nullable || !$self->id)
{
# Try to set default value
my $default = $field_obj->get_default($self);
my $default = $field_obj->get_default_value($self, 1);
if ($default)
{
$self->set($field_obj->name, $default);

View File

@ -586,7 +586,7 @@ use constant ABSTRACT_SCHEMA => {
default_value => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
],
INDEXES => [
fieldvaluecontrol_primary_idx => {FIELDS => ['field_id', 'visibility_value_id'], TYPE => 'PRIMARY'},
PRIMARY => {FIELDS => ['field_id', 'visibility_value_id']},
],
},
@ -1886,6 +1886,10 @@ sub get_add_index_ddl {
$index_type = '';
}
if (lc($name) eq 'primary') {
return "ALTER TABLE $table ADD PRIMARY KEY (".join(", ", @$index_fields).")";
}
return $self->_get_create_index_ddl($table, $name, $index_fields,
$index_type);
}
@ -1989,6 +1993,10 @@ sub get_drop_index_ddl {
my ($self, $table, $name) = @_;
if (lc($name) eq 'primary') {
return "ALTER TABLE $table DROP PRIMARY KEY";
}
# Although ANSI SQL-92 doesn't specify a method of dropping an index,
# many DBs support this syntax.
return ("DROP INDEX $name");

View File

@ -536,8 +536,6 @@ sub url { $_[0]->{url} }
sub default_value { $_[0]->{default_value} }
sub default_value_hash { $_[0]->is_select ? { map { $_ => 1 } split /,/, $_[0]->{default_value} } : undef }
sub value_type
{
my $self = shift;
@ -662,14 +660,14 @@ sub check_clone
}
# Get default value for this field in bug $bug
sub get_default
sub get_default_value
{
my $self = shift;
my ($bug) = @_;
my $default = $self->default_value;
my ($bug, $useGlobal) = @_;
my $default = $useGlobal ? $self->default_value : undef;
if ($self->default_field_id)
{
my $value = $bug->get_ids($self->default_field->name);
my $value = bug_or_hash_value($bug, $self->default_field);
my $d = Bugzilla->fieldvaluecontrol->{$self->default_field_id}
->{defaults}->{$self->id};
for (ref $value ? @$value : $value)
@ -685,6 +683,17 @@ sub get_default
return $default;
}
sub default_value_hash { $_[0]->is_select ? { map { $_ => 1 } split /,/, $_[0]->{default_value} } : undef }
sub default_value_hash_for
{
my ($self, $visibility_value_id) = @_;
return undef if !$self->is_select;
return { map { $_ => 1 } split /,/, Bugzilla->fieldvaluecontrol
->{$self->default_field_id}->{defaults}
->{$self->id}->{$visibility_value_id} };
}
=pod
=over
@ -1274,10 +1283,12 @@ sub bug_or_hash_value
my $value;
if (blessed $bug)
{
# Bug object
$value = $bug->get_ids($vf->name);
}
else
elsif (ref $bug)
{
# Hashref with value names
$value = $bug->{$vf->name};
if (!ref $value)
{
@ -1288,6 +1299,11 @@ sub bug_or_hash_value
$value = $value->id if $value;
}
}
else
{
# Just value ID
$value = $bug;
}
return $value;
}
@ -1344,6 +1360,7 @@ sub update_visibility_values
sub toggle_value
{
my ($f, $v, $vv, $enable) = @_;
$f = $f->id if ref $f;
my ($any, $this) = Bugzilla->dbh->selectrow_array(
'SELECT COUNT(*), SUM(visibility_value_id=?) FROM fieldvaluecontrol WHERE field_id=? AND value_id=?', undef, $vv, $f, $v
);
@ -1376,6 +1393,30 @@ sub toggle_value
return undef;
}
# FIXME: This issues A LOT OF sql queries and should be optimized.
sub update_control_lists
{
my ($controlling_field_id, $controlling_value_id, $params) = @_;
$controlling_field_id = $controlling_field_id->id if ref $controlling_field_id;
for my $f (Bugzilla->get_fields({ obsolete => 0, visibility_field_id => $controlling_field_id }))
{
$f->toggle_value(FLAG_VISIBLE, $controlling_value_id, $params->{'is_visible_'.$f->name} ? 1 : 0);
}
for my $f (Bugzilla->get_fields({ obsolete => 0, null_field_id => $controlling_field_id }))
{
$f->toggle_value(FLAG_NULLABLE, $controlling_value_id, $params->{'is_nullable_'.$f->name} ? 1 : 0);
}
for my $f (Bugzilla->get_fields({ obsolete => 0, clone_field_id => $controlling_field_id }))
{
$f->toggle_value(FLAG_CLONED, $controlling_value_id, $params->{'is_cloned_'.$f->name} ? 1 : 0);
}
for my $f (Bugzilla->get_fields({ obsolete => 0, default_field_id => $controlling_field_id }))
{
next if $f eq 'version'; # FIXME: default version is hardcoded to depend on component
$f->update_default_values($controlling_value_id, $params->{'default_'.$f->name});
}
}
sub update_controlled_values
{
my ($controlled_field, $controlled_value_ids, $visibility_value_id) = @_;
@ -1419,6 +1460,7 @@ sub update_default_values
}
else
{
trick_taint($default_value);
Bugzilla->dbh->do(
'REPLACE INTO field_defaults (field_id, visibility_value_id, default_value) VALUES (?, ?, ?)',
undef, $controlled_field->id, $visibility_value_id, $default_value

View File

@ -689,8 +689,6 @@ sub _check_votes
# Implement Bugzilla::Field::Choice #
#####################################
sub field { Bugzilla->get_field('product') }
use constant is_default => 0;
###############################

View File

@ -41,6 +41,8 @@ my $vars = {};
# so all actions point to the same page.
$vars->{'doc_section'} = 'components.html';
my $ARGS = { %{ $cgi->Vars } };
#
# Preliminary checks:
#
@ -243,6 +245,8 @@ if ($action eq 'update') {
$component->set_is_active(scalar $cgi->param('is_active'));
my $changes = $component->update();
$component->field->update_control_lists($component->id, $ARGS);
$vars->{'message'} = 'component_updated';
$vars->{'comp'} = $component;
$vars->{'product'} = $product;

View File

@ -37,6 +37,8 @@ my $vars = {};
# so all actions point to the same page.
$vars->{'doc_section'} = 'milestones.html';
my $ARGS = { %{ $cgi->Vars } };
#
# Preliminary checks:
#
@ -209,6 +211,8 @@ if ($action eq 'update') {
$milestone->set_is_active($isactive);
my $changes = $milestone->update();
$milestone->field->update_control_lists($milestone->id, $ARGS);
delete_token($token);
$vars->{'message'} = 'milestone_updated';

View File

@ -49,6 +49,8 @@ my $vars = {};
# improved and each action has its own section.
$vars->{doc_section} = 'products.html';
my $ARGS = { %{ $cgi->Vars } };
$user->in_group('editcomponents') ||
scalar(@{$user->get_editable_products}) ||
ThrowUserError('auth_failure', {
@ -281,26 +283,6 @@ if ($action eq 'edit' || (!$action && $product_name))
}
$vars->{product} = $product;
$vars->{token} = issue_session_token('edit_product');
my $controlled_fields = { map { $_->id => ($_->has_visibility_value($product) ? $_->description : undef) } @{ $product->field->controls_visibility_of() } };
for my $i (keys %$controlled_fields)
{
if (!defined($controlled_fields->{$i}))
{
delete $controlled_fields->{$i};
}
}
my $length = values %$controlled_fields;
$vars->{controlled_field_names} = $length > 0 ? join(', ', values %$controlled_fields) : 'No fields';
for (qw(version target_milestone))
{
my $f = Bugzilla->get_field($_);
if ($vars->{'allow_nullable_'.$f->name} = $f->nullable)
{
my $null = $f->null_visibility_values;
$vars->{$f->name.'_is_nullable'} = !$null || $null->{$product->id};
}
}
$vars->{all_groups} = [ map { $_->name } Bugzilla::Group->get_all ];
$template->process('admin/products/edit.html.tmpl', $vars)
@ -340,17 +322,7 @@ if ($action eq 'update')
my $changes = $product->update();
for (qw(version target_milestone))
{
my $f = Bugzilla->get_field($_);
if ($f->nullable)
{
my $c = Bugzilla::Field::toggle_value(
$f->id, -1, $product->id, scalar $cgi->param($f->name.'_is_nullable')
);
$changes->{$f->name.'_is_nullable'} = [ !$c, $c ] if defined $c;
}
}
$product->field->update_control_lists($product->id, $ARGS);
delete_token($token);

View File

@ -138,9 +138,8 @@ if ($action eq 'control_list')
if ($visibility_value_id)
{
$vars->{visibility_value_id} = $visibility_value_id;
$vars->{default_value_hash} = { map { $_ => 1 } split /,/, Bugzilla->fieldvaluecontrol
->{$field->value_field_id}->{defaults}->{$field->id}->{$visibility_value_id} };
my %values = map { $_->{id} => $_ } @{$field->{value_field}->legal_values()};
$vars->{default_value_hash} = $field->default_value_hash_for($visibility_value_id);
my %values = map { $_->{id} => $_ } @{$field->value_field->legal_values};
$vars->{field_value} = $values{$visibility_value_id};
$step++ unless $token;
$need_token = 1;
@ -148,9 +147,12 @@ if ($action eq 'control_list')
{
check_token_data($token, "edit_control_list");
$field->update_controlled_values($values, $visibility_value_id);
$field->update_default_values($visibility_value_id, $field->type == FIELD_TYPE_MULTI_SELECT
? [ $cgi->param('default_value') ]
: scalar $cgi->param('default_value'));
if ($field->default_field_id == $field->value_field_id)
{
$field->update_default_values($visibility_value_id, $field->type == FIELD_TYPE_MULTI_SELECT
? [ $cgi->param('default_value') ]
: scalar $cgi->param('default_value'));
}
$step++;
$need_token = 0;
delete_token($token);

View File

@ -41,6 +41,8 @@ my $vars = {};
# so all actions point to the same page.
$vars->{'doc_section'} = 'versions.html';
my $ARGS = { %{ $cgi->Vars } };
#
# Preliminary checks:
#
@ -200,6 +202,8 @@ if ($action eq 'update') {
$version->set_is_active($isactive);
my $changes = $version->update();
$version->field->update_control_lists($version->id, $ARGS);
$dbh->bz_commit_transaction();
delete_token($token);

View File

@ -143,6 +143,7 @@
[% END %]
</td>
</tr>
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=comp.field this_value_id=comp.id %]
[% END %]
</table>

View File

@ -252,7 +252,9 @@ var constants = {
</td>
</tr>
[% END %]
[% IF field.is_select || !field.id %]
[%# FIXME These fields are still handled differently... %]
[% IF field.name != 'version' && field.name != 'target_milestone' &&
field.name != 'component' && field.is_select || !field.id %]
<tr valign="top" id="value_field_row">
<th colspan="2" align="left"><label for="value_field_id">
Field that controls the values that appear in this field:

View File

@ -0,0 +1,141 @@
[%# Dependent field lists (visible, nullable, cloneable, default values) for a controlling value
# License: Dual-license GPL 3.0+ or MPL 1.1+
# Author: (c) Vitaliy Filippov 2014 %]
[% IF this_field && this_value_id %]
[% SET any_field = 0 %]
[% FOR f = Bugzilla.get_fields({ obsolete => 0, visibility_field_id => this_field.id, sort => 1 }) %]
[% IF NOT any_field %]
<tr>
<th align="left" valign="top">
Visible fields:
</th>
<td>
Show these fields in this [% this_field.description | html %]:<br />
[% SET any_field = 1 %]
[% ELSE %]
<br />
[% END %]
<input type="checkbox" name="is_visible_[% f.name | html %]" id="is_visible_[% f.name | html %]" value="1"
[% " checked" IF f.has_visibility_value(this_value_id) %] />
<label for="is_visible_[% f.name | html %]">[% f.description | html %]</label>
[% END %]
[% IF any_field %]
</td>
</tr>
[% END %]
[% SET any_field = 0 %]
[% FOR f = Bugzilla.get_fields({ obsolete => 0, value_field_id => this_field.id, sort => 1 }) %]
[% IF f.name != 'version' && f.name != 'target_milestone' && f.name != 'component' &&
(f.visibility_field_id != f.value_field_id || f.has_visibility_value(this_value_id)) %]
[% IF NOT any_field %]
<tr>
<th align="left" valign="top">
Edit values:
</th>
<td>
Edit values of dependent fields for this [% this_field.description | html %]:<br />
[% SET any_field = 1 %]
[% ELSE %],
[% END %]
<a href="editvalues.cgi?action=control_list&field=[% f.name | html %]&visibility_value_id=[% this_value_id %]">[% f.description | html %]</a>
[% END %]
[% END %]
[% IF any_field %]
</td>
</tr>
[% END %]
[% SET any_field = 0 %]
[% FOR f = Bugzilla.get_fields({ obsolete => 0, null_field_id => this_field.id, sort => 1 }) %]
[% IF f.visibility_field_id != f.null_field_id || f.has_visibility_value(this_value_id) %]
[% IF NOT any_field %]
<tr>
<th align="left" valign="top">
Allow empty values:
</th>
<td>
Allow these fields to be empty in this [% this_field.description | html %]:<br />
[% SET any_field = 1 %]
[% ELSE %]
<br />
[% END %]
<input type="checkbox" name="is_nullable_[% f.name | html %]" id="is_nullable_[% f.name | html %]" value="1"
[% " checked" IF f.check_is_nullable(this_value_id) %] />
<label for="is_nullable_[% f.name | html %]">[% f.description | html %]</label>
[% END %]
[% END %]
[% IF any_field %]
</td>
</tr>
[% END %]
[% SET any_field = 0 %]
[% FOR f = Bugzilla.get_fields({ obsolete => 0, clone_field_id => this_field.id, sort => 1 }) %]
[% IF f.visibility_field_id != f.clone_field_id || f.has_visibility_value(this_value_id) %]
[% IF NOT any_field %]
<tr>
<th align="left" valign="top">
Clone fields:
</th>
<td>
Copy these fields when cloning from this [% this_field.description | html %]:<br />
[% SET any_field = 1 %]
[% ELSE %]
<br />
[% END %]
<input type="checkbox" name="is_cloned_[% f.name | html %]" id="is_cloned_[% f.name | html %]" value="1"
[% " checked" IF f.check_clone(this_value_id) %] />
<label for="is_cloned_[% f.name | html %]">[% f.description | html %]</label>
[% END %]
[% END %]
[% IF any_field %]
</td>
</tr>
[% END %]
[% SET any_field = 0 %]
[% FOR f = Bugzilla.get_fields({ obsolete => 0, default_field_id => this_field.id, sort => 1 }) %]
[%# FIXME: default version is hardcoded to depend on component %]
[% IF f.name != 'version' && (f.visibility_field_id != f.default_field_id || f.has_visibility_value(this_value_id)) %]
[% IF NOT any_field %]
<tr>
<th align="left" valign="top">
Default values:
</th>
<td>
Override default field values for new and moved [% terms.bugs %] in this [% this_field.description | html %]:<br />
<table style="border-collapse: collapse">
[% SET any_field = 1 %]
[% END %]
<tr>
<td><label for="default_[% f.name | html %]">[% f.description | html %]:</label></td>
<td>
[% IF f.is_select %]
[% SET cur_default = f.default_value_hash_for(this_value_id) %]
<select name="default_[% f.name | html %]" id="default_[% f.name | html %]"
[% IF f.type == constants.FIELD_TYPE_MULTI_SELECT %] multiple size="5"[% END %]>[%# FIXME array[] %]
<option value="">---</option>
[% SET vs = f.value_field_id == f.default_field_id ? f.restricted_legal_values(this_value_id) : f.legal_values %]
[% FOR v = vs %]
<option value="[% v.id %]"[% " selected" IF cur_default.${v.id} %]>[% v.name | html %]</option>
[% END %]
</select>
[% ELSIF f.type == constants.FIELD_TYPE_TEXTAREA %]
<textarea cols="50" rows="5" name="default_[% f.name | html %]" id="default_[% f.name | html %]">[% f.get_default_value(this_value_id) | html %]</textarea>
[% ELSE %]
<input type="text" name="default_[% f.name | html %]" id="default_[% f.name | html %]" value="[% f.get_default_value(this_value_id) | html %]" />
[% END %]
</td>
</tr>
[% END %]
[% END %]
[% IF any_field %]
</table>
</td>
</tr>
[% END %]
[% END %]

View File

@ -38,7 +38,7 @@
<input type="hidden" name="visibility_value_id" value="[% visibility_value_id FILTER html %]"/>
<p>Значения <strong>[% field.description FILTER html %]</strong> для <strong>[% field_value.name FILTER html %]</strong>:</p>
[% IF field.visibility_field_id == field.value_field_id %] [%# FIXME should be default_field_id %]
[% IF field.default_field_id == field.value_field_id %]
<p>Значение по умолчанию:[% "<br />" IF field.type == constants.FIELD_TYPE_MULTI_SELECT %]
[% IF field.is_select %]
<select name="default_value" style="width: 400px" [% "multiple size=3" IF field.type == constants.FIELD_TYPE_MULTI_SELECT %]>

View File

@ -94,6 +94,7 @@
</td>
</tr>
[% END %]
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=field this_value_id=value.id %]
</tr>
[% Hook.process('fields') %]
</table>

View File

@ -36,6 +36,7 @@
<th class="field_label"><label for="isactive">Enabled For [% terms.Bugs %]:</label></th>
<td><input id="isactive" name="isactive" type="checkbox" value="1" [% 'checked="checked"' IF milestone.isactive %]></td>
</tr>
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=milestone.field this_value_id=milestone.id %]
[% END %]
</table>

View File

@ -79,26 +79,6 @@
[% END %]
</td>
</tr>
[% IF allow_nullable_version %]
<tr>
<th align="left" valign="top">
<label for="version_is_nullable">Allow empty Version:</label>
</th>
<td>
<input type="checkbox" name="version_is_nullable" id="version_is_nullable" value="1" [% " checked" IF version_is_nullable %] />
</td>
</tr>
[% END %]
[% IF allow_nullable_target_milestone %]
<tr>
<th align="left" valign="top">
<label for="target_milestone_is_nullable">Allow empty Target Milestone:</label>
</th>
<td>
<input type="checkbox" name="target_milestone_is_nullable" id="target_milestone_is_nullable" value="1" [% " checked" IF target_milestone_is_nullable %] />
</td>
</tr>
[% END %]
[% IF Param('usetargetmilestone') %]
<tr>
<th align="left" valign="top">
@ -117,14 +97,9 @@
</td>
</tr>
[% END %]
<tr>
<th align="left">
<a href="editvisibility.cgi?field=product&value=[% product.name FILTER url_quote %]">
Active custom fields:
</a>
</th>
<td>[% controlled_field_names %]</td>
</tr>
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=product.field this_value_id=product.id %]
<tr>
<th align="left" valign="top">
<a href="editproducts.cgi?action=editgroupcontrols&product=

View File

@ -32,6 +32,7 @@
<th class="field_label"><label for="isactive">Enabled For [% terms.Bugs %]:</label></th>
<td><input id="isactive" name="isactive" type="checkbox" value="1" [% 'checked="checked"' IF version.isactive %]></td>
</tr>
[% INCLUDE "admin/fieldvalues/control-list-common.html.tmpl" this_field=version.field this_value_id=version.id %]
[% END %]
</table>