Bug 59592 - Codebomb + Bug 40933

Refactor chfieldfrom/to/value search, add search by comments
Minor refactor query.cgi, remove logic duplication - useless editable_bug_fields()
Allow editing non-custom fields (for example, make them obsolete)


git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@639 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2010-02-02 16:57:14 +00:00
parent a0f62e9309
commit c353257d8e
7 changed files with 282 additions and 212 deletions

View File

@ -3172,21 +3172,12 @@ sub update_comment {
$current_comment_obj[0]->{'body'} = $new_comment;
}
# Represents which fields from the bugs table are handled by process_bug.cgi.
sub editable_bug_fields {
my @fields = Bugzilla->dbh->bz_table_columns('bugs');
# Obsolete custom fields are not editable.
my @obsolete_fields = Bugzilla->get_fields({obsolete => 1, custom => 1});
@obsolete_fields = map { $_->name } @obsolete_fields;
foreach my $remove ("bug_id", "reporter", "creation_ts", "delta_ts", "lastdiffed", @obsolete_fields) {
my $location = lsearch(\@fields, $remove);
# Custom multi-select fields are not stored in the bugs table.
splice(@fields, $location, 1) if ($location > -1);
}
# Sorted because the old @::log_columns variable, which this replaces,
# was sorted.
return sort(@fields);
}
# FIXME // Vitaliy Filippov <vitalif@mail.ru> 2010-02-01 19:23
# editable_bug_fields() is one more example of incorrect and unused generalization.
# It does not represent which fields from the bugs table are handled by process_bug.cgi,
# because process_bug.cgi itself does not use it at any point. In fact, it was used only
# in 2 places: 1) the field list for boolean charts 2) in BugMail.pm to avoid 'SELECT *'.
# Both of them are not related to "editable" at all.
# XXX - When Bug::update() will be implemented, we should make this routine
# a private method.

View File

@ -132,8 +132,7 @@ sub Send {
}
my %values = %{$dbh->selectrow_hashref(
'SELECT ' . join(',', editable_bug_fields()) . ', reporter,
lastdiffed AS start_time, LOCALTIMESTAMP(0) AS end_time
'SELECT *, lastdiffed AS start_time, LOCALTIMESTAMP(0) AS end_time
FROM bugs WHERE bug_id = ?',
undef, $id)};

View File

