]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 344875: Implement a UI to manage custom fields and remove customfield.pl - Patch...
authorlpsolit%gmail.com <>
Sun, 20 Aug 2006 06:02:55 +0000 (06:02 +0000)
committerlpsolit%gmail.com <>
Sun, 20 Aug 2006 06:02:55 +0000 (06:02 +0000)
Bugzilla/Field.pm
customfield.pl [deleted file]
editfields.cgi [new file with mode: 0644]
template/en/default/admin/custom_fields/create.html.tmpl [new file with mode: 0644]
template/en/default/admin/custom_fields/edit.html.tmpl [new file with mode: 0644]
template/en/default/admin/custom_fields/list.html.tmpl [new file with mode: 0644]
template/en/default/global/user-error.html.tmpl

index c6c889957c1a4c3ab8a57a0e9b3ed2a1eb670fd8..cd510471d67b59c682162bdefa4b3c696ded8b38 100644 (file)
@@ -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 (executable)
index b5b9fd0..0000000
+++ /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 (file)
index 0000000..17db140
--- /dev/null
@@ -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 (file)
index 0000000..b366371
--- /dev/null
@@ -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>&nbsp;</th>
+      <td>&nbsp;</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 (file)
index 0000000..cb7b56a
--- /dev/null
@@ -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>&nbsp;</th>
+      <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="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 (file)
index 0000000..e02609d
--- /dev/null
@@ -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&amp;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 %]
index e67c1a81cfd3706c37c8ee5e4b1329d65428837a..e07be8846234493eace8848d310ecb773a9f7f5d 100644 (file)
       classifications
     [% ELSIF object == "components" %]
       components
+    [% ELSIF object == "custom_fields" %]
+      custom fields
     [% ELSIF object == "flagtypes" %]
       flag types
     [% ELSIF object == "group_access" %]
     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" %]
     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'} %]