diff options
author | mkanat%bugzilla.org <> | 2008-10-25 06:11:30 +0200 |
---|---|---|
committer | mkanat%bugzilla.org <> | 2008-10-25 06:11:30 +0200 |
commit | 3cea91884b28b52df4e38f2ba88c00b65071a81f (patch) | |
tree | c0f451235176b2b542a38b0c935dcddf0453a1ee | |
parent | e0da20baba17b7f068946c8647fb6d67e77c39b7 (diff) | |
download | bugzilla-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.pm | 14 | ||||
-rw-r--r-- | Bugzilla/DB/Schema.pm | 4 | ||||
-rw-r--r-- | Bugzilla/Field.pm | 169 | ||||
-rw-r--r-- | Bugzilla/Field/Choice.pm | 12 | ||||
-rw-r--r-- | Bugzilla/Install/DB.pm | 3 | ||||
-rw-r--r-- | Bugzilla/Object.pm | 10 | ||||
-rwxr-xr-x | Bugzilla/WebService/Constants.pm | 2 | ||||
-rwxr-xr-x | config.cgi | 3 | ||||
-rw-r--r-- | editfields.cgi | 5 | ||||
-rw-r--r-- | js/field.js | 36 | ||||
-rw-r--r-- | js/util.js | 65 | ||||
-rw-r--r-- | skins/standard/global.css | 4 | ||||
-rw-r--r-- | template/en/default/admin/custom_fields/cf-js.js.tmpl | 47 | ||||
-rw-r--r-- | template/en/default/admin/custom_fields/create.html.tmpl | 37 | ||||
-rw-r--r-- | template/en/default/admin/custom_fields/edit.html.tmpl | 51 | ||||
-rw-r--r-- | template/en/default/admin/fieldvalues/confirm-delete.html.tmpl | 15 | ||||
-rw-r--r-- | template/en/default/bug/field.html.tmpl | 26 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 26 |
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> </th> - <td> </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> </th> - <td> </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> </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 %]. |