@ -111,12 +111,15 @@ sub COLUMNS {
# Next we define columns that have special SQL instead of just something
# like "bugs.bug_id".
my $actual_time = '(SUM(ldtime.work_time)'
. ' * COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id))';
# ---- vfilippov@custis.ru 2010-02-02
# The previous sql code:
# "(SUM(ldtime.work_time) * COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id))"
# was probably written by Australopithecus.
my $actual_time = '(SELECT SUM(ldtime.work_time) FROM longdescs ldtime WHERE ldtime.bug_id=bugs.bug_id)';
my %special_sql = (
deadline => $dbh->sql_date_format('bugs.deadline', '%Y-%m-%d'),
actual_time => $actual_time,
percentage_complete =>
"(CASE WHEN $actual_time + bugs.remaining_time = 0.0"
. " THEN 0.0"
@ -265,10 +268,6 @@ sub init {
"ON bugs.component_id = map_components.id";
}
if (grep($_ eq 'actual_time' || $_ eq 'percentage_complete', @fields)) {
push(@supptables, "LEFT JOIN longdescs AS ldtime " .
"ON ldtime.bug_id = bugs.bug_id");
}
### Testopia ###
if (grep($_ eq 'test_cases', @fields)){
push(@supptables, "LEFT JOIN test_case_bugs AS tcb " .
@ -423,139 +422,186 @@ sub init {
my $sql_chto = $chfieldto ? $dbh->quote(SqlifyDate($chfieldto)) :'';
my $sql_chvalue = $chvalue ne '' ? $dbh->quote($chvalue) : '';
trick_taint($sql_chvalue);
if(!@chfield) {
if ($sql_chfrom) {
my $term = "bugs.delta_ts >= $sql_chfrom";
push(@wherepart, $term);
$self->search_description({
field => 'delta_ts', type => 'greaterthaneq',
value => $chfieldfrom, term => $term,
});
}
if ($sql_chto) {
my $term = "bugs.delta_ts <= $sql_chto";
push(@wherepart, $term);
$self->search_description({
field => 'delta_ts', type => 'lessthaneq',
value => $chfieldto, term => $term,
});
}
} else {
my $bug_creation_clause;
my @list;
my @actlist;
foreach my $f (@chfield) {
if ($f eq "[Bug creation]") {
# Treat [Bug creation] differently because we need to look
# at bugs.creation_ts rather than the bugs_activity table.
my @l;
if ($sql_chfrom) {
my $term = "bugs.creation_ts >= $sql_chfrom";
push(@l, $term);
$self->search_description({
field => 'creation_ts', type => 'greaterthaneq',
value => $chfieldfrom, term => $term,
});
}
if ($sql_chto) {
my $term = "bugs.creation_ts <= $sql_chto";
push(@l, $term);
$self->search_description({
field => 'creation_ts', type => 'lessthaneq',
value => $chfieldto, term => $term,
});
}
$bug_creation_clause = "(" . join(' AND ', @l) . ")";
} else {
push(@actlist, get_field_id($f));
my $from_term = " AND actcheck.bug_when >= $sql_chfrom";
my $to_term = " AND actcheck.bug_when <= $sql_chto";
my $value_term = " AND actcheck.added = $sql_chvalue";
# ---- vfilippov@custis.ru 2010-02-01
# Search using bugs.delta_ts is not correct. It's "LAST changed in", not "Changed in".
my $bug_creation_clause;
my @list;
my @actlist;
my $seen_longdesc;
my $need_commenter;
foreach my $f (@chfield)
{
my $term;
if ($f eq "[Bug creation]")
{
# Treat [Bug creation] differently because we need to look
# at bugs.creation_ts rather than the bugs_activity table.
my @l;
if ($sql_chfrom) {
my $term = "bugs.creation_ts >= $sql_chfrom";
push(@l, $term);
$self->search_description({
field => 'creation_ts', type => 'greaterthaneq',
value => $chfieldfrom, term => $term,
});
}
if ($sql_chto) {
my $term = "bugs.creation_ts <= $sql_chto";
push(@l, $term);
$self->search_description({
field => 'creation_ts', type => 'lessthaneq',
value => $chfieldto, term => $term,
});
}
$bug_creation_clause = "(" . join(' AND ', @l) . ")";
}
# @actlist won't have any elements if the only field being searched
# is [Bug creation] (in which case we don't need bugs_activity).
if(@actlist) {
my $extra = " actcheck.bug_id = bugs.bug_id";
push(@list, "(actcheck.bug_when IS NOT NULL)");
my $from_term = " AND actcheck.bug_when >= $sql_chfrom";
$extra .= $from_term if $sql_chfrom;
my $to_term = " AND actcheck.bug_when <= $sql_chto";
$extra .= $to_term if $sql_chto;
my $value_term = " AND actcheck.added = $sql_chvalue";
$extra .= $value_term if $sql_chvalue;
push(@supptables, "LEFT JOIN bugs_activity AS actcheck " .
"ON $extra AND "
. $dbh->sql_in('actcheck.fieldid', \@actlist));
foreach my $field (@chfield) {
next if $field eq "[Bug creation]";
if ($sql_chvalue) {
$self->search_description({
field => $field, type => 'changedto',
value => $chvalue, term => $value_term,
});
elsif ($f eq 'longdesc' || $f eq 'longdescs.isprivate' || $f eq 'commenter')
{
# Treat comment properties differently because we need to look at longdescs table.
if ($sql_chvalue)
{
if ($f eq 'longdesc' && !$seen_longdesc)
{
# User is searching for a comment with specific text,
# but that has no sense if $chvalue was already used for comment privacy.
$seen_longdesc = [ $term = "INSTR(actcheck_comment.thetext, $sql_chvalue) > 0" ];
}
if ($sql_chfrom) {
$self->search_description({
field => $field, type => 'changedafter',
value => $chfieldfrom, term => $from_term,
});
elsif ($f eq 'commenter')
{
# User is searching for a comment with specific author
$need_commenter = [ $term = "actcheck_commenter.login_name = $sql_chvalue" ];
}
if ($sql_chvalue) {
elsif (!$seen_longdesc)
{
# User is searching for a private / non-private comment for specific period,
# but that has no sense if $chvalue was already used for comment text.
$seen_longdesc = [ $term = "actcheck_comment.isprivate = ".($chvalue ? 1 : 0) ];
}
if ($term)
{
$self->search_description({
field => $field, type => 'changedbefore',
value => $chfieldto, term => $to_term,
field => $f, type => 'changedto',
value => $chvalue, term => $term,
});
}
}
else
{
$seen_longdesc = [];
}
}
else
{
push @actlist, get_field_id($f);
$term = 1;
if ($sql_chvalue)
{
$self->search_description({
field => $f, type => 'changedto',
value => $chvalue, term => $value_term,
});
}
}
if ($term)
{
if ($sql_chfrom)
{
$self->search_description({
field => $f, type => 'changedafter',
value => $chfieldfrom, term => $from_term,
});
}
if ($sql_chto)
{
$self->search_description({
field => $f, type => 'changedbefore',
value => $chfieldto, term => $to_term,
});
}
}
# Now that we're done using @list to determine if there are any
# regular fields to search (and thus we need bugs_activity),
# add the [Bug creation] criterion to the list so we can OR it
# together with the others.
push(@list, $bug_creation_clause) if $bug_creation_clause;
push(@wherepart, "(" . join(" OR ", @list) . ")");
}
my $extra;
if (!$bug_creation_clause && !$seen_longdesc || @actlist)
{
$extra = "actcheck.bug_id = bugs.bug_id";
$extra .= $from_term if $sql_chfrom;
$extra .= $to_term if $sql_chto;
$extra .= $value_term if $sql_chvalue;
if (@actlist)
{
# Restrict bug activity to selected fields
$extra .= " AND " . $dbh->sql_in('actcheck.fieldid', \@actlist);
}
push @supptables, "LEFT JOIN bugs_activity AS actcheck ON $extra";
push @list, "(actcheck.bug_when IS NOT NULL)";
}
$extra = "actcheck_comment.bug_id = bugs.bug_id";
$extra .= " AND actcheck_comment.bug_when >= $sql_chfrom" if $sql_chfrom;
$extra .= " AND actcheck_comment.bug_when <= $sql_chto" if $sql_chto;
if ($seen_longdesc)
{
$extra .= " AND ".$seen_longdesc->[0];
}
push @supptables, "LEFT JOIN longdescs AS actcheck_comment ON $extra";
push @list, "(actcheck_comment.bug_when IS NOT NULL)";
if ($need_commenter)
{
push @supptables,
"LEFT JOIN profiles AS actcheck_commenter" .
" ON actcheck_commenter.userid=actcheck_comment.who AND $need_commenter";
}
# Now that we're done using @list to determine if there are any
# regular fields to search (and thus we need bugs_activity),
# add the [Bug creation] criterion to the list so we can OR it
# together with the others.
push(@list, $bug_creation_clause) if $bug_creation_clause;
push(@wherepart, "(" . join(" OR ", @list) . ")");
}
my $sql_deadlinefrom;
my $sql_deadlineto;
if ($user->is_timetracker) {
my $deadlinefrom;
my $deadlineto;
my $deadlinefrom;
my $deadlineto;
if ($params->param('deadlinefrom')){
$deadlinefrom = $params->param('deadlinefrom');
validate_date($deadlinefrom)
|| ThrowUserError('illegal_date', {date => $deadlinefrom,
format => 'YYYY-MM-DD'});
$sql_deadlinefrom = $dbh->quote($deadlinefrom);
trick_taint($sql_deadlinefrom);
my $term = "bugs.deadline >= $sql_deadlinefrom";
push(@wherepart, $term);
$self->search_description({
field => 'deadline', type => 'greaterthaneq',
value => $deadlinefrom, term => $term,
});
}
if ($params->param('deadlinefrom')){
$deadlinefrom = $params->param('deadlinefrom');
validate_date($deadlinefrom)
|| ThrowUserError('illegal_date', {date => $deadlinefrom,
format => 'YYYY-MM-DD'});
$sql_deadlinefrom = $dbh->quote($deadlinefrom);
trick_taint($sql_deadlinefrom);
my $term = "bugs.deadline >= $sql_deadlinefrom";
push(@wherepart, $term);
$self->search_description({
field => 'deadline', type => 'greaterthaneq',
value => $deadlinefrom, term => $term,
});
}
if ($params->param('deadlineto')){
$deadlineto = $params->param('deadlineto');
validate_date($deadlineto)
|| ThrowUserError('illegal_date', {date => $deadlineto,
format => 'YYYY-MM-DD'});
$sql_deadlineto = $dbh->quote($deadlineto);
trick_taint($sql_deadlineto);
my $term = "bugs.deadline <= $sql_deadlineto";
push(@wherepart, $term);
$self->search_description({
field => 'deadline', type => 'lessthaneq',
value => $deadlineto, term => $term,
});
}
if ($params->param('deadlineto')){
$deadlineto = $params->param('deadlineto');
validate_date($deadlineto)
|| ThrowUserError('illegal_date', {date => $deadlineto,
format => 'YYYY-MM-DD'});
$sql_deadlineto = $dbh->quote($deadlineto);
trick_taint($sql_deadlineto);
my $term = "bugs.deadline <= $sql_deadlineto";
push(@wherepart, $term);
$self->search_description({
field => 'deadline', type => 'lessthaneq',
value => $deadlineto, term => $term,
});
}
}
my @textfields = ("short_desc", "longdesc", "bug_file_loc", "status_whiteboard");

View File

@ -80,10 +80,6 @@ elsif ($action eq 'new') {
}
elsif ($action eq 'edit') {
my $name = $cgi->param('name') || ThrowUserError('field_missing_name');
# Custom field names must start with "cf_".
if ($name !~ /^cf_/) {
$name = 'cf_' . $name;
}
my $field = new Bugzilla::Field({'name' => $name});
$field || ThrowUserError('customfield_nonexistent', {'name' => $name});
@ -99,21 +95,22 @@ elsif ($action eq 'update') {
# Validate fields.
$name || ThrowUserError('field_missing_name');
# Custom field names must start with "cf_".
if ($name !~ /^cf_/) {
$name = 'cf_' . $name;
}
my $field = new Bugzilla::Field({'name' => $name});
$field || ThrowUserError('customfield_nonexistent', {'name' => $name});
$field->set_description($cgi->param('desc'));
$field->set_sortkey($cgi->param('sortkey'));
$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->set_value_field($cgi->param('value_field_id'));
if ($field->custom)
{
# TODO enter_bug could be edited for non-custom fields, too.
# At the moment, though, it has no effect for non-custom fields.
$field->set_enter_bug($cgi->param('enter_bug'));
$field->set_visibility_field($cgi->param('visibility_field_id'));
$field->set_visibility_value($cgi->param('visibility_value_id'));
$field->set_value_field($cgi->param('value_field_id'));
}
$field->update();
delete_token($token);
@ -129,6 +126,7 @@ elsif ($action eq 'del') {
# Validate field.
$name || ThrowUserError('field_missing_name');
# Do not allow deleting non-custom fields.
# Custom field names must start with "cf_".
if ($name !~ /^cf_/) {
$name = 'cf_' . $name;
@ -148,6 +146,7 @@ elsif ($action eq 'delete') {
# Validate fields.
$name || ThrowUserError('field_missing_name');
# Do not allow deleting non-custom fields.
# Custom field names must start with "cf_".
if ($name !~ /^cf_/) {
$name = 'cf_' . $name;
@ -158,10 +157,10 @@ elsif ($action eq 'delete') {
# Calling remove_from_db will check if field can be deleted.
# If the field cannot be deleted, it will throw an error.
$field->remove_from_db();
$vars->{'field'} = $field;
$vars->{'message'} = 'custom_field_deleted';
delete_token($token);
$template->process('admin/custom_fields/list.html.tmpl', $vars)

View File

@ -241,52 +241,45 @@ push(@$legal_resolutions, "---"); # Oy, what a hack.
# Another hack - this array contains "" for some reason. See bug 106589.
$vars->{'resolution'} = [grep ($_, @$legal_resolutions)];
my @chfields;
# Fields for boolean charts
my @fields = Bugzilla->get_fields({ obsolete => 0 });
@fields = sort {lc($a->description) cmp lc($b->description)} @fields;
$vars->{'fields'} = \@fields;
push @chfields, "[Bug creation]";
# "where one or more of the following changed:"
# ---- vfilippov@custis.ru 2010-02-01
# This is much much more correct than Bugzilla::Bug::editable_bug_fields().
# We only need to exclude final and automatic fields.
# FIXME remove hardcode
my %exclude = map { $_ => 1 } qw(
noop bug_id delta_ts creation_ts days_elapsed owner_idle_time
everconfirmed percentage_complete
);
$vars->{'chfield'} = [ sort grep { !$exclude{$_} } map { $_->name } @fields ];
# This is what happens when you have variables whose definition depends
# on the DB schema, and then the underlying schema changes...
foreach my $val (editable_bug_fields()) {
if ($val eq 'classification_id') {
$val = 'classification';
} elsif ($val eq 'product_id') {
$val = 'product';
} elsif ($val eq 'component_id') {
$val = 'component';
}
push @chfields, $val;
}
# Another hack...
unshift @{$vars->{fields}}, { name => "noop", description => "---" };
if (Bugzilla->user->in_group(Bugzilla->params->{'timetrackinggroup'})) {
push @chfields, "work_time";
} else {
@chfields = grep($_ ne "estimated_time", @chfields);
@chfields = grep($_ ne "remaining_time", @chfields);
}
@chfields = (sort(@chfields));
$vars->{'chfield'} = \@chfields;
$vars->{'bug_status'} = get_legal_field_values('bug_status');
$vars->{'rep_platform'} = get_legal_field_values('rep_platform') if Bugzilla->params->{useplatform};
$vars->{'op_sys'} = get_legal_field_values('op_sys') if Bugzilla->params->{useopsys};
$vars->{'priority'} = get_legal_field_values('priority');
# Legal values for select fields
$vars->{'bug_status'} = get_legal_field_values('bug_status');
$vars->{'priority'} = get_legal_field_values('priority');
$vars->{'bug_severity'} = get_legal_field_values('bug_severity');
if (Bugzilla->params->{useplatform})
{
# Only if turned on
$vars->{'rep_platform'} = get_legal_field_values('rep_platform');
}
if (Bugzilla->params->{useopsys})
{
# Only if turned on
$vars->{'op_sys'} = get_legal_field_values('op_sys');
}
# NB // vfilippov@custis.ru 2010-02-01
# Do we need to deny non-timetrackers searching on timetracking fields?
# Timetracking information is not a secret.
# Boolean charts
my @fields = Bugzilla->get_fields({ obsolete => 0 });
# If we're not in the time-tracking group, exclude time-tracking fields.
if (!Bugzilla->user->in_group(Bugzilla->params->{'timetrackinggroup'})) {
foreach my $tt_field (qw(estimated_time remaining_time work_time
percentage_complete deadline))
{
@fields = grep($_->name ne $tt_field, @fields);
}
}
@fields = sort {lc($a->description) cmp lc($b->description)} @fields;
unshift(@fields, { name => "noop", description => "---" });
$vars->{'fields'} = \@fields;
# Creating new charts - if the cmd-add value is there, we define the field
# value so the code sees it and creates the chart. It will attempt to select

View File

@ -20,7 +20,7 @@
[% PROCESS "global/field-descs.none.tmpl" %]
[% title = BLOCK %]
Edit the Custom Field '[% field.name FILTER html %]' ([% field.description FILTER html %])
Edit the [% "Custom" IF field.custom %] field '[% field.name FILTER html %]' ([% field.description FILTER html %])
[% END %]
[% javascript = BLOCK %]
@ -29,7 +29,7 @@
[% PROCESS global/header.html.tmpl
title = title
onload = "toggleCheckbox(document.getElementById('enter_bug'), 'new_bugmail');"
onload = field.custom ? "toggleCheckbox(document.getElementById('enter_bug'), 'new_bugmail');" : ""
javascript_urls = [ 'js/util.js' ]
doc_section = "custom-fields.html#edit-custom-fields"
%]
@ -45,12 +45,14 @@
<th align="right">Name:</th>
<td>[% field.name FILTER html %]</td>
[% IF field.custom %]
<th align="right">
<label for="enter_bug">Can be set on [% terms.bug %] creation:</label>
</th>
<td><input type="checkbox" id="enter_bug" name="enter_bug" value="1"
[%- " checked" IF field.enter_bug %]
onchange="toggleCheckbox(this, 'new_bugmail');"></td>
[% END %]
</tr>
<tr>
<th align="right"><label for="desc">Description:</label></th>
@ -78,6 +80,7 @@
value="[% field.sortkey FILTER html %]">
</td>
[% IF field.custom %]
<th align="right">
<label for="visibility_field_id">Field only appears when:</label>
</th>
@ -88,7 +91,7 @@
[% FOREACH sel_field = Bugzilla.get_fields({ is_select => 1 }) %]
[% NEXT IF sel_field.id == field.id %]
<option value="[% sel_field.id FILTER html %]"
[% ' selected="selected"'
[% ' selected="selected"'
IF sel_field.id == field.visibility_field.id %]>
[% sel_field.description FILTER html %]
([% sel_field.name FILTER html %])
@ -99,15 +102,16 @@
<select name="visibility_value_id" id="visibility_value_id">
[% FOREACH value = field.visibility_field.legal_values %]
<option value="[% value.id FILTER html %]"
[% ' selected="selected"'
[% ' selected="selected"'
IF field.visibility_value.id == value.id %]>
[% value.name FILTER html %]
</option>
[% END %]
[% END %]
</select>
</td>
[% END %]
</tr>
[% IF field.is_select %]
[% IF field.is_select AND field.name != 'product' %]
<tr>
<th>&nbsp;</th>
<td>
@ -115,6 +119,7 @@
legal values for this field</a>.
</td>
[% IF field.custom %]
<th>
<label for="value_field_id">
Field that controls the values<br>
@ -128,7 +133,7 @@
[% FOREACH sel_field = Bugzilla.get_fields({ is_select => 1 }) %]
[% NEXT IF sel_field.id == field.id %]
<option value="[% sel_field.id FILTER html %]"
[% ' selected="selected"'
[% ' selected="selected"'
IF sel_field.id == field.value_field.id %]>
[% sel_field.description FILTER html %]
([% sel_field.name FILTER html %])
@ -136,6 +141,7 @@
[% END %]
</select>
</td>
[% END %]
</tr>
[% END %]
<tr>
@ -143,14 +149,13 @@
<td>&nbsp;</td>
</tr>
</table>
<br>
<input type="hidden" name="action" value="update">
<input type="hidden" name="name" value="[% field.name FILTER html %]">
<input type="hidden" name="token" value="[% token FILTER html %]">
<input type="submit" id="edit" value="Submit">
</form>
[% IF field.obsolete %]
[% IF field.obsolete AND field.custom %]
<p>
<a href="editfields.cgi?action=del&amp;name=[% field.name FILTER html %]">Remove
this custom field from the database.</a><br>
@ -160,7 +165,7 @@
[% END %]
<p>
<a href="editfields.cgi">Back to the list of existing custom fields</a>
<a href="editfields.cgi">Back to the list of existing fields</a>
</p>
[% PROCESS global/footer.html.tmpl %]

View File

@ -65,7 +65,6 @@
%]
[% USE Bugzilla %]
[% custom_fields = Bugzilla.get_fields({ custom => 1 }) %]
[%# We want to display the type name of fields, not their type ID. %]
[% overrides.type = {} %]
@ -78,25 +77,63 @@
%]
[% END %]
[% overrides.action.obsolete = {
"1" => {
override_content => 1
content => "Delete"
override_contentlink => 1
contentlink => delete_contentlink
override_content => 1
content => "Delete"
override_contentlink => 1
contentlink => delete_contentlink
}
}
%]
%]
<h2>Custom fields</h2>
[% PROCESS admin/table.html.tmpl
columns = columns
overrides = overrides
data = custom_fields
data = Bugzilla.get_fields({custom => 1})
%]
<p>
<a href="editfields.cgi?action=add">Add a new custom field</a>
</p>
<h2>Standard fields</h2>
[% columns = [
{
name => "name"
heading => "Edit field..."
contentlink => "editfields.cgi?action=edit&amp;name=%%name%%"
},
{
name => "description"
heading => "Description"
},
{
name => "sortkey"
heading => "Sortkey"
},
{
name => "type"
heading => "Type"
},
{
name => "mailhead"
heading => "In ${terms.Bug}mail on $terms.Bug Creation"
},
{
name => "obsolete"
heading => "Is Obsolete"
},
]
%]
[% PROCESS admin/table.html.tmpl
columns = columns
overrides = overrides
data = Bugzilla.get_fields({custom => 0})
%]
[% PROCESS global/footer.html.tmpl %]