summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormkanat%bugzilla.org <>2008-10-25 06:11:30 +0200
committermkanat%bugzilla.org <>2008-10-25 06:11:30 +0200
commit3cea91884b28b52df4e38f2ba88c00b65071a81f (patch)
treec0f451235176b2b542a38b0c935dcddf0453a1ee
parente0da20baba17b7f068946c8647fb6d67e77c39b7 (diff)
downloadbugzilla-3cea91884b28b52df4e38f2ba88c00b65071a81f.tar.gz
bugzilla-3cea91884b28b52df4e38f2ba88c00b65071a81f.tar.xz
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
-rw-r--r--Bugzilla/DB.pm14
-rw-r--r--Bugzilla/DB/Schema.pm4
-rw-r--r--Bugzilla/Field.pm169
-rw-r--r--Bugzilla/Field/Choice.pm12
-rw-r--r--Bugzilla/Install/DB.pm3
-rw-r--r--Bugzilla/Object.pm10
-rwxr-xr-xBugzilla/WebService/Constants.pm2
-rwxr-xr-xconfig.cgi3
-rw-r--r--editfields.cgi5
-rw-r--r--js/field.js36
-rw-r--r--js/util.js65
-rw-r--r--skins/standard/global.css4
-rw-r--r--template/en/default/admin/custom_fields/cf-js.js.tmpl47
-rw-r--r--template/en/default/admin/custom_fields/create.html.tmpl37
-rw-r--r--template/en/default/admin/custom_fields/edit.html.tmpl51
-rw-r--r--template/en/default/admin/fieldvalues/confirm-delete.html.tmpl15
-rw-r--r--template/en/default/bug/field.html.tmpl26
-rw-r--r--template/en/default/global/user-error.html.tmpl26
18 files changed, 465 insertions, 64 deletions
diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm
index e264dc443..cc4ddb9aa 100644
--- a/Bugzilla/DB.pm
+++ b/Bugzilla/DB.pm
@@ -1209,12 +1209,16 @@ sub _check_references {
my $foreign_table = $fk->{TABLE};
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(
- "SELECT DISTINCT $table.$column
- FROM $table LEFT JOIN $foreign_table
- ON $table.$column = $foreign_table.$foreign_column
- WHERE $foreign_table.$foreign_column IS NULL
- AND $table.$column IS NOT NULL");
+ "SELECT DISTINCT tabl.$column
+ FROM $table AS tabl LEFT JOIN $foreign_table AS forn
+ ON tabl.$column = forn.$foreign_column
+ WHERE forn.$foreign_column IS NULL
+ AND tabl.$column IS NOT NULL");
if (@$bad_values) {
my $delete_action = $fk->{DELETE} || '';
diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm
index 850e48a1b..80a964446 100644
--- a/Bugzilla/DB/Schema.pm
+++ b/Bugzilla/DB/Schema.pm
@@ -631,6 +631,10 @@ use constant ABSTRACT_SCHEMA => {
DEFAULT => 'FALSE'},
enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
+ visibility_field_id => {TYPE => 'INT3',
+ REFERENCES => {TABLE => 'fielddefs',
+ COLUMN => 'id'}},
+ visibility_value_id => {TYPE => 'INT2'},
],
INDEXES => [
fielddefs_name_idx => {FIELDS => ['name'],
diff --git a/Bugzilla/Field.pm b/Bugzilla/Field.pm
index 5d8e5adc0..7da8a8bba 100644
--- a/Bugzilla/Field.pm
+++ b/Bugzilla/Field.pm
@@ -77,6 +77,8 @@ use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Util;
+use Scalar::Util qw(blessed);
+
###############################
#### Initialization ####
###############################
@@ -84,16 +86,18 @@ use Bugzilla::Util;
use constant DB_TABLE => 'fielddefs';
use constant LIST_ORDER => 'sortkey, name';
-use constant DB_COLUMNS => (
- 'id',
- 'name',
- 'description',
- 'type',
- 'custom',
- 'mailhead',
- 'sortkey',
- 'obsolete',
- 'enter_bug',
+use constant DB_COLUMNS => qw(
+ id
+ name
+ description
+ type
+ custom
+ mailhead
+ sortkey
+ obsolete
+ enter_bug
+ visibility_field_id
+ visibility_value_id
);
use constant REQUIRED_CREATE_FIELDS => qw(name description);
@@ -106,6 +110,11 @@ use constant VALIDATORS => {
obsolete => \&_check_obsolete,
sortkey => \&_check_sortkey,
type => \&_check_type,
+ visibility_field_id => \&_check_control_field,
+};
+
+use constant UPDATE_VALIDATORS => {
+ visibility_value_id => \&_check_control_value,
};
use constant UPDATE_COLUMNS => qw(
@@ -114,6 +123,8 @@ use constant UPDATE_COLUMNS => qw(
sortkey
obsolete
enter_bug
+ visibility_field_id
+ visibility_value_id
);
# How various field types translate into SQL data definitions.
@@ -259,6 +270,37 @@ sub _check_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
=head2 Instance Properties
@@ -362,6 +404,11 @@ sub enter_bug { return $_[0]->{enter_bug} }
=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>
Valid values for this field, as an array of L<Bugzilla::Field::Choice>
@@ -371,6 +418,11 @@ objects.
=cut
+sub is_select {
+ return ($_[0]->type == FIELD_TYPE_SINGLE_SELECT
+ || $_[0]->type == FIELD_TYPE_MULTI_SELECT) ? 1 : 0
+}
+
sub legal_values {
my $self = shift;
@@ -384,6 +436,76 @@ sub legal_values {
=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
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.
=item C<set_in_new_bugmail>
+=item C<set_visibility_field>
+
+=item C<set_visibility_value>
+
=back
=cut
@@ -413,6 +539,17 @@ sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); }
sub set_obsolete { $_[0]->set('obsolete', $_[1]); }
sub set_sortkey { $_[0]->set('sortkey', $_[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
@@ -487,9 +624,7 @@ sub remove_from_db {
$dbh->bz_drop_column('bugs', $name);
}
- if ($type == FIELD_TYPE_SINGLE_SELECT
- || $type == FIELD_TYPE_MULTI_SELECT)
- {
+ if ($self->is_select) {
# Delete the table that holds the legal values for this field.
$dbh->bz_drop_field_tables($self);
}
@@ -545,9 +680,7 @@ sub create {
$dbh->bz_add_column('bugs', $name, SQL_DEFINITIONS->{$type});
}
- if ($type == FIELD_TYPE_SINGLE_SELECT
- || $type == FIELD_TYPE_MULTI_SELECT)
- {
+ if ($field->is_select) {
# Create the table that holds the legal values for this field.
$dbh->bz_add_field_tables($field);
}
@@ -572,6 +705,10 @@ sub run_create_validators {
"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;
}
diff --git a/Bugzilla/Field/Choice.pm b/Bugzilla/Field/Choice.pm
index dbdfea1a3..4da644f1d 100644
--- a/Bugzilla/Field/Choice.pm
+++ b/Bugzilla/Field/Choice.pm
@@ -186,6 +186,10 @@ sub remove_from_db {
ThrowUserError("fieldvalue_still_has_bugs",
{ 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();
}
@@ -248,6 +252,14 @@ sub is_static {
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 #
############
diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm
index 213fe3099..7655b7619 100644
--- a/Bugzilla/Install/DB.pm
+++ b/Bugzilla/Install/DB.pm
@@ -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.
# That is below. Add new changes to the fielddefs table above this
# comment.
diff --git a/Bugzilla/Object.pm b/Bugzilla/Object.pm
index 1e624e5f7..53720327b 100644
--- a/Bugzilla/Object.pm
+++ b/Bugzilla/Object.pm
@@ -117,12 +117,10 @@ sub check {
if (!ref $param) {
$param = { name => $param };
}
- # Don't allow empty names.
- if (exists $param->{name}) {
- $param->{name} = trim($param->{name});
- $param->{name} || ThrowUserError('object_name_not_specified',
- { class => $class });
- }
+ # Don't allow empty names or ids.
+ my $check_param = exists $param->{id} ? $param->{id} : $param->{name};
+ $check_param = trim($check_param);
+ $check_param || ThrowUserError('object_not_specified', { class => $class });
my $obj = $class->new($param)
|| ThrowUserError('object_does_not_exist', {%$param, class => $class});
return $obj;
diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm
index a43e1f590..593801a6f 100755
--- a/Bugzilla/WebService/Constants.pm
+++ b/Bugzilla/WebService/Constants.pm
@@ -49,7 +49,7 @@ use base qw(Exporter);
# have to fix it here.
use constant WS_ERROR_CODE => {
# Generic Bugzilla::Object errors are 50-99.
- object_name_not_specified => 50,
+ object_not_specified => 50,
param_required => 50,
object_does_not_exist => 51,
# Bug errors usually occupy the 100-200 range.
diff --git a/config.cgi b/config.cgi
index ebdf71241..c229dacd6 100755
--- a/config.cgi
+++ b/config.cgi
@@ -56,8 +56,7 @@ $vars->{'keyword'} = [map($_->name, Bugzilla::Keyword->get_all)];
$vars->{'resolution'} = get_legal_field_values('resolution');
$vars->{'status'} = get_legal_field_values('bug_status');
$vars->{'custom_fields'} =
- [ grep {$_->type == FIELD_TYPE_SINGLE_SELECT || $_->type == FIELD_TYPE_MULTI_SELECT}
- Bugzilla->active_custom_fields ];
+ [ grep {$_->is_select} Bugzilla->active_custom_fields ];
# Include a list of product objects.
if ($cgi->param('product')) {
diff --git a/editfields.cgi b/editfields.cgi
index 138c6b729..e1c7d12c2 100644
--- a/editfields.cgi
+++ b/editfields.cgi
@@ -55,7 +55,6 @@ elsif ($action eq 'add') {
}
elsif ($action eq 'new') {
check_token_data($token, 'add_field');
-
$vars->{'field'} = Bugzilla::Field->create({
name => scalar $cgi->param('name'),
description => scalar $cgi->param('desc'),
@@ -65,6 +64,8 @@ elsif ($action eq 'new') {
enter_bug => scalar $cgi->param('enter_bug'),
obsolete => scalar $cgi->param('obsolete'),
custom => 1,
+ visibility_field_id => scalar $cgi->param('visibility_field_id'),
+ visibility_value_id => scalar $cgi->param('visibility_value_id'),
});
delete_token($token);
@@ -107,6 +108,8 @@ elsif ($action eq 'update') {
$field->set_in_new_bugmail($cgi->param('new_bugmail'));
$field->set_enter_bug($cgi->param('enter_bug'));
$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();
delete_token($token);
diff --git a/js/field.js b/js/field.js
index 497c9d2c1..fb8716872 100644
--- a/js/field.js
+++ b/js/field.js
@@ -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');
+ }
+}
diff --git a/js/util.js b/js/util.js
index ce7ea4cae..feef8fe41 100644
--- a/js/util.js
+++ b/js/util.js
@@ -154,3 +154,68 @@ function bz_isValueInArray(aArray, aValue)
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;
+}
+
diff --git a/skins/standard/global.css b/skins/standard/global.css
index 5118e9c28..0c2c0c434 100644
--- a/skins/standard/global.css
+++ b/skins/standard/global.css
@@ -406,6 +406,10 @@ div.user_match {
vertical-align: top;
}
+.bz_hidden_field {
+ display: none;
+}
+
.calendar_button {
background: transparent url("global/calendar.png") no-repeat;
width: 20px;
diff --git a/template/en/default/admin/custom_fields/cf-js.js.tmpl b/template/en/default/admin/custom_fields/cf-js.js.tmpl
new file mode 100644
index 000000000..863f14dca
--- /dev/null
+++ b/template/en/default/admin/custom_fields/cf-js.js.tmpl
@@ -0,0 +1,47 @@
+[%# 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);
+ }
+}
diff --git a/template/en/default/admin/custom_fields/create.html.tmpl b/template/en/default/admin/custom_fields/create.html.tmpl
index 5dd50ce3b..da10c7bcb 100644
--- a/template/en/default/admin/custom_fields/create.html.tmpl
+++ b/template/en/default/admin/custom_fields/create.html.tmpl
@@ -19,22 +19,17 @@
[% PROCESS "global/field-descs.none.tmpl" %]
+[% javascript = BLOCK %]
+ [% INCLUDE "admin/custom_fields/cf-js.js.tmpl" %]
+[% END %]
+
[% PROCESS global/header.html.tmpl
title = "Add a new Custom Field"
onload = "document.getElementById('new_bugmail').disabled = true;"
+ javascript_urls = [ 'js/util.js' ]
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>
Adding custom fields can make the interface of [% terms.Bugzilla %] very
complicated. Many admins who are new to [% terms.Bugzilla %] start off
@@ -97,8 +92,26 @@
<input type="text" id="sortkey" name="sortkey" size="6" maxlength="6">
</td>
- <th>&nbsp;</th>
- <td>&nbsp;</td>
+ <th align="right">
+ <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>
</table>
<p>
diff --git a/template/en/default/admin/custom_fields/edit.html.tmpl b/template/en/default/admin/custom_fields/edit.html.tmpl
index 02334ab13..2186c7562 100644
--- a/template/en/default/admin/custom_fields/edit.html.tmpl
+++ b/template/en/default/admin/custom_fields/edit.html.tmpl
@@ -14,7 +14,7 @@
#%]
[%# INTERFACE:
- # none
+ # field: Bugzila::Field; the current field being edited
#%]
[% PROCESS "global/field-descs.none.tmpl" %]
@@ -23,22 +23,17 @@
Edit the Custom Field '[% field.name FILTER html %]' ([% field.description FILTER html %])
[% END %]
+[% javascript = BLOCK %]
+ [% INCLUDE "admin/custom_fields/cf-js.js.tmpl" %]
+[% END %]
+
[% PROCESS global/header.html.tmpl
title = title
onload = "toggleCheckbox(document.getElementById('enter_bug'), 'new_bugmail');"
+ javascript_urls = [ 'js/util.js' ]
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>
Descriptions are a very short string describing the field and will be used as
the label for this field in the user interface.
@@ -82,12 +77,36 @@
<input type="text" id="sortkey" name="sortkey" size="6" maxlength="6"
value="[% field.sortkey FILTER html %]">
</td>
-
- <th>&nbsp;</th>
- <td>&nbsp;</td>
+ <th align="right">
+ <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 || 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>
- [% IF field.type == constants.FIELD_TYPE_SINGLE_SELECT
- || field.type == constants.FIELD_TYPE_MULTI_SELECT %]
+ [% IF field.is_select %]
<tr>
<th>&nbsp;</th>
<td colspan="3">
diff --git a/template/en/default/admin/fieldvalues/confirm-delete.html.tmpl b/template/en/default/admin/fieldvalues/confirm-delete.html.tmpl
index 12be0e8e4..2389fb6ae 100644
--- a/template/en/default/admin/fieldvalues/confirm-delete.html.tmpl
+++ b/template/en/default/admin/fieldvalues/confirm-delete.html.tmpl
@@ -61,7 +61,9 @@
<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
from the '[% field.description FILTER html %]' field for the following
@@ -108,6 +110,17 @@
'[%- field.description FILTER html %]', and so it can not be deleted.
</li>
[% 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>
[% ELSE %]
diff --git a/template/en/default/bug/field.html.tmpl b/template/en/default/bug/field.html.tmpl
index 762e659db..3b26073aa 100644
--- a/template/en/default/bug/field.html.tmpl
+++ b/template/en/default/bug/field.html.tmpl
@@ -28,9 +28,21 @@
# allow_dont_change: display the --do_not_change-- option for select fields.
# value_span: A colspan for the table cell containing
# 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 %]
<label for="[% field.name FILTER html %]">
[% END %]
@@ -38,7 +50,9 @@
[% '</label>' IF editable %]
</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 %]
[% SWITCH field.type %]
[% CASE constants.FIELD_TYPE_FREETEXT %]
@@ -121,6 +135,14 @@
id = field.name name = field.name minrows = 4 maxrows = 8
cols = 60 defaultcontent = value %]
[% 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 %]
<div class="uneditable_textarea">[% value FILTER wrap_comment(60)
FILTER html %]</div>
diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl
index 1b737744c..f4ea42ccf 100644
--- a/template/en/default/global/user-error.html.tmpl
+++ b/template/en/default/global/user-error.html.tmpl
@@ -402,6 +402,17 @@
([% field.description FILTER html %]) already exists. Please
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" %]
[% title = "Invalid Field Name" %]
'[% name FILTER html %]' is not a valid name for a field.
@@ -430,6 +441,12 @@
The value '[% value.name FILTER html %]' already exists for the
[%+ 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" %]
[% title = "Specified Field Value Is Default" %]
'[% value.name FILTER html %]' is the default value for
@@ -1168,7 +1185,7 @@
in the <em>[% field_descs.$field FILTER html %]</em> field
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 %]
[% title = BLOCK %][% type FILTER ucfirst FILTER html %] Not
Specified[% END %]
@@ -1177,7 +1194,12 @@
[% ELSIF error == "object_does_not_exist" %]
[% type = BLOCK %][% INCLUDE object_name class = class %][% 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 %]
in the '[% product.name FILTER html %]' product
[% END %].