From b8a8680e9d5ed7de541ab9f4e0275fa79a9a6d30 Mon Sep 17 00:00:00 2001 From: "mkanat%kerio.com" <> Date: Fri, 25 Feb 2005 06:48:05 +0000 Subject: Bug 281876: New Admin Interface to manage old Enum fields (Field Values) Patch By Max Kanat-Alexander r=joel, a=justdave --- editvalues.cgi | 386 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100755 editvalues.cgi (limited to 'editvalues.cgi') diff --git a/editvalues.cgi b/editvalues.cgi new file mode 100755 index 000000000..8231dab5b --- /dev/null +++ b/editvalues.cgi @@ -0,0 +1,386 @@ +#!/usr/bin/perl -wT +# 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): Max Kanat-Alexander + +# This is a script to edit the values of fields that have drop-down +# or select boxes. It is largely a copy of editmilestones.cgi, but +# with some cleanup. + +use strict; +use lib "."; + +use Bugzilla; +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Config qw(:DEFAULT :locations); + +# List of different tables that contain the changeable field values +# (the old "enums.") Keep them in alphabetical order by their +# English name from field-descs.html.tmpl. +# Format: Array of valid field names. +# Admins may add resolution and bug_status to this list, but they +# do so at their own risk. +our @valid_fields = ('op_sys', 'rep_platform', 'priority', 'bug_severity',); + +###################################################################### +# Subroutines +###################################################################### + +# Returns whether or not the specified table exists in the @tables array. +sub FieldExists ($) { + my ($field) = @_; + + return lsearch(\@valid_fields, $field) >= 0; +} + +# Same as FieldExists, but emits and error and dies if it fails. +sub FieldMustExist ($) { + my ($field)= @_; + + $field || + ThrowUserError('fieldname_not_specified'); + + # Is it a valid field to be editing? + FieldExists($field) || + ThrowUserError('fieldname_invalid', {'field' => $field}); +} + +# Returns if the specified value exists for the field specified. +sub ValueExists ($$) { + my ($field, $value) = @_; + FieldMustExist($field); + trick_taint($field); + # Value is safe because it's being passed only to a SELECT + # statement via a placeholder. + trick_taint($value); + + my $dbh = Bugzilla->dbh; + my $value_count = + $dbh->selectrow_array("SELECT COUNT(*) FROM $field " + . " WHERE value = ?", undef, $value); + + return $value_count; +} + +# Same check as ValueExists, emits an error text and dies if it fails. +sub ValueMustExist ($$) { + my ($field, $value)= @_; + + # Values may not be empty (it's very difficult to deal + # with empty values in the admin interface). + trim($value) || ThrowUserError('fieldvalue_not_specified'); + + # Does it exist in the DB? + ValueExists($field, $value) || + ThrowUserError('fieldvalue_doesnt_exist', {'value' => $value, + 'field' => $field}); +} + +###################################################################### +# Main Body Execution +###################################################################### + +# require the user to have logged in +Bugzilla->login(LOGIN_REQUIRED); + +my $dbh = Bugzilla->dbh; +my $cgi = Bugzilla->cgi; +my $template = Bugzilla->template; +my $vars = {}; + +print $cgi->header(); + +exists Bugzilla->user->groups->{'editcomponents'} || + ThrowUserError('auth_failure', {group => "editcomponents", + action => "edit", + object => "field values"}); + +# +# often-used variables +# +my $field = trim($cgi->param('field') || ''); +my $value = trim($cgi->param('value') || ''); +my $sortkey = trim($cgi->param('sortkey') || '0'); +my $action = trim($cgi->param('action') || ''); + + +# +# field = '' -> Show nice list of fields +# +unless ($field) { + # Convert @valid_fields into the format that select-field wants. + my @field_list = (); + foreach my $field_name (@valid_fields) { + push(@field_list, {name => $field_name}); + } + + $vars->{'fields'} = \@field_list; + $template->process("admin/fieldvalues/select-field.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + exit; +} + + +# +# action='' -> Show nice list of values. +# +unless ($action) { + FieldMustExist($field); + # Now we know the $field is valid. + trick_taint($field); + + my $fieldvalues = + $dbh->selectall_arrayref("SELECT value AS name, sortkey" + . " FROM $field ORDER BY sortkey, value", + {Slice =>{}}); + $vars->{'field'} = $field; + $vars->{'values'} = $fieldvalues; + $template->process("admin/fieldvalues/list.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + + exit; +} + + +# +# action='add' -> show form for adding new field value. +# (next action will be 'new') +# +if ($action eq 'add') { + FieldMustExist($field); + + $vars->{'value'} = $value; + $vars->{'field'} = $field; + $template->process("admin/fieldvalues/create.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + + exit; +} + + +# +# action='new' -> add field value entered in the 'action=add' screen +# +if ($action eq 'new') { + FieldMustExist($field); + trick_taint($field); + + # Cleanups and validity checks + $value || ThrowUserError('fieldvalue_undefined'); + + if (length($value) > 60) { + ThrowUserError('fieldvalue_name_too_long', + {'value' => $value}); + } + # Need to store in case detaint_natural() clears the sortkey + my $stored_sortkey = $sortkey; + if (!detaint_natural($sortkey)) { + ThrowUserError('fieldvalue_sortkey_invalid', + {'name' => $field, + 'sortkey' => $stored_sortkey}); + } + if (ValueExists($field, $value)) { + ThrowUserError('fieldvalue_already_exists', + {'field' => $field, + 'value' => $value}); + } + # Value is only used in a SELECT placeholder and through the HTML filter. + trick_taint($value); + + # Add the new field value. + my $sth = $dbh->prepare("INSERT INTO $field ( value, sortkey ) + VALUES ( ?, ? )"); + $sth->execute($value, $sortkey); + + unlink "$datadir/versioncache"; + + $vars->{'value'} = $value; + $vars->{'field'} = $field; + $template->process("admin/fieldvalues/created.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + + exit; +} + + +# +# action='del' -> ask if user really wants to delete +# (next action would be 'delete') +# +if ($action eq 'del') { + ValueMustExist($field, $value); + trick_taint($field); + trick_taint($value); + + # See if any bugs are still using this value. + $vars->{'bug_count'} = + $dbh->selectrow_array("SELECT COUNT(*) FROM bugs WHERE $field = ?", + undef, $value) || 0; + $vars->{'value_count'} = + $dbh->selectrow_array("SELECT COUNT(*) FROM $field"); + + $vars->{'value'} = $value; + $vars->{'field'} = $field; + $template->process("admin/fieldvalues/confirm-delete.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + + exit; +} + + +# +# action='delete' -> really delete the field value +# +if ($action eq 'delete') { + ValueMustExist($field, $value); + trick_taint($field); + trick_taint($value); + + # Check if there are any bugs that still have this value. + my $bug_ids = $dbh->selectcol_arrayref( + "SELECT bug_id FROM bugs WHERE $field = ?", undef, $value); + + if (scalar(@$bug_ids)) { + # You tried to delete a field that bugs are still using. + # You can't just delete the bugs. That's ridiculous. + $dbh->do('UNLOCK TABLES'); + ThrowUserError("fieldvalue_still_has_bugs", + { field => $field, value => $value, + count => scalar(@$bug_ids) }); + } + + $dbh->do("DELETE FROM $field WHERE value = ?", undef, $value); + + unlink "$datadir/versioncache"; + + $vars->{'value'} = $value; + $vars->{'field'} = $field; + $template->process("admin/fieldvalues/deleted.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + exit; +} + + +# +# action='edit' -> present the edit-value form +# (next action would be 'update') +# +if ($action eq 'edit') { + ValueMustExist($field, $value); + trick_taint($field); + trick_taint($value); + + $vars->{'sortkey'} = $dbh->selectrow_array( + "SELECT sortkey FROM $field WHERE value = ?", undef, $value) || 0; + + $vars->{'value'} = $value; + $vars->{'field'} = $field; + + $template->process("admin/fieldvalues/edit.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + + exit; +} + + +# +# action='update' -> update the field value +# +if ($action eq 'update') { + my $valueold = trim($cgi->param('valueold') || ''); + my $sortkeyold = trim($cgi->param('sortkeyold') || '0'); + + ValueMustExist($field, $valueold); + trick_taint($field); + trick_taint($valueold); + + if (length($value) > 60) { + ThrowUserError('fieldvalue_name_too_long', + {'value' => $value}); + } + + $dbh->do("LOCK TABLES bugs WRITE, $field WRITE"); + + # Need to store because detaint_natural() will delete this if + # invalid + my $stored_sortkey = $sortkey; + if ($sortkey != $sortkeyold) { + + if (!detaint_natural($sortkey)) { + $dbh->do('UNLOCK TABLES'); + ThrowUserError('fieldvalue_sortkey_invalid', + {'name' => $field, + 'sortkey' => $stored_sortkey}); + + } + + $dbh->do("UPDATE $field SET sortkey = ? WHERE value = ?", + undef, $sortkey, $valueold); + + unlink "$datadir/versioncache"; + $vars->{'updated_sortkey'} = 1; + $vars->{'sortkey'} = $sortkey; + } + + if ($value ne $valueold) { + + unless ($value) { + $dbh->do('UNLOCK TABLES'); + ThrowUserError('fieldvalue_undefined'); + } + if (ValueExists($field, $value)) { + $dbh->do('UNLOCK TABLES'); + ThrowUserError('fieldvalue_already_exists', + {'value' => $value, + 'field' => $field}); + } + trick_taint($value); + + $dbh->do("UPDATE bugs SET $field = ?, delta_ts = NOW() + WHERE $field = ?", undef, $value, $valueold); + + $dbh->do("UPDATE $field SET value = ? WHERE value = ?", + undef, $value, $valueold); + + unlink "$datadir/versioncache"; + + $vars->{'updated_value'} = 1; + } + + $dbh->do('UNLOCK TABLES'); + + $vars->{'value'} = $value; + $vars->{'field'} = $field; + $template->process("admin/fieldvalues/updated.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + + exit; +} + + +# +# No valid action found +# +# We can't get here without $field being defined -- +# See the unless($field) block at the top. +ThrowUserError('no_valid_action', { field => $field } ); -- cgit v1.2.3-24-g4f1b