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 {
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} || '';
......
......@@ -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'],
......
......@@ -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;
}
......
......@@ -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 #
############
......
......@@ -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.
......
......@@ -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;
......
......@@ -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.
......
......@@ -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')) {
......
......@@ -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);
......
......@@ -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)
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 {
vertical-align: top;
}
.bz_hidden_field {
display: none;
}
.calendar_button {
background: transparent url("global/calendar.png") no-repeat;
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 @@
[% 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>
......
......@@ -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">
......
......@@ -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 %]
......
......@@ -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>
......
......@@ -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 %].
......
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