diff options
-rw-r--r-- | Bugzilla/Field.pm | 48 | ||||
-rwxr-xr-x | customfield.pl | 89 | ||||
-rw-r--r-- | editfields.cgi | 120 | ||||
-rw-r--r-- | template/en/default/admin/custom_fields/create.html.tmpl | 111 | ||||
-rw-r--r-- | template/en/default/admin/custom_fields/edit.html.tmpl | 99 | ||||
-rw-r--r-- | template/en/default/admin/custom_fields/list.html.tmpl | 63 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 30 |
7 files changed, 462 insertions, 98 deletions
diff --git a/Bugzilla/Field.pm b/Bugzilla/Field.pm index c6c889957..cd510471d 100644 --- a/Bugzilla/Field.pm +++ b/Bugzilla/Field.pm @@ -93,6 +93,8 @@ use constant DB_COLUMNS => ( 'description', 'type', 'custom', + 'mailhead', + 'sortkey', 'obsolete', 'enter_bug', ); @@ -216,6 +218,31 @@ sub custom { return $_[0]->{custom} } =over +=item C<in_new_bugmail> + +a boolean specifying whether or not the field is displayed in bugmail +for newly-created bugs; + +=back + +=cut + +sub in_new_bugmail { return $_[0]->{mailhead} } + +=over + +=item C<sortkey> + +an integer specifying the sortkey of the field. + +=back + +=cut + +sub sortkey { return $_[0]->{sortkey} } + +=over + =item C<obsolete> a boolean specifying whether or not the field is obsolete; @@ -256,8 +283,14 @@ Params: This function takes named parameters in a hashref: C<desc> - string - The field label to display in the UI. C<in_new_bugmail> - boolean - Whether this field appears at the top of the bugmail for a newly-filed bug. + + The following parameters are only available on field creation: C<custom> - boolean - True if this is a Custom Field. The field will be added to the C<bugs> table if it does not exist. + C<sortkey> - integer - The sortkey of the field. + C<editable_on_enter_bug> - boolean - Whether this field is + editable on the bug creation form. + C<is_obsolete> - boolean - Whether this field is obsolete. Returns: a C<Bugzilla::Field> object. @@ -267,12 +300,16 @@ Returns: a C<Bugzilla::Field> object. sub create_or_update { my ($params) = @_; - + my $custom = $params->{custom} ? 1 : 0; my $name = $params->{name}; my $in_new_bugmail = $params->{in_new_bugmail} ? 1 : 0; + my $sortkey = $params->{sortkey} || 0; + my $enter_bug = $params->{editable_on_enter_bug} ? 1 : 0; + my $is_obsolete = $params->{is_obsolete} ? 1 : 0; # Some day we'll allow invocants to specify the field type. + # We don't care about $params->{type} yet. my $type = $custom ? FIELD_TYPE_FREETEXT : FIELD_TYPE_UNKNOWN; my $field = new Bugzilla::Field({name => $name}); @@ -285,16 +322,15 @@ sub create_or_update { undef, $params->{desc}, $in_new_bugmail, $field->id); } else { - # Some day we'll allow invocants to specify the sort key. - my ($sortkey) = $dbh->selectrow_array( + $sortkey ||= $dbh->selectrow_array( "SELECT MAX(sortkey) + 100 FROM fielddefs") || 100; # Add the field to the list of fields at this Bugzilla installation. $dbh->do("INSERT INTO fielddefs (name, description, sortkey, type, - custom, mailhead) - VALUES (?, ?, ?, ?, ?, ?)", undef, + custom, mailhead, obsolete, enter_bug) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", undef, $name, $params->{desc}, $sortkey, $type, $custom, - $in_new_bugmail); + $in_new_bugmail, $is_obsolete, $enter_bug); } if (!$dbh->bz_column_info('bugs', $name) && $custom) { diff --git a/customfield.pl b/customfield.pl deleted file mode 100755 index b5b9fd07b..000000000 --- a/customfield.pl +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/perl -wT -# -*- Mode: perl; indent-tabs-mode: nil -*- -# -# 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 Netscape Communications -# Corporation. Portions created by Netscape are -# Copyright (C) 1998 Netscape Communications Corporation. All -# Rights Reserved. -# -# Contributor(s): Myk Melez <myk@mozilla.org> - -################################################################################ -# Script Initialization -################################################################################ - -use strict; - -use lib "."; - -use Bugzilla; -use Bugzilla::Field; -use Getopt::Long; - -my ($name, $desc); -my $result = GetOptions("name=s" => \$name, - "description|desc=s" => \$desc); - -if (!$name or !$desc) { - my $command = - $^O =~ /MSWin32/i ? "perl -T customfield.pl" : "./customfield.pl"; - print <<END; -Usage: - - Use this script to add a custom field to your Bugzilla installation - by invoking it with the --name and --desc command-line options: - - $command --name=<field_name> --desc="<field description>" - - <field_name> is the name of the custom field in the database. - The string "cf_" will be prepended to this name to distinguish - the field from standard fields. This name must conform to the - naming rules for the database server you use. - - <field description> is a short string describing the field. It will - be displayed to Bugzilla users in several parts of Bugzilla's UI, - for example as the label for the field on the "show bug" page. - -Warning: - - Custom fields can make Bugzilla less usable. See this URL - for alternatives to custom fields: - - http://www.gerv.net/hacking/custom-fields.html - - You should try to implement applicable alternatives before using - this script to add a custom field. -END - - exit; -} - -# Prepend cf_ to the custom field name to distinguish it from standard fields. -$name =~ /^cf_/ - or $name = "cf_" . $name; - -# Exit gracefully if there is already a field with the given name. -if ( new Bugzilla::Field({name => $name}) ) { - print "There is already a field named $name. Please choose " . - "a different name.\n"; - exit; -} - - -# Create the field. -print "Creating custom field $name ...\n"; -Bugzilla::Field::create_or_update( - {name => $name, desc => $desc, custom => 1}); -print "Custom field $name created.\n"; diff --git a/editfields.cgi b/editfields.cgi new file mode 100644 index 000000000..17db14092 --- /dev/null +++ b/editfields.cgi @@ -0,0 +1,120 @@ +#!/usr/bin/perl -wT +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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. +# +# Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; +use Bugzilla::Field; + +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; +my $vars = {}; + +# Make sure the user is logged in and is an administrator. +my $user = Bugzilla->login(LOGIN_REQUIRED); +$user->in_group('admin') + || ThrowUserError('auth_failure', {group => 'admin', + action => 'edit', + object => 'custom_fields'}); + +my $action = trim($cgi->param('action') || ''); + +print $cgi->header(); + +# List all existing custom fields if no action is given. +if (!$action) { + $vars->{'custom_fields'} = [Bugzilla->get_fields({'custom' => 1})]; + + $template->process('admin/custom_fields/list.html.tmpl', $vars) + || ThrowTemplateError($template->error()); +} +# Interface to add a new custom field. +elsif ($action eq 'add') { + $template->process('admin/custom_fields/create.html.tmpl') + || ThrowTemplateError($template->error()); +} +elsif ($action eq 'new') { + my $name = clean_text($cgi->param('name') || ''); + my $desc = clean_text($cgi->param('desc') || ''); + # For now, there is only one type available for custom fields. + # In the future, we will have to look at $cgi->param('type'). + my $type = FIELD_TYPE_FREETEXT; + my $sortkey = $cgi->param('sortkey') || 0; + + # Validate these fields. + $name || ThrowUserError('customfield_missing_name'); + # Prepend cf_ to the custom field name to distinguish it from standard fields. + if ($name !~ /^cf_/) { + $name = 'cf_' . $name; + } + my $field = new Bugzilla::Field({'name' => $name}); + ThrowUserError('customfield_already_exists', {'field' => $field }) if $field; + + $desc || ThrowUserError('customfield_missing_description', {'name' => $name}); + + my $skey = $sortkey; + detaint_natural($sortkey) + || ThrowUserError('customfield_invalid_sortkey', {'name' => $name, + 'sortkey' => $skey}); + + # All fields have been validated. We can create this new custom field. + trick_taint($name); + trick_taint($desc); + + $vars->{'name'} = $name; + $vars->{'desc'} = $desc; + $vars->{'sortkey'} = $sortkey; + $vars->{'type'} = $type; + $vars->{'custom'} = 1; + $vars->{'in_new_bugmail'} = $cgi->param('new_bugmail') ? 1 : 0; + $vars->{'editable_on_enter_bug'} = $cgi->param('enter_bug') ? 1 : 0; + $vars->{'is_obsolete'} = $cgi->param('obsolete') ? 1 : 0; + + Bugzilla::Field::create_or_update($vars); + + $vars->{'custom_fields'} = [Bugzilla->get_fields({'custom' => 1})]; + + $template->process('admin/custom_fields/list.html.tmpl', $vars) + || ThrowTemplateError($template->error()); +} +elsif ($action eq 'edit') { + my $name = $cgi->param('name') || ThrowUserError('customfield_missing_name'); + trick_taint($name); + my @field = Bugzilla->get_fields({'name' => $name, 'custom' => 1}); + scalar(@field) || ThrowUserError('customfield_nonexistent', {'name' => $name}); + + $vars->{'field'} = $field[0]; + + $template->process('admin/custom_fields/edit.html.tmpl', $vars) + || ThrowTemplateError($template->error()); +} +elsif ($action eq 'update') { + die "not yet implemented...\n"; +} +elsif ($action eq 'del') { + die "not yet implemented...\n"; +} +elsif ($action eq 'delete') { + die "not yet implemented...\n"; +} +else { + ThrowUserError('no_valid_action', {'field' => 'custom_field'}); +} diff --git a/template/en/default/admin/custom_fields/create.html.tmpl b/template/en/default/admin/custom_fields/create.html.tmpl new file mode 100644 index 000000000..b366371bd --- /dev/null +++ b/template/en/default/admin/custom_fields/create.html.tmpl @@ -0,0 +1,111 @@ +[%# 1.0@bugzilla.org %] +[%# 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. + # + # Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + #%] + +[%# INTERFACE: + # none + #%] + +[% PROCESS "global/field-descs.none.tmpl" %] + +[% PROCESS global/header.html.tmpl + title = "Add a new Custom Field" + onload = "document.getElementById('new_bugmail').disabled = true;" %] + +<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 + adding many custom fields, and then their users complain that the interface + is "too complex". Please think carefully before adding any custom fields. + It may be the case that [% terms.Bugzilla %] already does what you need, + and you just haven't enabled the correct feature yet. + + <ul> + <li>Custom field names must begin with "cf_" to distinguish them from standard + fields. If you omit "cf_" from the name, it will automatically be appended.</li> + <li>Descriptions are a very short string describing the field and will be used + as the label for this field in the user interface.</li> + </ul> + <br> +</p> + +<form id="add_field" action="editfields.cgi" method="GET"> + <table border="0" cellspacing="0" cellpadding="5"> + <tr> + <th align="right"><label for="name">Name:</label></th> + <td> + <input type="text" id="name" name="name" value="cf_" size="40" maxlength="64"> + </td> + + <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" + onchange="toggleCheckbox(this, 'new_bugmail');"> + </td> + </tr> + <tr> + <th align="right"><label for="desc">Description:</label></th> + <td><input type="text" id="desc" name="desc" value="" size="40"></td> + + <th align="right"> + <label for="new_bugmail">Displayed in bugmail for new [% terms.bugs %]:</label> + </th> + <td><input type="checkbox" id="new_bugmail" name="new_bugmail" value="1"></td> + </tr> + <tr> + <th align="right"><label for="type">Type:</label></th> + <td> + [%# Only one field type is valid right now. But let's prepare the UI + # for future new types. %] + <select id="type" name="type"> + <option value="FIELD_TYPE_FREETEXT">Free Text</option> + </select> + </td> + + <th align="right"><label for="obsolete">Is obsolete:</label></th> + <td><input type="checkbox" id="obsolete" name="obsolete" value="1"></td> + </tr> + <tr> + <th align="right"><label for="sortkey">Sortkey:</label></th> + <td> + <input type="text" id="sortkey" name="sortkey" value="0" size="6" maxlength="6"> + </td> + + <th> </th> + <td> </td> + </tr> + </table> + <br> + <input type="hidden" name="action" value="new"> + <input type="submit" id="create" value="Create"> +</form> + +<p> + <a href="editfields.cgi">Back to the list of existing custom fields</a> +</p> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/custom_fields/edit.html.tmpl b/template/en/default/admin/custom_fields/edit.html.tmpl new file mode 100644 index 000000000..cb7b56a0a --- /dev/null +++ b/template/en/default/admin/custom_fields/edit.html.tmpl @@ -0,0 +1,99 @@ +[%# 1.0@bugzilla.org %] +[%# 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. + # + # Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + #%] + +[%# INTERFACE: + # none + #%] + +[% PROCESS "global/field-descs.none.tmpl" %] + +[% title = BLOCK %] + Edit the Custom Field '[% field.name FILTER html %]' ([% field.description FILTER html %]) +[% END %] + +[% PROCESS global/header.html.tmpl + title = title + onload = "toggleCheckbox(document.getElementById('enter_bug'), 'new_bugmail');" %] + +<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. +</p> + +<form id="edit_field" action="editfields.cgi" method="GET"> + <table border="0" cellspacing="0" cellpadding="5"> + <tr> + <th align="right">Name:</th> + <td>[% field.name FILTER html %]</td> + + <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> + </tr> + <tr> + <th align="right"><label for="desc">Description:</label></th> + <td><input type="text" id="desc" name="desc" size="40" + value="[% field.description FILTER html %]"></td> + + <th align="right"> + <label for="new_bugmail">Displayed in bugmail for new [% terms.bugs %]:</label> + </th> + <td><input type="checkbox" id="new_bugmail" name="new_bugmail" value="1" + [%- " checked" IF field.mailhead %]></td> + </tr> + <tr> + <th align="right">Type:</th> + <td>Free Text</td> + + <th align="right"><label for="obsolete">Is obsolete:</label></th> + <td><input type="checkbox" id="obsolete" name="obsolete" value="1" + [%- " checked" IF field.obsolete %]></td> + </tr> + <tr> + <th align="right"><label for="sortkey">Sortkey:</label></th> + <td> + <input type="text" id="sortkey" name="sortkey" size="6" maxlength="6" + value="[% field.sortkey FILTER html %]"> + </td> + + <th> </th> + <td> </td> + </tr> + </table> + <br> + <input type="hidden" name="action" value="update"> + <input type="hidden" name="name" value="[% field.name FILTER html %]"> + <input type="submit" id="edit" value="Submit" disabled="disabled"> +</form> + +<p> + <a href="editfields.cgi">Back to the list of existing custom fields</a> +</p> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/custom_fields/list.html.tmpl b/template/en/default/admin/custom_fields/list.html.tmpl new file mode 100644 index 000000000..e02609dd3 --- /dev/null +++ b/template/en/default/admin/custom_fields/list.html.tmpl @@ -0,0 +1,63 @@ +[%# 1.0@bugzilla.org %] +[%# 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. + # + # Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + #%] + +[%# INTERFACE: + # custom_fields: a list of Bugzilla::Field objects, representing custom fields. + #%] + +[% PROCESS "global/field-descs.none.tmpl" %] + +[% PROCESS global/header.html.tmpl title = "Custom Fields" %] + +[% columns = [ + { + name => "name" + heading => "Edit custom field..." + contentlink => "editfields.cgi?action=edit&name=%%name%%" + }, + { + name => "description" + heading => "Description" + }, + { + name => "sortkey" + heading => "Sortkey" + }, + { + name => "enter_bug" + heading => "Editable on Bug Creation" + }, + { + name => "mailhead" + heading => "In Bugmail on Bug Creation" + }, + { + name => "obsolete" + heading => "Is Obsolete" + } + ] +%] + +[% PROCESS admin/table.html.tmpl + columns = columns + data = custom_fields +%] + +<p> + <a href="editfields.cgi?action=add">Add a new custom field</a> +</p> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index e67c1a81c..e07be8846 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -158,6 +158,8 @@ classifications [% ELSIF object == "components" %] components + [% ELSIF object == "custom_fields" %] + custom fields [% ELSIF object == "flagtypes" %] flag types [% ELSIF object == "group_access" %] @@ -307,9 +309,27 @@ Product [% product FILTER html %] does not have a component named [% name FILTER html %]. - [% ELSIF error == "product_doesnt_exist" %] - [% title = "Specified Product Does Not Exist" %] - The product '[% product FILTER html %]' does not exist. + [% ELSIF error == "customfield_already_exists" %] + [% title = "Field Already Exists" %] + The field '[% field.name FILTER html %]' ([% field.description FILTER html %]) + already exists. Please choose another name. + + [% ELSIF error == "customfield_nonexistent" %] + [% title = "Unknown Custom Field" %] + There is no custom field with the name '[% name FILTER html %]'. + + [% ELSIF error == "customfield_invalid_sortkey" %] + [% title = "Invalid Sortkey for Field" %] + The sortkey [% sortkey FILTER html %] that you have provided for + the '[% name FILTER html %]' field is not a valid positive integer. + + [% ELSIF error == "customfield_missing_description" %] + [% title = "Missing Description for Field" %] + You must enter a description for the '[% name FILTER html %]' field. + + [% ELSIF error == "customfield_missing_name" %] + [% title = "Missing Name for Field" %] + You must enter a name for this field. [% ELSIF error == "dependency_loop_multi" %] [% title = "Dependency Loop Detected" %] @@ -1086,6 +1106,10 @@ Patches cannot be more than [% Param('maxpatchsize') %] KB in size. Try breaking your patch into several pieces. + [% ELSIF error == "product_doesnt_exist" %] + [% title = "Specified Product Does Not Exist" %] + The product '[% product FILTER html %]' does not exist. + [% ELSIF error == "product_votes_per_bug_must_be_nonnegative" %] [% title = "Maximum Votes Must Be Non-negative" %] [% admindocslinks = {'voting.html' => 'Setting up the voting feature'} %] |