246 lines
8.0 KiB
Perl
Executable File
246 lines
8.0 KiB
Perl
Executable File
#!/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 Testopia System.
|
|
#
|
|
# The Initial Developer of the Original Code is Greg Hendricks.
|
|
# Portions created by Greg Hendricks are Copyright (C) 2006
|
|
# Novell. All Rights Reserved.
|
|
#
|
|
# Contributor(s): Greg Hendricks <ghendricks@novell.com>
|
|
|
|
use strict;
|
|
use lib qw(. lib);
|
|
use Bugzilla::Constants;
|
|
use lib (bz_locations()->{extensionsdir} . '/testopia/lib');
|
|
|
|
use Bugzilla;
|
|
use Bugzilla::Util;
|
|
use Bugzilla::Error;
|
|
use Bugzilla::Product;
|
|
use Bugzilla::Token;
|
|
|
|
use Testopia::Util;
|
|
use Testopia::Constants;
|
|
use Testopia::TestPlan;
|
|
use Testopia::TestCase;
|
|
use Testopia::Category;
|
|
use Testopia::Importer;
|
|
|
|
use XML::Twig;
|
|
use Text::CSV;
|
|
|
|
my $vars = {};
|
|
my $template = Bugzilla->template;
|
|
my $cgi = Bugzilla->cgi;
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
# Re-bless CGI's filehandle lite as a real IO::Handle so that Text::CSV
|
|
# knows what to do with it
|
|
@Fh::ISA= qw( IO::Handle );
|
|
|
|
Bugzilla->login(LOGIN_REQUIRED);
|
|
|
|
$cgi->send_header;
|
|
|
|
my $action = $cgi->param('action') || '';
|
|
my $ctype = $cgi->param('ctype') || '';
|
|
|
|
Bugzilla->error_mode(ERROR_MODE_AJAX) if $ctype eq 'json';
|
|
|
|
if ($action eq 'upload') {
|
|
my $token = trim($cgi->param('token'));
|
|
if ($token) {
|
|
my ($creator_id, $date, $old_file) = Bugzilla::Token::GetTokenData($token);
|
|
unless ($creator_id
|
|
&& ($creator_id == Bugzilla->user->id)
|
|
&& ($old_file =~ "^importtests:"))
|
|
{
|
|
# The token is invalid.
|
|
ThrowUserError('token_inexistent');
|
|
}
|
|
$old_file =~ s/^importtests://;
|
|
# Must have hit refresh on the form.
|
|
ThrowUserError('import_repeat') if ($old_file);
|
|
|
|
}
|
|
defined $cgi->upload('data') || ThrowUserError("file_not_specified");
|
|
my $type = $cgi->uploadInfo($cgi->param("data"))->{'Content-Type'};
|
|
|
|
# IE Sends application/octet-stream
|
|
if ($type =~ /application/){
|
|
if ($cgi->param('data') =~ /\.csv$/){
|
|
$type = "text/csv";
|
|
}
|
|
elsif ($cgi->param('data') =~ /\.xml$/){
|
|
$type = "text/xml";
|
|
}
|
|
}
|
|
|
|
ThrowUserError('invalid_import_type', {type => $type}) unless $type =~ /text\/(plain|xml|csv|x-comma-separated-values)/;
|
|
|
|
if ($type eq 'text/xml'){
|
|
my $fh = $cgi->upload('data');
|
|
my $data;
|
|
# enable 'slurp' mode
|
|
local $/;
|
|
|
|
$data = <$fh>;
|
|
# Limit to 1 MB. Anything larger will take way too long to parse.
|
|
ThrowUserError("file_too_large", { filesize => sprintf("%.0f", length($data)/1024) }) if length($data) > 1048576;
|
|
|
|
my $importer = new Testopia::Importer;
|
|
my $case_ids = $importer->parse($data,$cgi->param('product_id'),$cgi->param('plan_id'));
|
|
|
|
if ($ctype eq 'json'){
|
|
print '{success: true}';
|
|
exit;
|
|
}
|
|
else{
|
|
$vars->{'cases'} = join(',', @$case_ids);
|
|
$template->process("testopia/import/importer.html.tmpl", $vars) ||
|
|
ThrowTemplateError($template->error());
|
|
}
|
|
|
|
}
|
|
else {
|
|
# Define the fields of the CSV file. The file must be in this order.
|
|
my @fields = qw(
|
|
product
|
|
plans
|
|
summary
|
|
author_id
|
|
default_tester_id
|
|
case_status_id
|
|
priority_id
|
|
category_id
|
|
components
|
|
requirement
|
|
estimated_time
|
|
isautomated
|
|
script
|
|
arguments
|
|
alias
|
|
tags
|
|
bugs
|
|
dependson
|
|
blocks
|
|
runs
|
|
setup
|
|
breakdown
|
|
action
|
|
effect
|
|
);
|
|
|
|
my $csv = Text::CSV->new({binary => 1, eol => $/});
|
|
my $fh = $cgi->upload('data');
|
|
|
|
$csv->column_names(\@fields);
|
|
my @rows;
|
|
my @validated;
|
|
while(my $row = $csv->getline_hr($fh)){
|
|
# print Data::Dumper::Dumper($row);
|
|
push @rows, $row;
|
|
}
|
|
|
|
if (! $csv->eof()){
|
|
ThrowUserError('csv_parse_failure', {row => scalar @rows + 1});
|
|
}
|
|
# Validate all the fields ahead of time to ensure that we have an atomic upload
|
|
foreach my $row (@rows){
|
|
next if ($row->{'plans'} =~ /Plans/);
|
|
my $product = Bugzilla::Product::check_product($row->{'product'});
|
|
delete $row->{'product'};
|
|
|
|
my $import_plan = $cgi->param('plan_id');
|
|
if (detaint_natural($import_plan)){
|
|
$row->{'plans'} .= ',' if $row->{'plans'};
|
|
$row->{'plans'} .= "$import_plan";
|
|
}
|
|
my @plans;
|
|
foreach my $id (split(/[\s,]+/, $row->{'plans'})){
|
|
my $plan = Testopia::TestPlan->new($id);
|
|
ThrowUserError("invalid-test-id-non-existent", {'id' => $id, 'type' => 'Plan'}) unless $plan;
|
|
ThrowUserError("testopia-create-denied", {'object' => 'Test Case', 'plan' => $plan}) unless $plan->canedit;
|
|
push @plans, $plan;
|
|
}
|
|
$row->{'plans'} = \@plans;
|
|
$row->{'author_id'} ||= Bugzilla->user->id;
|
|
my $category = $row->{'category_id'};
|
|
trick_taint($category);
|
|
if (trim($category) =~ /^\d+$/){
|
|
$row->{'category_id'} = Testopia::Util::validate_selection($row->{'category_id'}, 'category_id', 'test_case_categories');
|
|
}
|
|
else {
|
|
$category = check_case_category($category, $product);
|
|
ThrowUserError("invalid-test-id-non-existent", {'id' => $row->{'category_id'}, 'type' => 'category'}) unless $category;
|
|
$row->{'category_id'} = $category;
|
|
}
|
|
my @comps;
|
|
foreach my $comp (split(/,+/,$row->{'components'})){
|
|
if (trim($comp) =~ /^\d+$/){
|
|
push @comps, $comp;
|
|
}
|
|
else {
|
|
push @comps, {component => trim($comp), product => $product->name};
|
|
}
|
|
}
|
|
$row->{'components'} = \@comps;
|
|
$row->{'isautomated'} = $row->{'isautomated'} =~ /yes/i ? 1 : 0;
|
|
|
|
$row->{'setup'} =~ s/\n/<BR>/g;
|
|
$row->{'breakdown'} =~ s/\n/<BR>/g;
|
|
$row->{'action'} =~ s/\n/<BR>/g;
|
|
$row->{'effect'} =~ s/\n/<BR>/g;
|
|
|
|
# print Data::Dumper::Dumper($row);
|
|
my $case = Testopia::TestCase->new({});
|
|
|
|
$case->check_required_create_fields($row);
|
|
$case->run_create_validators($row);
|
|
push @validated, $row;
|
|
}
|
|
|
|
# OK, if we are here, all the fields passed validation. Time to create
|
|
my @case_ids;
|
|
foreach my $row (@validated){
|
|
my $case = Testopia::TestCase->create($row);
|
|
push @case_ids, $case->id;
|
|
}
|
|
if ($ctype eq 'json'){
|
|
print '{success: true}';
|
|
exit;
|
|
}
|
|
else{
|
|
$vars->{'cases'} = join(',',@case_ids);
|
|
$template->process("testopia/import/importer.html.tmpl", $vars) ||
|
|
ThrowTemplateError($template->error());
|
|
}
|
|
}
|
|
if ($token) {
|
|
trick_taint($token);
|
|
my $filename = $cgi->param('data');
|
|
trick_taint($filename);
|
|
$dbh->do('UPDATE tokens SET eventdata = ? WHERE token = ?', undef,
|
|
("importtests:$filename", $token));
|
|
}
|
|
}
|
|
|
|
else {
|
|
$vars->{'token'} = issue_session_token('importtests:');
|
|
$template->process("testopia/import/importer.html.tmpl", $vars) ||
|
|
ThrowTemplateError($template->error());
|
|
|
|
}
|
|
|