#!/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 mozilla.org code. # # The Initial Developer of the Original Code is Holger # Schurig. Portions created by Holger Schurig are # Copyright (C) 1999 Holger Schurig. All # Rights Reserved. # # Contributor(s): Holger Schurig # Terry Weissman # # Direct any questions on this source code to # # Holger Schurig use strict; use lib "."; require "globals.pl"; use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Series; use Bugzilla::Util; use Bugzilla::User; use Bugzilla::Product; use Bugzilla::Component; use Bugzilla::Bug; my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; my $template = Bugzilla->template; my $vars = {}; # # Preliminary checks: # my $user = Bugzilla->login(LOGIN_REQUIRED); my $whoid = $user->id; print $cgi->header(); $user->in_group('editcomponents') || ThrowUserError("auth_failure", {group => "editcomponents", action => "edit", object => "components"}); # # often used variables # my $product_name = trim($cgi->param('product') || ''); my $comp_name = trim($cgi->param('component') || ''); my $action = trim($cgi->param('action') || ''); my $showbugcounts = (defined $cgi->param('showbugcounts')); # # product = '' -> Show nice list of products # unless ($product_name) { my @products = Bugzilla::Product::get_all_products(); $vars->{'showbugcounts'} = $showbugcounts; $vars->{'products'} = \@products; $template->process("admin/components/select-product.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } my $product = Bugzilla::Product::check_product($product_name); # # action='' -> Show nice list of components # unless ($action) { $vars->{'showbugcounts'} = $showbugcounts; $vars->{'product'} = $product->name; $vars->{'components'} = $product->components; $template->process("admin/components/list.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # # action='add' -> present form for parameters for new component # # (next action will be 'new') # if ($action eq 'add') { $vars->{'product'} = $product->name; $template->process("admin/components/create.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # # action='new' -> add component entered in the 'action=add' screen # if ($action eq 'new') { # Do the user matching Bugzilla::User::match_field ($cgi, { 'initialowner' => { 'type' => 'single' }, 'initialqacontact' => { 'type' => 'single' }, }); my $default_assignee = trim($cgi->param('initialowner') || ''); my $default_qa_contact = trim($cgi->param('initialqacontact') || ''); my $description = trim($cgi->param('description') || ''); $comp_name || ThrowUserError('component_blank_name'); if (length($comp_name) > 64) { ThrowUserError('component_name_too_long', {'name' => $comp_name}); } my $component = new Bugzilla::Component({product_id => $product->id, name => $comp_name}); if ($component) { ThrowUserError('component_already_exists', {'name' => $component->name}); } $description || ThrowUserError('component_blank_description', {name => $comp_name}); $default_assignee || ThrowUserError('component_need_initialowner', {name => $comp_name}); my $default_assignee_id = login_to_id($default_assignee); my $default_qa_contact_id = Param('useqacontact') ? (login_to_id($default_qa_contact) || undef) : undef; trick_taint($comp_name); trick_taint($description); $dbh->do("INSERT INTO components (product_id, name, description, initialowner, initialqacontact) VALUES (?, ?, ?, ?, ?)", undef, ($product->id, $comp_name, $description, $default_assignee_id, $default_qa_contact_id)); # Insert default charting queries for this product. # If they aren't using charting, this won't do any harm. GetVersionTable(); my @series; my $prodcomp = "&product=" . url_quote($product->name) . "&component=" . url_quote($comp_name); # For localisation reasons, we get the title of the queries from the # submitted form. my $open_name = $cgi->param('open_name'); my $closed_name = $cgi->param('closed_name'); my @openedstatuses = OpenStates(); my $statuses = join("&", map { "bug_status=" . url_quote($_) } @openedstatuses) . $prodcomp; my $resolved = "field0-0-0=resolution&type0-0-0=notequals&value0-0-0=---" . $prodcomp; # trick_taint is ok here, as these variables aren't used as a command # or in SQL unquoted trick_taint($open_name); trick_taint($closed_name); trick_taint($statuses); trick_taint($resolved); push(@series, [$open_name, $statuses]); push(@series, [$closed_name, $resolved]); foreach my $sdata (@series) { my $series = new Bugzilla::Series(undef, $product->name, $comp_name, $sdata->[0], $::userid, 1, $sdata->[1], 1); $series->writeToDatabase(); } # Make versioncache flush unlink "$datadir/versioncache"; $vars->{'name'} = $comp_name; $vars->{'product'} = $product->name; $template->process("admin/components/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') { $vars->{'comp'} = Bugzilla::Component::check_component($product, $comp_name); $vars->{'prod'} = $product; $template->process("admin/components/confirm-delete.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # # action='delete' -> really delete the component # if ($action eq 'delete') { my $component = Bugzilla::Component::check_component($product, $comp_name); if ($component->bug_count) { if (Param("allowbugdeletion")) { foreach my $bug_id (@{$component->bug_ids}) { my $bug = new Bugzilla::Bug($bug_id, $whoid); $bug->remove_from_db(); } } else { ThrowUserError("component_has_bugs", {nb => $component->bug_count }); } } $vars->{'deleted_bug_count'} = $component->bug_count; $dbh->bz_lock_tables('components WRITE', 'flaginclusions WRITE', 'flagexclusions WRITE'); $dbh->do("DELETE FROM flaginclusions WHERE component_id = ?", undef, $component->id); $dbh->do("DELETE FROM flagexclusions WHERE component_id = ?", undef, $component->id); $dbh->do("DELETE FROM components WHERE id = ?", undef, $component->id); $dbh->bz_unlock_tables(); unlink "$datadir/versioncache"; $vars->{'name'} = $component->name; $vars->{'product'} = $product->name; $template->process("admin/components/deleted.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # # action='edit' -> present the edit component form # # (next action would be 'update') # if ($action eq 'edit') { $vars->{'comp'} = Bugzilla::Component::check_component($product, $comp_name); $vars->{'prod'} = $product; $template->process("admin/components/edit.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # # action='update' -> update the component # if ($action eq 'update') { # Do the user matching Bugzilla::User::match_field ($cgi, { 'initialowner' => { 'type' => 'single' }, 'initialqacontact' => { 'type' => 'single' }, }); my $comp_old_name = trim($cgi->param('componentold') || ''); my $default_assignee = trim($cgi->param('initialowner') || ''); my $default_qa_contact = trim($cgi->param('initialqacontact') || ''); my $description = trim($cgi->param('description') || ''); my $component_old = Bugzilla::Component::check_component($product, $comp_old_name); $comp_name || ThrowUserError('component_blank_name'); if (length($comp_name) > 64) { ThrowUserError('component_name_too_long', {'name' => $comp_name}); } if ($comp_name ne $component_old->name) { my $component = new Bugzilla::Component({product_id => $product->id, name => $comp_name}); if ($component) { ThrowUserError('component_already_exists', {'name' => $component->name}); } } $description || ThrowUserError('component_blank_description', {'name' => $component_old->name}); $default_assignee || ThrowUserError('component_need_initialowner', {name => $comp_name}); my $default_assignee_id = login_to_id($default_assignee); my $default_qa_contact_id = login_to_id($default_qa_contact) || undef; $dbh->bz_lock_tables('components WRITE', 'products READ', 'profiles READ'); if ($comp_name ne $component_old->name) { trick_taint($comp_name); $dbh->do("UPDATE components SET name = ? WHERE id = ?", undef, ($comp_name, $component_old->id)); unlink "$datadir/versioncache"; $vars->{'updated_name'} = 1; } if ($description ne $component_old->description) { trick_taint($description); $dbh->do("UPDATE components SET description = ? WHERE id = ?", undef, ($description, $component_old->id)); $vars->{'updated_description'} = 1; $vars->{'description'} = $description; } if ($default_assignee ne $component_old->default_assignee->login) { $dbh->do("UPDATE components SET initialowner = ? WHERE id = ?", undef, ($default_assignee_id, $component_old->id)); $vars->{'updated_initialowner'} = 1; $vars->{'initialowner'} = $default_assignee; } if (Param('useqacontact') && $default_qa_contact ne $component_old->default_qa_contact->login) { $dbh->do("UPDATE components SET initialqacontact = ? WHERE id = ?", undef, ($default_qa_contact_id, $component_old->id)); $vars->{'updated_initialqacontact'} = 1; $vars->{'initialqacontact'} = $default_qa_contact; } $dbh->bz_unlock_tables(); $vars->{'name'} = $comp_name; $vars->{'product'} = $product->name; $template->process("admin/components/updated.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; } # # No valid action found # ThrowUserError('no_valid_action', {'field' => "component"});