Commit 3cea9188 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 291433: Ability to have custom fields whose visibility depends on the values of other fields

Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=bbaetz, a=mkanat
parent e0da20ba
...@@ -1209,12 +1209,16 @@ sub _check_references { ...@@ -1209,12 +1209,16 @@ sub _check_references {
my $foreign_table = $fk->{TABLE}; my $foreign_table = $fk->{TABLE};
my $foreign_column = $fk->{COLUMN}; my $foreign_column = $fk->{COLUMN};
# We use table aliases because sometimes we join a table to itself,
# and we can't use the same table name on both sides of the join.
# We also can't use the words "table" or "foreign" because those are
# reserved words.
my $bad_values = $self->selectcol_arrayref( my $bad_values = $self->selectcol_arrayref(
"SELECT DISTINCT $table.$column "SELECT DISTINCT tabl.$column
FROM $table LEFT JOIN $foreign_table FROM $table AS tabl LEFT JOIN $foreign_table AS forn
ON $table.$column = $foreign_table.$foreign_column ON tabl.$column = forn.$foreign_column
WHERE $foreign_table.$foreign_column IS NULL WHERE forn.$foreign_column IS NULL
AND $table.$column IS NOT NULL"); AND tabl.$column IS NOT NULL");
if (@$bad_values) { if (@$bad_values) {
my $delete_action = $fk->{DELETE} || ''; my $delete_action = $fk->{DELETE} || '';
......
...@@ -631,6 +631,10 @@ use constant ABSTRACT_SCHEMA => { ...@@ -631,6 +631,10 @@ use constant ABSTRACT_SCHEMA => {
DEFAULT => 'FALSE'}, DEFAULT => 'FALSE'},
enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'}, DEFAULT => 'FALSE'},
visibility_field_id => {TYPE => 'INT3',
REFERENCES => {TABLE => 'fielddefs',
COLUMN => 'id'}},
visibility_value_id => {TYPE => 'INT2'},
], ],
INDEXES => [ INDEXES => [
fielddefs_name_idx => {FIELDS => ['name'], fielddefs_name_idx => {FIELDS => ['name'],
......
...@@ -77,6 +77,8 @@ use Bugzilla::Constants; ...@@ -77,6 +77,8 @@ use Bugzilla::Constants;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Util; use Bugzilla::Util;
use Scalar::Util qw(blessed);
############################### ###############################
#### Initialization #### #### Initialization ####
############################### ###############################
...@@ -84,16 +86,18 @@ use Bugzilla::Util; ...@@ -84,16 +86,18 @@ use Bugzilla::Util;
use constant DB_TABLE => 'fielddefs'; use constant DB_TABLE => 'fielddefs';
use constant LIST_ORDER => 'sortkey, name'; use constant LIST_ORDER => 'sortkey, name';
use constant DB_COLUMNS => ( use constant DB_COLUMNS => qw(
'id', id
'name', name
'description', description
'type', type
'custom', custom
'mailhead', mailhead
'sortkey', sortkey
'obsolete', obsolete
'enter_bug', enter_bug
visibility_field_id
visibility_value_id
); );
use constant REQUIRED_CREATE_FIELDS => qw(name description); use constant REQUIRED_CREATE_FIELDS => qw(name description);
...@@ -106,6 +110,11 @@ use constant VALIDATORS => { ...@@ -106,6 +110,11 @@ use constant VALIDATORS => {
obsolete => \&_check_obsolete, obsolete => \&_check_obsolete,
sortkey => \&_check_sortkey, sortkey => \&_check_sortkey,
type => \&_check_type, type => \&_check_type,
visibility_field_id => \&_check_control_field,
};
use constant UPDATE_VALIDATORS => {
visibility_value_id => \&_check_control_value,
}; };
use constant UPDATE_COLUMNS => qw( use constant UPDATE_COLUMNS => qw(
...@@ -114,6 +123,8 @@ use constant UPDATE_COLUMNS => qw( ...@@ -114,6 +123,8 @@ use constant UPDATE_COLUMNS => qw(
sortkey sortkey
obsolete obsolete
enter_bug enter_bug
visibility_field_id
visibility_value_id
); );
# How various field types translate into SQL data definitions. # How various field types translate into SQL data definitions.
...@@ -259,6 +270,37 @@ sub _check_type { ...@@ -259,6 +270,37 @@ sub _check_type {
return $type; return $type;
} }
sub _check_control_field {
my ($invocant, $field_id) = @_;
$field_id = trim($field_id);
return undef if !$field_id;
my $field = Bugzilla::Field->check({ id => $field_id });
if (blessed($invocant) && $field->id == $invocant->id) {
ThrowUserError('field_cant_control_self', { field => $field });
}
if (!$field->is_select) {
ThrowUserError('field_control_must_be_select',
{ field => $field });
}
return $field->id;
}
sub _check_control_value {
my ($invocant, $value_id, $field_id) = @_;
my $field;
if (blessed($invocant)) {
$field = $invocant->visibility_field;
}
elsif ($field_id) {
$field = $invocant->new($field_id);
}
# When no field is set, no value is set.
return undef if !$field;
my $value_obj = Bugzilla::Field::Choice->type($field)
->check({ id => $value_id });
return $value_obj->id;
}
=pod =pod
=head2 Instance Properties =head2 Instance Properties
...@@ -362,6 +404,11 @@ sub enter_bug { return $_[0]->{enter_bug} } ...@@ -362,6 +404,11 @@ sub enter_bug { return $_[0]->{enter_bug} }
=over =over
=item C<is_select>
True if this is a C<FIELD_TYPE_SINGLE_SELECT> or C<FIELD_TYPE_MULTI_SELECT>
field. It is only safe to call L</legal_values> if this is true.
=item C<legal_values> =item C<legal_values>
Valid values for this field, as an array of L<Bugzilla::Field::Choice> Valid values for this field, as an array of L<Bugzilla::Field::Choice>
...@@ -371,6 +418,11 @@ objects. ...@@ -371,6 +418,11 @@ objects.
=cut =cut
sub is_select {
return ($_[0]->type == FIELD_TYPE_SINGLE_SELECT
|| $_[0]->type == FIELD_TYPE_MULTI_SELECT) ? 1 : 0
}
sub legal_values { sub legal_values {
my $self = shift; my $self = shift;
...@@ -384,6 +436,76 @@ sub legal_values { ...@@ -384,6 +436,76 @@ sub legal_values {
=pod =pod
=over
=item C<visibility_field>
What field controls this field's visibility? Returns a C<Bugzilla::Field>
object representing the field that controls this field's visibility.
Returns undef if there is no field that controls this field's visibility.
=back
=cut
sub visibility_field {
my $self = shift;
if ($self->{visibility_field_id}) {
$self->{visibility_field} ||=
$self->new($self->{visibility_field_id});
}
return $self->{visibility_field};
}
=pod
=over
=item C<visibility_value>
If we have a L</visibility_field>, then what value does that field have to
be set to in order to show this field? Returns a L<Bugzilla::Field::Choice>
or undef if there is no C<visibility_field> set.
=back
=cut
sub visibility_value {
my $self = shift;
if ($self->{visibility_field_id}) {
require Bugzilla::Field::Choice;
$self->{visibility_value} ||=
Bugzilla::Field::Choice->type($self->visibility_field)->new(
$self->{visibility_value_id});
}
return $self->{visibility_value};
}
=pod
=over
=item C<controls_visibility_of>
An arrayref of C<Bugzilla::Field> objects, representing fields that this
field controls the visibility of.
=back
=cut
sub controls_visibility_of {
my $self = shift;
$self->{controls_visibility_of} ||=
Bugzilla::Field->match({ visibility_field_id => $self->id });
return $self->{controls_visibility_of};
}
=pod
=head2 Instance Mutators =head2 Instance Mutators
These set the particular field that they are named after. These set the particular field that they are named after.
...@@ -404,6 +526,10 @@ They will throw an error if you try to set the values to something invalid. ...@@ -404,6 +526,10 @@ They will throw an error if you try to set the values to something invalid.
=item C<set_in_new_bugmail> =item C<set_in_new_bugmail>
=item C<set_visibility_field>
=item C<set_visibility_value>
=back =back
=cut =cut
...@@ -413,6 +539,17 @@ sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); } ...@@ -413,6 +539,17 @@ sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); }
sub set_obsolete { $_[0]->set('obsolete', $_[1]); } sub set_obsolete { $_[0]->set('obsolete', $_[1]); }
sub set_sortkey { $_[0]->set('sortkey', $_[1]); } sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
sub set_in_new_bugmail { $_[0]->set('mailhead', $_[1]); } sub set_in_new_bugmail { $_[0]->set('mailhead', $_[1]); }
sub set_visibility_field {
my ($self, $value) = @_;
$self->set('visibility_field_id', $value);
delete $self->{visibility_field};
delete $self->{visibility_value};
}
sub set_visibility_value {
my ($self, $value) = @_;
$self->set('visibility_value_id', $value);
delete $self->{visibility_value};
}
=pod =pod
...@@ -487,9 +624,7 @@ sub remove_from_db { ...@@ -487,9 +624,7 @@ sub remove_from_db {
$dbh->bz_drop_column('bugs', $name); $dbh->bz_drop_column('bugs', $name);
} }
if ($type == FIELD_TYPE_SINGLE_SELECT if ($self->is_select) {
|| $type == FIELD_TYPE_MULTI_SELECT)
{
# Delete the table that holds the legal values for this field. # Delete the table that holds the legal values for this field.
$dbh->bz_drop_field_tables($self); $dbh->bz_drop_field_tables($self);
} }
...@@ -545,9 +680,7 @@ sub create { ...@@ -545,9 +680,7 @@ sub create {
$dbh->bz_add_column('bugs', $name, SQL_DEFINITIONS->{$type}); $dbh->bz_add_column('bugs', $name, SQL_DEFINITIONS->{$type});
} }
if ($type == FIELD_TYPE_SINGLE_SELECT if ($field->is_select) {
|| $type == FIELD_TYPE_MULTI_SELECT)
{
# Create the table that holds the legal values for this field. # Create the table that holds the legal values for this field.
$dbh->bz_add_field_tables($field); $dbh->bz_add_field_tables($field);
} }
...@@ -572,6 +705,10 @@ sub run_create_validators { ...@@ -572,6 +705,10 @@ sub run_create_validators {
"SELECT MAX(sortkey) + 100 FROM fielddefs") || 100; "SELECT MAX(sortkey) + 100 FROM fielddefs") || 100;
} }
$params->{visibility_value_id} =
$class->_check_control_value($params->{visibility_value_id},
$params->{visibility_field_id});
return $params; return $params;
} }
......
...@@ -186,6 +186,10 @@ sub remove_from_db { ...@@ -186,6 +186,10 @@ sub remove_from_db {
ThrowUserError("fieldvalue_still_has_bugs", ThrowUserError("fieldvalue_still_has_bugs",
{ field => $self->field, value => $self }); { field => $self->field, value => $self });
} }
if (my @vis_fields = @{ $self->controls_visibility_of_fields }) {
ThrowUserError('fieldvalue_is_controller',
{ value => $self, fields => [map($_->name, @vis_fields)] });
}
$self->SUPER::remove_from_db(); $self->SUPER::remove_from_db();
} }
...@@ -248,6 +252,14 @@ sub is_static { ...@@ -248,6 +252,14 @@ sub is_static {
return 0; return 0;
} }
sub controls_visibility_of_fields {
my $self = shift;
$self->{controls_visibility_of_fields} ||= Bugzilla::Field->match(
{ visibility_field_id => $self->field->id,
visibility_value_id => $self->id });
return $self->{controls_visibility_of_fields};
}
############ ############
# Mutators # # Mutators #
############ ############
......
...@@ -86,6 +86,9 @@ sub update_fielddefs_definition { ...@@ -86,6 +86,9 @@ sub update_fielddefs_definition {
} }
} }
$dbh->bz_add_column('fielddefs', 'visibility_field_id', {TYPE => 'INT3'});
$dbh->bz_add_column('fielddefs', 'visibility_value_id', {TYPE => 'INT2'});
# Remember, this is not the function for adding general table changes. # Remember, this is not the function for adding general table changes.
# That is below. Add new changes to the fielddefs table above this # That is below. Add new changes to the fielddefs table above this
# comment. # comment.
......
...@@ -117,12 +117,10 @@ sub check { ...@@ -117,12 +117,10 @@ sub check {
if (!ref $param) { if (!ref $param) {
$param = { name => $param }; $param = { name => $param };
} }
# Don't allow empty names. # Don't allow empty names or ids.
if (exists $param->{name}) { my $check_param = exists $param->{id} ? $param->{id} : $param->{name};
$param->{name} = trim($param->{name}); $check_param = trim($check_param);
$param->{name} || ThrowUserError('object_name_not_specified', $check_param || ThrowUserError('object_not_specified', { class => $class });
{ class => $class });
}
my $obj = $class->new($param) my $obj = $class->new($param)
|| ThrowUserError('object_does_not_exist', {%$param, class => $class}); || ThrowUserError('object_does_not_exist', {%$param, class => $class});
return $obj; return $obj;
......
...@@ -49,7 +49,7 @@ use base qw(Exporter); ...@@ -49,7 +49,7 @@ use base qw(Exporter);
# have to fix it here. # have to fix it here.
use constant WS_ERROR_CODE => { use constant WS_ERROR_CODE => {
# Generic Bugzilla::Object errors are 50-99. # Generic Bugzilla::Object errors are 50-99.
object_name_not_specified => 50, object_not_specified => 50,
param_required => 50, param_required => 50,
object_does_not_exist => 51, object_does_not_exist => 51,
# Bug errors usually occupy the 100-200 range. # Bug errors usually occupy the 100-200 range.
......
...@@ -56,8 +56,7 @@ $vars->{'keyword'} = [map($_->name, Bugzilla::Keyword->get_all)]; ...@@ -56,8 +56,7 @@ $vars->{'keyword'} = [map($_->name, Bugzilla::Keyword->get_all)];
$vars->{'resolution'} = get_legal_field_values('resolution'); $vars->{'resolution'} = get_legal_field_values('resolution');
$vars->{'status'} = get_legal_field_values('bug_status'); $vars->{'status'} = get_legal_field_values('bug_status');
$vars->{'custom_fields'} = $vars->{'custom_fields'} =
[ grep {$_->type == FIELD_TYPE_SINGLE_SELECT || $_->type == FIELD_TYPE_MULTI_SELECT} [ grep {$_->is_select} Bugzilla->active_custom_fields ];
Bugzilla->active_custom_fields ];
# Include a list of product objects. # Include a list of product objects.
if ($cgi->param('product')) { if ($cgi->param('product')) {
......
...@@ -55,7 +55,6 @@ elsif ($action eq 'add') { ...@@ -55,7 +55,6 @@ elsif ($action eq 'add') {
} }
elsif ($action eq 'new') { elsif ($action eq 'new') {
check_token_data($token, 'add_field'); check_token_data($token, 'add_field');
$vars->{'field'} = Bugzilla::Field->create({ $vars->{'field'} = Bugzilla::Field->create({
name => scalar $cgi->param('name'), name => scalar $cgi->param('name'),
description => scalar $cgi->param('desc'), description => scalar $cgi->param('desc'),
...@@ -65,6 +64,8 @@ elsif ($action eq 'new') { ...@@ -65,6 +64,8 @@ elsif ($action eq 'new') {
enter_bug => scalar $cgi->param('enter_bug'), enter_bug => scalar $cgi->param('enter_bug'),
obsolete => scalar $cgi->param('obsolete'), obsolete => scalar $cgi->param('obsolete'),
custom => 1, custom => 1,
visibility_field_id => scalar $cgi->param('visibility_field_id'),
visibility_value_id => scalar $cgi->param('visibility_value_id'),
}); });
delete_token($token); delete_token($token);
...@@ -107,6 +108,8 @@ elsif ($action eq 'update') { ...@@ -107,6 +108,8 @@ elsif ($action eq 'update') {
$field->set_in_new_bugmail($cgi->param('new_bugmail')); $field->set_in_new_bugmail($cgi->param('new_bugmail'));
$field->set_enter_bug($cgi->param('enter_bug')); $field->set_enter_bug($cgi->param('enter_bug'));
$field->set_obsolete($cgi->param('obsolete')); $field->set_obsolete($cgi->param('obsolete'));
$field->set_visibility_field($cgi->param('visibility_field_id'));
$field->set_visibility_value($cgi->param('visibility_value_id'));
$field->update(); $field->update();
delete_token($token); delete_token($token);
......
...@@ -323,3 +323,39 @@ function boldOnChange(e, field_id){ ...@@ -323,3 +323,39 @@ function boldOnChange(e, field_id){
} }
} }
} }
/**
* Says that a field should only be displayed when another field has
* a certain value. May only be called after the controller has already
* been added to the DOM.
*/
function showFieldWhen(controlled_id, controller_id, value) {
var controller = document.getElementById(controller_id);
// Note that we don't get an object for "controlled" here, because it
// might not yet exist in the DOM. We just pass along its id.
YAHOO.util.Event.addListener(controller, 'change',
handleVisControllerValueChange, [controlled_id, controller, value]);
}
/**
* Called by showFieldWhen when a field's visibility controller
* changes values.
*/
function handleVisControllerValueChange(e, args) {
var controlled_id = args[0];
var controller = args[1];
var value = args[2];
var label_container =
document.getElementById('field_label_' + controlled_id);
var field_container =
document.getElementById('field_container_' + controlled_id);
if (bz_valueSelected(controller, value)) {
YAHOO.util.Dom.removeClass(label_container, 'bz_hidden_field');
YAHOO.util.Dom.removeClass(field_container, 'bz_hidden_field');
}
else {
YAHOO.util.Dom.addClass(label_container, 'bz_hidden_field');
YAHOO.util.Dom.addClass(field_container, 'bz_hidden_field');
}
}
...@@ -154,3 +154,68 @@ function bz_isValueInArray(aArray, aValue) ...@@ -154,3 +154,68 @@ function bz_isValueInArray(aArray, aValue)
return false; return false;
} }
/**
* Create wanted options in a select form control.
*
* @param aSelect Select form control to manipulate.
* @param aValue Value attribute of the new option element.
* @param aTextValue Value of a text node appended to the new option
* element.
* @return Created option element.
*/
function bz_createOptionInSelect(aSelect, aTextValue, aValue) {
var myOption = new Option(aTextValue, aValue);
aSelect.appendChild(myOption);
return myOption;
}
/**
* Clears all options from a select form control.
*
* @param aSelect Select form control of which options to clear.
*/
function bz_clearOptions(aSelect) {
var length = aSelect.options.length;
for (var i = 0; i < length; i++) {
aSelect.removeChild(aSelect.options[0]);
}
}
/**
* Takes an array and moves all the values to an select.
*
* @param aSelect Select form control to populate. Will be cleared
* before array values are created in it.
* @param aArray Array with values to populate select with.
*/
function bz_populateSelectFromArray(aSelect, aArray) {
// Clear the field
bz_clearOptions(aSelect);
for (var i = 0; i < aArray.length; i++) {
var item = aArray[i];
bz_createOptionInSelect(aSelect, item[1], item[0]);
}
}
/**
* Tells you whether or not a particular value is selected in a select,
* whether it's a multi-select or a single-select. The check is
* case-sensitive.
*
* @param aSelect The select you're checking.
* @param aValue The value that you want to know about.
*/
function bz_valueSelected(aSelect, aValue) {
var options = aSelect.options;
for (var i = 0; i < options.length; i++) {
if (options[i].selected && options[i].value == aValue) {
return true;
}
}
return false;
}
...@@ -406,6 +406,10 @@ div.user_match { ...@@ -406,6 +406,10 @@ div.user_match {
vertical-align: top; vertical-align: top;
} }
.bz_hidden_field {
display: none;
}
.calendar_button { .calendar_button {
background: transparent url("global/calendar.png") no-repeat; background: transparent url("global/calendar.png") no-repeat;
width: 20px; width: 20px;
......
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is NASA.
# Portions created by NASA are Copyright (C) 2008
# San Jose State University Foundation. All Rights Reserved.
#
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
// Disable a checkbox based on the state of another one.
function toggleCheckbox(this_checkbox, other_checkbox_id) {
var other_checkbox = document.getElementById(other_checkbox_id);
other_checkbox.disabled = !this_checkbox.checked;
}
var select_values = new Array();
[% FOREACH sel_field = Bugzilla.active_custom_fields %]
[% NEXT IF !sel_field.is_select %]
select_values[[% sel_field.id FILTER js %]] = [
[% FOREACH legal_value = sel_field.legal_values %]
[[% legal_value.id FILTER js %], '[% legal_value.name FILTER html %]'],
[% END %]
];
[% END %]
function onChangeVisibilityField() {
var vis_field = document.getElementById('visibility_field_id');
var vis_value = document.getElementById('visibility_value_id');
if (vis_field.value) {
var values = select_values[vis_field.value];
bz_populateSelectFromArray(vis_value, values);
}
else {
bz_clearOptions(vis_value);
}
}
...@@ -19,22 +19,17 @@ ...@@ -19,22 +19,17 @@
[% PROCESS "global/field-descs.none.tmpl" %] [% PROCESS "global/field-descs.none.tmpl" %]
[% javascript = BLOCK %]
[% INCLUDE "admin/custom_fields/cf-js.js.tmpl" %]
[% END %]
[% PROCESS global/header.html.tmpl [% PROCESS global/header.html.tmpl
title = "Add a new Custom Field" title = "Add a new Custom Field"
onload = "document.getElementById('new_bugmail').disabled = true;" onload = "document.getElementById('new_bugmail').disabled = true;"
javascript_urls = [ 'js/util.js' ]
doc_section = "custom-fields.html#add-custom-fields" doc_section = "custom-fields.html#add-custom-fields"
%] %]
<script type="text/javascript">
<!--
// Disable a checkbox based on the state of another one.
function toggleCheckbox(this_checkbox, other_checkbox_id) {
var other_checkbox = document.getElementById(other_checkbox_id);
other_checkbox.disabled = !this_checkbox.checked;
}
//-->
</script>
<p> <p>
Adding custom fields can make the interface of [% terms.Bugzilla %] very Adding custom fields can make the interface of [% terms.Bugzilla %] very
complicated. Many admins who are new to [% terms.Bugzilla %] start off complicated. Many admins who are new to [% terms.Bugzilla %] start off
...@@ -97,8 +92,26 @@ ...@@ -97,8 +92,26 @@
<input type="text" id="sortkey" name="sortkey" size="6" maxlength="6"> <input type="text" id="sortkey" name="sortkey" size="6" maxlength="6">
</td> </td>
<th>&nbsp;</th> <th align="right">
<td>&nbsp;</td> <label for="visibility_field_id">Field only appears when:</label>
</th>
<td>
<select name="visibility_field_id" id="visibility_field_id"
onchange="onChangeVisibilityField()">
<option></option>
[% FOREACH sel_field = Bugzilla.active_custom_fields %]
[% NEXT IF !sel_field.is_select %]
<option value="[% sel_field.id FILTER html %]">
[% sel_field.description FILTER html %]
([% sel_field.name FILTER html %])
</option>
[% END %]
</select>
<label for="visibility_value_id"><strong>is set to:</strong></label>
<select name="visibility_value_id" id="visibility_value_id">
<option value=""></option>
</select>
</td>
</tr> </tr>
</table> </table>
<p> <p>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#%] #%]
[%# INTERFACE: [%# INTERFACE:
# none # field: Bugzila::Field; the current field being edited
#%] #%]
[% PROCESS "global/field-descs.none.tmpl" %] [% PROCESS "global/field-descs.none.tmpl" %]
...@@ -23,22 +23,17 @@ ...@@ -23,22 +23,17 @@
Edit the Custom Field '[% field.name FILTER html %]' ([% field.description FILTER html %]) Edit the Custom Field '[% field.name FILTER html %]' ([% field.description FILTER html %])
[% END %] [% END %]
[% javascript = BLOCK %]
[% INCLUDE "admin/custom_fields/cf-js.js.tmpl" %]
[% END %]
[% PROCESS global/header.html.tmpl [% PROCESS global/header.html.tmpl
title = title title = title
onload = "toggleCheckbox(document.getElementById('enter_bug'), 'new_bugmail');" onload = "toggleCheckbox(document.getElementById('enter_bug'), 'new_bugmail');"
javascript_urls = [ 'js/util.js' ]
doc_section = "custom-fields.html#edit-custom-fields" doc_section = "custom-fields.html#edit-custom-fields"
%] %]
<script type="text/javascript">
<!--
// Disable a checkbox based on the state of another one.
function toggleCheckbox(this_checkbox, other_checkbox_id) {
var other_checkbox = document.getElementById(other_checkbox_id);
other_checkbox.disabled = !this_checkbox.checked;
}
//-->
</script>
<p> <p>
Descriptions are a very short string describing the field and will be used as Descriptions are a very short string describing the field and will be used as
the label for this field in the user interface. the label for this field in the user interface.
...@@ -82,12 +77,36 @@ ...@@ -82,12 +77,36 @@
<input type="text" id="sortkey" name="sortkey" size="6" maxlength="6" <input type="text" id="sortkey" name="sortkey" size="6" maxlength="6"
value="[% field.sortkey FILTER html %]"> value="[% field.sortkey FILTER html %]">
</td> </td>
<th align="right">
<th>&nbsp;</th> <label for="visibility_field_id">Field only appears when:</label>
<td>&nbsp;</td> </th>
<td>
<select name="visibility_field_id" id="visibility_field_id"
onchange="onChangeVisibilityField()">
<option></option>
[% FOREACH sel_field = Bugzilla.active_custom_fields %]
[% NEXT IF !sel_field.is_select || sel_field.id == field.id %]
<option value="[% sel_field.id FILTER html %]"
[% ' selected="selected"'
IF sel_field.id == field.visibility_field.id %]>
[% sel_field.description FILTER html %]
([% sel_field.name FILTER html %])
</option>
[% END %]
</select>
<label for="visibility_value_id"><strong>is set to:</strong></label>
<select name="visibility_value_id" id="visibility_value_id">
[% FOREACH value = field.visibility_field.legal_values %]
<option value="[% value.id FILTER html %]"
[% ' selected="selected"'
IF field.visibility_value.id == value.id %]>
[% value.name FILTER html %]
</option>
[% END %]
</select>
</td>
</tr> </tr>
[% IF field.type == constants.FIELD_TYPE_SINGLE_SELECT [% IF field.is_select %]
|| field.type == constants.FIELD_TYPE_MULTI_SELECT %]
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<td colspan="3"> <td colspan="3">
......
...@@ -61,7 +61,9 @@ ...@@ -61,7 +61,9 @@
<h2>Confirmation</h2> <h2>Confirmation</h2>
[% IF value.is_default || value.bug_count || (value_count == 1) %] [% IF value.is_default || value.bug_count || (value_count == 1)
|| value.controls_visibility_of_fields.size
%]
<p>Sorry, but the '[% value.name FILTER html %]' value cannot be deleted <p>Sorry, but the '[% value.name FILTER html %]' value cannot be deleted
from the '[% field.description FILTER html %]' field for the following from the '[% field.description FILTER html %]' field for the following
...@@ -108,6 +110,17 @@ ...@@ -108,6 +110,17 @@
'[%- field.description FILTER html %]', and so it can not be deleted. '[%- field.description FILTER html %]', and so it can not be deleted.
</li> </li>
[% END %] [% END %]
[% IF value.controls_visibility_of_fields.size %]
<li>This value controls the visibility of the following fields:<br>
[% FOREACH field = value.controls_visibility_of_fields %]
<a href="editfields.cgi?action=edit&name=
[%- field.name FILTER url_quote %]">
[%- field.description FILTER html %]
([% field.name FILTER html %])</a><br>
[% END %]
</li>
[% END %]
</ul> </ul>
[% ELSE %] [% ELSE %]
......
...@@ -28,9 +28,21 @@ ...@@ -28,9 +28,21 @@
# allow_dont_change: display the --do_not_change-- option for select fields. # allow_dont_change: display the --do_not_change-- option for select fields.
# value_span: A colspan for the table cell containing # value_span: A colspan for the table cell containing
# the field value. # the field value.
# bug (optional): The current Bugzilla::Bug being displayed, or a hash
# with default field values being displayed on a page.
#%] #%]
<th class="field_label"> [% SET hidden = 0 %]
[% IF field.visibility_field.defined %]
[% IF !bug.${field.visibility_field.name}
.contains(field.visibility_value.name)
%]
[% SET hidden = 1 %]
[% END %]
[% END %]
<th class="field_label [% ' bz_hidden_field' IF hidden %]"
id="field_label_[% field.name FILTER html %]">
[% IF editable %] [% IF editable %]
<label for="[% field.name FILTER html %]"> <label for="[% field.name FILTER html %]">
[% END %] [% END %]
...@@ -38,7 +50,9 @@ ...@@ -38,7 +50,9 @@
[% '</label>' IF editable %] [% '</label>' IF editable %]
</th> </th>
<td class="field_value" [% "colspan=\"$value_span\"" FILTER none IF value_span %]> <td class="field_value [% ' bz_hidden_field' IF hidden %]"
id="field_container_[% field.name FILTER html %]"
[% " colspan=\"$value_span\"" FILTER none IF value_span %]>
[% IF editable %] [% IF editable %]
[% SWITCH field.type %] [% SWITCH field.type %]
[% CASE constants.FIELD_TYPE_FREETEXT %] [% CASE constants.FIELD_TYPE_FREETEXT %]
...@@ -121,6 +135,14 @@ ...@@ -121,6 +135,14 @@
id = field.name name = field.name minrows = 4 maxrows = 8 id = field.name name = field.name minrows = 4 maxrows = 8
cols = 60 defaultcontent = value %] cols = 60 defaultcontent = value %]
[% END %] [% END %]
[% FOREACH controlled_field = field.controls_visibility_of %]
<script type="text/javascript">
showFieldWhen('[% controlled_field.name FILTER js %]',
'[% field.name FILTER js %]',
'[% controlled_field.visibility_value.name FILTER js %]');
</script>
[% END %]
[% ELSIF field.type == constants.FIELD_TYPE_TEXTAREA %] [% ELSIF field.type == constants.FIELD_TYPE_TEXTAREA %]
<div class="uneditable_textarea">[% value FILTER wrap_comment(60) <div class="uneditable_textarea">[% value FILTER wrap_comment(60)
FILTER html %]</div> FILTER html %]</div>
......
...@@ -402,6 +402,17 @@ ...@@ -402,6 +402,17 @@
([% field.description FILTER html %]) already exists. Please ([% field.description FILTER html %]) already exists. Please
choose another name. choose another name.
[% ELSIF error == "field_cant_control_self" %]
[% title = "Field Can't Control Itself" %]
The [% field.description FILTER html %] field can't be set to control
itself.
[% ELSIF error == "field_control_must_be_select" %]
[% title = "Invalid Field Type Selected" %]
Only drop-down and multi-select fields can be used to control
the visibility of other fields. [% field.description FILTER html %]
is not the right type of field.
[% ELSIF error == "field_invalid_name" %] [% ELSIF error == "field_invalid_name" %]
[% title = "Invalid Field Name" %] [% title = "Invalid Field Name" %]
'[% name FILTER html %]' is not a valid name for a field. '[% name FILTER html %]' is not a valid name for a field.
...@@ -430,6 +441,12 @@ ...@@ -430,6 +441,12 @@
The value '[% value.name FILTER html %]' already exists for the The value '[% value.name FILTER html %]' already exists for the
[%+ field.description FILTER html %] field. [%+ field.description FILTER html %] field.
[% ELSIF error == "fieldvalue_is_controller" %]
[% title = "Value Controls Other Fields" %]
You cannot delete the '[% value.name FILTER html %]' value for this
field because it controls the visibility of the following other fields:
[%+ fields.join(', ') FILTER html %].
[% ELSIF error == "fieldvalue_is_default" %] [% ELSIF error == "fieldvalue_is_default" %]
[% title = "Specified Field Value Is Default" %] [% title = "Specified Field Value Is Default" %]
'[% value.name FILTER html %]' is the default value for '[% value.name FILTER html %]' is the default value for
...@@ -1168,7 +1185,7 @@ ...@@ -1168,7 +1185,7 @@
in the <em>[% field_descs.$field FILTER html %]</em> field in the <em>[% field_descs.$field FILTER html %]</em> field
is less than the minimum allowable value of '[% min_num FILTER html %]'. is less than the minimum allowable value of '[% min_num FILTER html %]'.
[% ELSIF error == "object_name_not_specified" %] [% ELSIF error == "object_not_specified" %]
[% type = BLOCK %][% INCLUDE object_name class = class %][% END %] [% type = BLOCK %][% INCLUDE object_name class = class %][% END %]
[% title = BLOCK %][% type FILTER ucfirst FILTER html %] Not [% title = BLOCK %][% type FILTER ucfirst FILTER html %] Not
Specified[% END %] Specified[% END %]
...@@ -1177,7 +1194,12 @@ ...@@ -1177,7 +1194,12 @@
[% ELSIF error == "object_does_not_exist" %] [% ELSIF error == "object_does_not_exist" %]
[% type = BLOCK %][% INCLUDE object_name class = class %][% END %] [% type = BLOCK %][% INCLUDE object_name class = class %][% END %]
[% title = BLOCK %]Invalid [% type FILTER ucfirst FILTER html %][% END %] [% title = BLOCK %]Invalid [% type FILTER ucfirst FILTER html %][% END %]
There is no [% type FILTER html %] named '[% name FILTER html %]' There is no [% type FILTER html %]
[% IF id.defined %]
with the id '[% id FILTER html %]'
[% ELSE %]
named '[% name FILTER html %]'
[% END %]
[% IF product.defined %] [% IF product.defined %]
in the '[% product.name FILTER html %]' product in the '[% product.name FILTER html %]' product
[% END %]. [% END %].
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment