]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 291433: Ability to have custom fields whose visibility depends on the values...
authormkanat%bugzilla.org <>
Sat, 25 Oct 2008 04:11:30 +0000 (04:11 +0000)
committermkanat%bugzilla.org <>
Sat, 25 Oct 2008 04:11:30 +0000 (04:11 +0000)
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=bbaetz, a=mkanat

18 files changed:
Bugzilla/DB.pm
Bugzilla/DB/Schema.pm
Bugzilla/Field.pm
Bugzilla/Field/Choice.pm
Bugzilla/Install/DB.pm
Bugzilla/Object.pm
Bugzilla/WebService/Constants.pm
config.cgi
editfields.cgi
js/field.js
js/util.js
skins/standard/global.css
template/en/default/admin/custom_fields/cf-js.js.tmpl [new file with mode: 0644]
template/en/default/admin/custom_fields/create.html.tmpl
template/en/default/admin/custom_fields/edit.html.tmpl
template/en/default/admin/fieldvalues/confirm-delete.html.tmpl
template/en/default/bug/field.html.tmpl
template/en/default/global/user-error.html.tmpl

index e264dc443fd908d7a079782d8be4a9921c539954..cc4ddb9aa71382f42250f4099583f392ae2a0024 100644 (file)
@@ -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} || '';
index 850e48a1bae0e5e467a4ea1a652d0afb6791ae75..80a964446131f7e04b9ccb9e3f2f1cedaba4520a 100644 (file)
@@ -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'],
index 5d8e5adc0eb022b3df4dd33bc7fba48fa9674e1c..7da8a8bba4d67b7131520746732873fa960e9a99 100644 (file)
@@ -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;
 }
 
index dbdfea1a38da929b5a579c014edaa535e928ef55..4da644f1dc3ce87cdb398f5c4e224997dce7d3ad 100644 (file)
@@ -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 #
 ############
index 213fe30997876b31bcdfa524c5fa9baf4baf2352..7655b7619101e024be637a6b9f6fa8f8a1226f98 100644 (file)
@@ -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.
index 1e624e5f7ce608fb0b71d76c6604a5728ccc16ad..53720327b59be3e20abf69763456c48d470ddefd 100644 (file)
@@ -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;
index a43e1f5908d6b84174c6f9bf8b038bef4ac12c78..593801a6f4447a1b23c108466bad6ca838db747f 100755 (executable)
@@ -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.
index ebdf71241d9750c12fd11184918876f49324cf4f..c229dacd693e44815d44fb2bd4177ae0da3add00 100755 (executable)
@@ -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')) {
index 138c6b72959baba9462a2f57de6e44b5f9715e45..e1c7d12c2f7c36b012cf427657c0c61a387916e9 100644 (file)
@@ -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);
index 497c9d2c1867434cc3b78b68a16b184a7ce03589..fb8716872ec28d4158ced26187df0e2e12083b37 100644 (file)
@@ -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');
+    }
+}
index ce7ea4caeb04497f75453f74bf2a345985ea5b3b..feef8fe41110795ab4574caa4b5d441f31beddf4 100644 (file)
@@ -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;
+}
+
index 5118e9c28ddf8b71125961879127d3c3b71f3277..0c2c0c434447f2e278a1ac1e6b6da12b0031cbd7 100644 (file)
@@ -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 (file)
index 0000000..863f14d
--- /dev/null
@@ -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);
+    }
+}
index 5dd50ce3b47682d69e5859c6fb76a7e89dcb054f..da10c7bcbf82b4944b6539dfd10b626c1ec5f1fa 100644 (file)
 
 [% 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
         <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>
index 02334ab13b9199cd9d3b2680e1a3d63c6d083c7a..2186c75627214df48fab049a43d04bc3784433e8 100644 (file)
@@ -14,7 +14,7 @@
   #%]
 
 [%# INTERFACE:
-  # none
+  # field: Bugzila::Field; the current field being edited
   #%]
 
 [% PROCESS "global/field-descs.none.tmpl" %]
   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.
         <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">
index 12be0e8e4243f61d21612bb58ce61d2015b4c5b1..2389fb6ae27973c960523ac890042c0d0c95c9ba 100644 (file)
@@ -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 
         '[%- 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 %]
index 762e659dbc9bc2dce3db3ebc1e87bbf88074dee5..3b26073aab9e51c9c92e9a71a3ae829b49b24d6a 100644 (file)
   #   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 %]
            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>
index 1b737744c3f88349cda604fe1d00d3d79a9a4043..f4ea42ccfdf10079e3b62470e9fd87f9f95e03c9 100644 (file)
     ([% 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.
     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
     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 %]
   [% 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 %].