Add nullable property for fields

master
Vitaliy Filippov 2014-03-24 18:44:31 +04:00
parent b2d8118048
commit 1cd3cae485
10 changed files with 124 additions and 130 deletions

View File

@ -102,9 +102,10 @@ sub get_param_list
my $legal = {}; my $legal = {};
for (qw(priority bug_severity platform op_sys)) for (qw(priority bug_severity platform op_sys))
{ {
$legal->{$_} = [ Bugzilla->get_field($_)->legal_value_names ]; # Ignore evaluation errors - this piece of code may be called in checksetup.pl,
# fielddefs table may not be created at that time...
$legal->{$_} = eval { Bugzilla->get_field($_)->legal_value_names } || [];
} }
my @param_list = ( my @param_list = (
{ {
name => 'useclassification', name => 'useclassification',

View File

@ -541,6 +541,7 @@ use constant ABSTRACT_SCHEMA => {
mailhead => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}, mailhead => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
sortkey => {TYPE => 'INT2', NOTNULL => 1}, sortkey => {TYPE => 'INT2', NOTNULL => 1},
obsolete => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}, obsolete => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
nullable => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}, enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
buglist => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}, buglist => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
visibility_field_id => {TYPE => 'INT4', REFERENCES => {TABLE => 'fielddefs', COLUMN => 'id'}}, visibility_field_id => {TYPE => 'INT4', REFERENCES => {TABLE => 'fielddefs', COLUMN => 'id'}},

View File

@ -102,6 +102,7 @@ use constant DB_COLUMNS => qw(
mailhead mailhead
sortkey sortkey
obsolete obsolete
nullable
enter_bug enter_bug
clone_bug clone_bug
buglist buglist
@ -123,6 +124,7 @@ use constant VALIDATORS => {
buglist => \&Bugzilla::Object::check_boolean, buglist => \&Bugzilla::Object::check_boolean,
mailhead => \&Bugzilla::Object::check_boolean, mailhead => \&Bugzilla::Object::check_boolean,
obsolete => \&Bugzilla::Object::check_boolean, obsolete => \&Bugzilla::Object::check_boolean,
nullable => \&Bugzilla::Object::check_boolean,
sortkey => \&_check_sortkey, sortkey => \&_check_sortkey,
type => \&_check_type, type => \&_check_type,
visibility_field_id => \&_check_visibility_field_id, visibility_field_id => \&_check_visibility_field_id,
@ -138,6 +140,7 @@ use constant UPDATE_COLUMNS => qw(
mailhead mailhead
sortkey sortkey
obsolete obsolete
nullable
enter_bug enter_bug
clone_bug clone_bug
buglist buglist
@ -168,7 +171,7 @@ use constant SQL_DEFINITIONS => {
# These are used by populate_field_definitions to populate # These are used by populate_field_definitions to populate
# the fielddefs table. # the fielddefs table.
use constant DEFAULT_FIELDS => ( use constant DEFAULT_FIELDS => (
{name => 'bug_id', desc => 'Bug ID', buglist => 1, in_new_bugmail => 1}, {name => 'bug_id', desc => 'Bug ID', buglist => 1, in_new_bugmail => 1},
{name => 'short_desc', desc => 'Summary', buglist => 1, in_new_bugmail => 1}, {name => 'short_desc', desc => 'Summary', buglist => 1, in_new_bugmail => 1},
{name => 'classification', desc => 'Classification', buglist => 1, in_new_bugmail => 1}, {name => 'classification', desc => 'Classification', buglist => 1, in_new_bugmail => 1},
{name => 'product', desc => 'Product', buglist => 1, in_new_bugmail => 1, type => FIELD_TYPE_SINGLE_SELECT}, {name => 'product', desc => 'Product', buglist => 1, in_new_bugmail => 1, type => FIELD_TYPE_SINGLE_SELECT},
@ -426,10 +429,21 @@ sub obsolete { return $_[0]->{obsolete} }
=over =over
=item C<nullable>
a boolean specifying whether NULL value is allowed for this field
=back
=cut
sub nullable { return $_[0]->{nullable} }
=over
=item C<enter_bug> =item C<enter_bug>
A boolean specifying whether or not this field should appear on A boolean specifying whether this field should appear on enter_bug.cgi
enter_bug.cgi
=back =back
@ -736,6 +750,8 @@ They will throw an error if you try to set the values to something invalid.
=item C<set_obsolete> =item C<set_obsolete>
=item C<set_nullable>
=item C<set_sortkey> =item C<set_sortkey>
=item C<set_in_new_bugmail> =item C<set_in_new_bugmail>
@ -754,6 +770,7 @@ sub set_description { $_[0]->set('description', $_[1]); }
sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); } sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); }
sub set_clone_bug { $_[0]->set('clone_bug', $_[1]); } sub set_clone_bug { $_[0]->set('clone_bug', $_[1]); }
sub set_obsolete { $_[0]->set('obsolete', $_[1]); } sub set_obsolete { $_[0]->set('obsolete', $_[1]); }
sub set_nullable { $_[0]->set('nullable', $_[1]); }
sub set_sortkey { $_[0]->set('sortkey', $_[1]); } sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
sub set_in_new_bugmail { $_[0]->set('mailhead', $_[1]); } sub set_in_new_bugmail { $_[0]->set('mailhead', $_[1]); }
sub set_buglist { $_[0]->set('buglist', $_[1]); } sub set_buglist { $_[0]->set('buglist', $_[1]); }

View File

@ -1,31 +1,10 @@
# -*- Mode: perl; indent-tabs-mode: nil -*- # Class representing single value of a field
# # Nearly 100% refactored
# The contents of this file are subject to the Mozilla Public # Author(s): Vitaliy Filippov <vitalif@mail.ru>, Max Kanat-Alexander <mkanat@bugzilla.org>, Greg Hendricks <ghendricks@novell.com>
# License Version 1.1 (the "License"); you may not use this file # License: Dual-license GPL 3.0+ or MPL 1.1+
# 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 Initial Developer of the Original Code is NASA.
# Portions created by NASA are Copyright (C) 2006 San Jose State
# University Foundation. All Rights Reserved.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
# Greg Hendricks <ghendricks@novell.com>
# Vitaliy Filippov <vitalif@mail.ru>
use strict; use strict;
##############################################
# Class representing single value of a field #
##############################################
package Bugzilla::Field::Choice; package Bugzilla::Field::Choice;
use base qw(Bugzilla::Object); use base qw(Bugzilla::Object);
@ -139,9 +118,11 @@ EOC
# We just make new() enforce this, which should give developers # We just make new() enforce this, which should give developers
# the understanding that you can't use Bugzilla::Field::Choice # the understanding that you can't use Bugzilla::Field::Choice
# without calling type(). # without calling type().
sub new { sub new
{
my $class = shift; my $class = shift;
if ($class eq 'Bugzilla::Field::Choice') { if ($class eq 'Bugzilla::Field::Choice')
{
ThrowCodeError('field_choice_must_use_type'); ThrowCodeError('field_choice_must_use_type');
} }
$class->SUPER::new(@_); $class->SUPER::new(@_);
@ -164,7 +145,8 @@ sub create
return $self; return $self;
} }
sub update { sub update
{
my $self = shift; my $self = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $fname = $self->field->name; my $fname = $self->field->name;
@ -172,7 +154,8 @@ sub update {
$dbh->bz_start_transaction(); $dbh->bz_start_transaction();
my ($changes, $old_self) = $self->SUPER::update(@_); my ($changes, $old_self) = $self->SUPER::update(@_);
if (exists $changes->{$self->NAME_FIELD}) { if (exists $changes->{$self->NAME_FIELD})
{
my ($old, $new) = @{ $changes->{$self->NAME_FIELD} }; my ($old, $new) = @{ $changes->{$self->NAME_FIELD} };
if ($self->field->type != FIELD_TYPE_MULTI_SELECT) if ($self->field->type != FIELD_TYPE_MULTI_SELECT)
{ {
@ -185,8 +168,8 @@ sub update {
$dbh->do("UPDATE bugs SET $fname = ?, lastdiffed = NOW() WHERE $fname = ?", $dbh->do("UPDATE bugs SET $fname = ?, lastdiffed = NOW() WHERE $fname = ?",
undef, $new, $old); undef, $new, $old);
} }
if ($old_self->is_default)
if ($old_self->is_default) { {
my $param = $self->DEFAULT_MAP->{$self->field->name}; my $param = $self->DEFAULT_MAP->{$self->field->name};
SetParam($param, $self->name); SetParam($param, $self->name);
write_params(); write_params();
@ -198,21 +181,30 @@ sub update {
return wantarray ? ($changes, $old_self) : $changes; return wantarray ? ($changes, $old_self) : $changes;
} }
sub remove_from_db { sub remove_from_db
{
my $self = shift; my $self = shift;
if ($self->is_default) { if ($self->is_default)
ThrowUserError('fieldvalue_is_default', {
{ field => $self->field, value => $self, ThrowUserError('fieldvalue_is_default', {
param_name => $self->DEFAULT_MAP->{$self->field->name}, field => $self->field,
}); value => $self,
param_name => $self->DEFAULT_MAP->{$self->field->name},
});
} }
if ($self->is_static) { if ($self->is_static)
ThrowUserError('fieldvalue_not_deletable', {
{ field => $self->field, value => $self }); ThrowUserError('fieldvalue_not_deletable', {
field => $self->field,
value => $self,
});
} }
if ($self->bug_count) { if ($self->bug_count)
ThrowUserError('fieldvalue_still_has_bugs', {
{ field => $self->field, value => $self }); ThrowUserError('fieldvalue_still_has_bugs', {
field => $self->field,
value => $self,
});
} }
$self->_check_if_controller(); $self->_check_if_controller();
$self->set_visibility_values(undef); $self->set_visibility_values(undef);
@ -353,20 +345,20 @@ sub _check_if_controller
sub is_active { return $_[0]->{'isactive'}; } sub is_active { return $_[0]->{'isactive'}; }
sub sortkey { return $_[0]->{'sortkey'}; } sub sortkey { return $_[0]->{'sortkey'}; }
sub bug_count { sub bug_count
{
my $self = shift; my $self = shift;
return $self->{bug_count} if defined $self->{bug_count}; return $self->{bug_count} if defined $self->{bug_count};
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $fname = $self->field->name; my $fname = $self->field->name;
my $count; my $count;
if ($self->field->type == FIELD_TYPE_MULTI_SELECT) { if ($self->field->type == FIELD_TYPE_MULTI_SELECT)
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bug_$fname {
WHERE value_id = ?", undef, $self->id); $count = $dbh->selectrow_array("SELECT COUNT(*) FROM bug_$fname WHERE value_id = ?", undef, $self->id);
} }
else { else
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs {
WHERE $fname = ?", $count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs WHERE $fname = ?", undef, $self->id);
undef, $self->name);
} }
$self->{bug_count} = $count; $self->{bug_count} = $count;
return $count; return $count;
@ -378,7 +370,8 @@ sub field
return Bugzilla->get_field($invocant->FIELD_NAME); return Bugzilla->get_field($invocant->FIELD_NAME);
} }
sub is_default { sub is_default
{
my $self = shift; my $self = shift;
my $name = $self->DEFAULT_MAP->{$self->field->name}; my $name = $self->DEFAULT_MAP->{$self->field->name};
# If it doesn't exist in DEFAULT_MAP, then there is no parameter # If it doesn't exist in DEFAULT_MAP, then there is no parameter
@ -389,14 +382,6 @@ sub is_default {
sub is_static sub is_static
{ {
my $self = shift;
# If we need to special-case Resolution for *anything* else, it should
# get its own subclass.
if ($self->field->name eq 'resolution')
{
return grep($_ eq $self->name, ('', 'FIXED', 'MOVED', 'DUPLICATE'))
? 1 : 0;
}
return 0; return 0;
} }
@ -529,14 +514,15 @@ sub set_visibility_values
my ($value_ids) = @_; my ($value_ids) = @_;
update_visibility_values($self->field, $self->id, $value_ids); update_visibility_values($self->field, $self->id, $value_ids);
delete $self->{visibility_values}; delete $self->{visibility_values};
return 1; return $value_ids;
} }
############## ##############
# Validators # # Validators #
############## ##############
sub _check_value { sub _check_value
{
my ($invocant, $value) = @_; my ($invocant, $value) = @_;
my $field = $invocant->field; my $field = $invocant->field;
@ -544,11 +530,9 @@ sub _check_value {
$value = trim($value); $value = trim($value);
# Make sure people don't rename static values # Make sure people don't rename static values
if (blessed($invocant) && $value ne $invocant->name if (blessed($invocant) && $value ne $invocant->name && $invocant->is_static)
&& $invocant->is_static)
{ {
ThrowUserError('fieldvalue_not_editable', ThrowUserError('fieldvalue_not_editable', { field => $field, old_value => $invocant });
{ field => $field, old_value => $invocant });
} }
ThrowUserError('fieldvalue_undefined') if !defined $value || $value eq ""; ThrowUserError('fieldvalue_undefined') if !defined $value || $value eq "";
@ -556,24 +540,25 @@ sub _check_value {
if length($value) > MAX_FIELD_VALUE_SIZE; if length($value) > MAX_FIELD_VALUE_SIZE;
my $exists = $invocant->type($field)->new({ name => $value }); my $exists = $invocant->type($field)->new({ name => $value });
if ($exists && (!blessed($invocant) || $invocant->id != $exists->id)) { if ($exists && (!blessed($invocant) || $invocant->id != $exists->id))
ThrowUserError('fieldvalue_already_exists', {
{ field => $field, value => $exists }); ThrowUserError('fieldvalue_already_exists', { field => $field, value => $exists });
} }
return $value; return $value;
} }
sub _check_sortkey { sub _check_sortkey
{
my ($invocant, $value) = @_; my ($invocant, $value) = @_;
$value = trim($value); $value = trim($value);
return 0 if !$value; return 0 if !$value;
# Store for the error message in case detaint_natural clears it. # Store for the error message in case detaint_natural clears it.
my $orig_value = $value; my $orig_value = $value;
detaint_natural($value) detaint_natural($value) || ThrowUserError('fieldvalue_sortkey_invalid', {
|| ThrowUserError('fieldvalue_sortkey_invalid', sortkey => $orig_value,
{ sortkey => $orig_value, field => $invocant->field,
field => $invocant->field }); });
return $value; return $value;
} }

View File

@ -215,24 +215,27 @@ if ($action eq 'edit') {
# #
# action='update' -> update the field value # action='update' -> update the field value
# #
if ($action eq 'update') { if ($action eq 'update')
{
check_token_data($token, 'edit_field_value'); check_token_data($token, 'edit_field_value');
$vars->{'value_old'} = $value->name; $vars->{value_old} = $value->name;
my $visibility_values; if ($value->can('set_timetracking'))
if (!($value->is_static || $value->is_default)) { {
$value->set_is_active($cgi->param('is_active'));
$value->set_name($cgi->param('value_new'));
$visibility_values = [ $cgi->param('visibility_value_id') ];
}
if ($value->can('set_timetracking')) {
$value->set_timetracking($cgi->param('timetracking') ? 1 : 0); $value->set_timetracking($cgi->param('timetracking') ? 1 : 0);
} }
$value->set_sortkey($cgi->param('sortkey')); $value->set_sortkey($cgi->param('sortkey'));
$vars->{'changes'} = $value->update(); if (!($value->is_static || $value->is_default))
my $ch = $value->set_visibility_values($visibility_values); {
$vars->{'changes'}->{'visibility_values'} = $ch if $visibility_values && $ch; $value->set_is_active($cgi->param('is_active'));
$value->set_name($cgi->param('value_new'));
if ($value->field->value_field)
{
$vars->{changes}->{visibility_values} = $value->set_visibility_values([ $cgi->param('visibility_value_id') ]);
}
}
delete_token($token); delete_token($token);
$vars->{'message'} = 'field_value_updated'; $vars->{changes} = $value->update;
$vars->{message} = 'field_value_updated';
display_field_values($vars); display_field_values($vars);
} }

View File

@ -401,11 +401,6 @@ $vars->{'product'} = $product;
$vars->{product_flag_types} = $types; $vars->{product_flag_types} = $types;
} }
$vars->{'priority'} = Bugzilla->get_field('priority')->legal_value_names;
$vars->{'bug_severity'} = Bugzilla->get_field('bug_severity')->legal_value_names;
$vars->{'rep_platform'} = Bugzilla->get_field('rep_platform')->legal_value_names if Bugzilla->params->{useplatform};
$vars->{'op_sys'} = Bugzilla->get_field('op_sys')->legal_value_names if Bugzilla->params->{useopsys};
$vars->{'assigned_to'} = formvalue('assigned_to'); $vars->{'assigned_to'} = formvalue('assigned_to');
$vars->{'assigned_to_disabled'} = !$has_editbugs; $vars->{'assigned_to_disabled'} = !$has_editbugs;
$vars->{'cc_disabled'} = 0; $vars->{'cc_disabled'} = 0;
@ -615,7 +610,6 @@ unless ($has_editbugs || $has_canconfirm) {
} }
$vars->{bug_status} = \@status; $vars->{bug_status} = \@status;
$vars->{resolution} = [ grep ($_, @{Bugzilla->get_field('resolution')->legal_value_names}) ];
# Get the default from a template value if it is legitimate. # Get the default from a template value if it is legitimate.
# Otherwise, and only if the user has privs, set the default # Otherwise, and only if the user has privs, set the default

View File

@ -424,6 +424,9 @@ sub install_update_fielddefs
# Bug 90854 - Тип поля "ссылка во внешнюю систему по ID" # Bug 90854 - Тип поля "ссылка во внешнюю систему по ID"
$dbh->bz_add_column('fielddefs', url => {TYPE => 'VARCHAR(255)'}); $dbh->bz_add_column('fielddefs', url => {TYPE => 'VARCHAR(255)'});
# Nullable field property
$dbh->bz_add_column('fielddefs', nullable => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
# Bug 70605 - Кэширование зависимостей полей для поиска и формы бага на клиентской стороне # Bug 70605 - Кэширование зависимостей полей для поиска и формы бага на клиентской стороне
if (!$dbh->bz_column_info('fielddefs', 'delta_ts')) if (!$dbh->bz_column_info('fielddefs', 'delta_ts'))
{ {

View File

@ -397,8 +397,8 @@ function checkWorktime(inp)
[% IF Param('useopsys') %] [% IF Param('useopsys') %]
<tr> <tr>
[% INCLUDE bug/field.html.tmpl [% INCLUDE bug/field.html.tmpl
bug = default, field = select_fields.op_sys, editable = 1, bug = default, field = select_fields.op_sys, editable = 1,
value = default.op_sys %] value = default.op_sys %]
</tr> </tr>
[% END %] [% END %]
</tbody> </tbody>
@ -406,8 +406,9 @@ function checkWorktime(inp)
<tbody class="expert_fields"> <tbody class="expert_fields">
<tr> <tr>
[% IF Param('usetargetmilestone') && Param('letsubmitterchoosemilestone') %] [% IF Param('usetargetmilestone') && Param('letsubmitterchoosemilestone') %]
[% sel = { description => 'Target Milestone', name => 'target_milestone' } %] [% INCLUDE bug/field.html.tmpl
[% INCLUDE select %] bug = default, field = select_fields.target_milestone, editable = 1,
value = default.target_milestone %]
[% ELSE %] [% ELSE %]
<td colspan="2">&nbsp;</td> <td colspan="2">&nbsp;</td>
[% END %] [% END %]
@ -445,8 +446,7 @@ function checkWorktime(inp)
<tr> <tr>
[% IF bug_status.size <= 1 %] [% IF bug_status.size <= 1 %]
<input type="hidden" name="bug_status" <input type="hidden" name="bug_status" value="[% default.bug_status FILTER html %]" />
value="[% default.bug_status FILTER html %]">
<th>Initial State:</th> <th>Initial State:</th>
<td>[% default.bug_status FILTER html %]</td> <td>[% default.bug_status FILTER html %]</td>
[% ELSE %] [% ELSE %]
@ -461,15 +461,8 @@ function checkWorktime(inp)
</tr> </tr>
<tr id="resolution_container" style="display:none"> <tr id="resolution_container" style="display:none">
<th><a href="page.cgi?id=fields.html#resolution">Resolution</a>:</th> [% INCLUDE bug/field.html.tmpl
<td> bug = default, field = select_fields.resolution, editable = 1 %]
<select name="resolution" id="resolution">
[%- FOREACH x = resolution %]
[% NEXT IF x == "MOVED" %]
<option value="[% x FILTER html %]">[% x FILTER html %]</option>
[% END %]
</select>
</td>
</tr> </tr>
<tr><td colspan="4"><hr /></td></tr> <tr><td colspan="4"><hr /></td></tr>

View File

@ -57,14 +57,10 @@
[%- '</a>' IF (!field.custom || desc_url) %] [%- '</a>' IF (!field.custom || desc_url) %]
[% '</label>' IF editable %] [% '</label>' IF editable %]
</th> </th>
[% END %] <td class="field_value [% ' bz_hidden_field' IF hidden %]"
[% IF NOT no_tds %]
<td class="field_value [% ' bz_hidden_field' IF hidden %]"
id="field_container_[% field.name FILTER html %]" id="field_container_[% field.name FILTER html %]"
[% " colspan=\"$value_span\"" FILTER none IF value_span %]> [% " colspan=\"$value_span\"" FILTER none IF value_span %]>
[% END %] [% END %]
[% Hook.process('start_field_column') %]
[% IF editable %] [% IF editable %]
[% SWITCH field.type %] [% SWITCH field.type %]
[% CASE constants.FIELD_TYPE_FREETEXT %] [% CASE constants.FIELD_TYPE_FREETEXT %]
@ -239,11 +235,10 @@
[% ELSIF field.type == constants.FIELD_TYPE_TEXTAREA %] [% ELSIF field.type == constants.FIELD_TYPE_TEXTAREA %]
<div class="uneditable_textarea">[% value FILTER html FILTER wrap_comment %]</div> <div class="uneditable_textarea">[% value FILTER html FILTER wrap_comment %]</div>
[% ELSIF field.type == constants.FIELD_TYPE_BUG_ID %] [% ELSIF field.type == constants.FIELD_TYPE_BUG_ID %]
[% IF bug.${field.name} %] [% IF bug.${field.name} %]
[% bug.${field.name} FILTER bug_link(bug.${field.name}) FILTER none %] [% bug.${field.name} FILTER bug_link(bug.${field.name}) FILTER none %]
[% END %] [% END %]
[% ELSE %] [% ELSE %]
[% value.join(', ') FILTER html %] [% value.join(', ') FILTER html %]
[% END %] [% END %]
[% Hook.process('end_field_column') %]
[% '</td>' IF NOT no_tds %] [% '</td>' IF NOT no_tds %]

View File

@ -350,8 +350,10 @@
</li> </li>
[% END %] [% END %]
[% IF changes.sortkey %] [% IF changes.sortkey %]
<li>Sortkey updated to <li>Sortkey updated to <em>[% changes.sortkey.1 | html %]</em>.</li>
<em>[% changes.sortkey.1 | html %]</em>.</li> [% END %]
[% IF changes.isactive %]
<li>It is now [% IF changes.isactive.1 %]enabled[% ELSE %]disabled[% END %] for selection.</li>
[% END %] [% END %]
[% IF changes.visibility_values.defined %] [% IF changes.visibility_values.defined %]
[% IF value.visibility_values.size > 0 %] [% IF value.visibility_values.size > 0 %]