bugzilla-4intranet/importxls.cgi

348 lines
10 KiB
Perl
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/perl -wT
# Bug 42133
# Интерфейс множественного импорта багов из Excel-файлов
use utf8;
use Encode;
use strict;
use lib qw(. lib);
use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Product;
use Bugzilla::Component;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Bug;
use Bugzilla::User;
# Подгружаются по необходимости: Spreadsheet::ParseExcel, Spreadsheet::XSLX;
# константы
use constant BUG_DAYS => 92;
use constant XLS_LISTNAME => 'Bugz';
use constant MANDATORY_FIELDS => [qw(short_desc product component)];
# начинаем-с
my $user = Bugzilla->login(LOGIN_REQUIRED);
my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh;
my $template = Bugzilla->template;
my $vars = {};
my $args = {};
for ($cgi->param)
{
my $v = $_;
utf8::decode($v) unless Encode::is_utf8($v);
if ($v eq 'bug_id')
{
$args->{$v} = [ $cgi->param($_) ];
}
else
{
$args->{$v} = $cgi->param($_);
}
utf8::decode($args->{$v}) unless Encode::is_utf8($args->{$v});
}
# проверяем группу
$user->in_group('importxls') ||
ThrowUserError('auth_failure', {
group => 'importxls',
action => 'import',
object => 'bugs',
});
my $listname = $cgi->param('listname') || '';
my $bugdays = $cgi->param('bugdays') || '';
($bugdays) = $bugdays =~ /^(\d+)$/so;
$bugdays ||= BUG_DAYS;
trick_taint($listname);
trick_taint($bugdays);
$vars->{listname} = $listname || XLS_LISTNAME;
$vars->{bugdays} = $bugdays;
my $upload;
my $name_tr = {};
my $bug_tpl = {};
$bug_tpl->{platform} = Bugzilla->params->{defaultplatform} if Bugzilla->params->{defaultplatform};
for (keys %$args)
{
if (/^f_/so && $args->{$_})
{
# шаблон для багов
$bug_tpl->{$'} = $args->{$_};
}
elsif (/^t_/so && $args->{$_} && $args->{$_} ne $')
{
# переименования полей таблицы
$name_tr->{$'} = $args->{$_};
}
}
$vars->{bug_tpl} = $bug_tpl;
$vars->{name_tr} = $name_tr;
# Функция угадывания поля
my $fielddescs;
$vars->{guess_field_name} = sub
{
my ($name) = @_;
unless ($fielddescs)
{
$fielddescs = $_[1];
$fielddescs = [
map { $_ => $fielddescs->{$_} }
sort { length($fielddescs->{$b}) <=> length($fielddescs->{$a}) }
keys %$fielddescs
];
}
for (my $i = 0; $i < @$fielddescs; $i+=2)
{
my ($k, $v) = ($fielddescs->[$i], $fielddescs->[$i+1]);
return $k if $name =~ /\Q$v\E/is;
}
return undef;
};
unless ($args->{commit})
{
unless ($upload = $cgi->upload('xls'))
{
if (!defined $args->{result})
{
# показываем формочку выбора файла и заливки
$vars->{form} = 1;
}
else
{
# показываем результат импорта
$vars->{show_result} = 1;
$vars->{result} = $args->{result};
$vars->{bug_id} = $args->{bug_id};
my $newcgi = new Bugzilla::CGI({
listname => $listname,
bugdays => $bugdays,
(map { ("f_$_" => $bug_tpl->{$_}) } keys %$bug_tpl),
(map { ("t_$_" => $name_tr->{$_}) } keys %$name_tr),
});
$vars->{importnext} = 'importxls.cgi?'.$newcgi->query_string;
}
}
else
{
# показываем интерфейс с распаршенной таблицей и галочками (или с обломом)
my $table = parse_excel($upload, $args->{xls}, $listname, $name_tr);
if (!$table || $table->{error})
{
# ошибка
$vars->{show_error} = 1;
$vars->{error} = $table->{error} if $table;
}
else
{
# распарсилось
my $i = 0;
my $sth = $dbh->prepare("SELECT COUNT(*) FROM `bugs` WHERE `short_desc`=? AND `delta_ts`>=DATE_SUB(CURDATE(),INTERVAL ? DAY)");
# номера и проверка
for my $bug (@{$table->{data}})
{
# проверяем нет ли уже такого бага
if ($bug->{short_desc})
{
trick_taint($bug->{short_desc});
$sth->execute($bug->{short_desc}, $bugdays);
($bug->{enabled}) = $sth->fetchrow_array;
$bug->{enabled} = !$bug->{enabled};
}
$bug->{num} = ++$i;
}
# показываем табличку с багами
my %fhash = map { ($name_tr->{$_} || $_) => 1 } @{$table->{fields}};
for (@{ MANDATORY_FIELDS() })
{
push @{$table->{fields}}, $_ unless $fhash{$_} || $bug_tpl->{$_};
}
$vars->{fields} = $table->{fields};
$vars->{data} = $table->{data};
}
}
print $cgi->header();
$template->process("bug/import/importxls.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
}
else
{
# выполняем импорт и отдаём редирект на результаты
my $bugs = {};
for (keys %$args)
{
if (/^b_(.*?)_(\d+)$/so)
{
# поля багов
$bugs->{$2}->{$name_tr->{$1} || $1} = $args->{$_};
}
}
my $r = 0;
my $ids = [];
my $f = 0;
Bugzilla->dbh->bz_start_transaction;
for my $bug (values %$bugs)
{
$bug->{$_} ||= $bug_tpl->{$_} for keys %$bug_tpl;
if ($bug->{enabled})
{
my $id = post_bug($bug);
if ($id)
{
$r++;
push @$ids, $id;
}
else
{
Bugzilla->dbh->bz_rollback_transaction;
$f = 1;
last;
}
}
}
unless ($f)
{
my $newcgi = new Bugzilla::CGI({
result => $r,
bug_id => $ids,
listname => $listname,
bugdays => $bugdays,
(map { ("f_$_" => $bug_tpl->{$_}) } keys %$bug_tpl),
(map { ("t_$_" => $name_tr->{$_}) } keys %$name_tr),
});
Bugzilla->dbh->bz_commit_transaction;
print $cgi->redirect(-location => 'importxls.cgi?'.$newcgi->query_string);
}
}
# разобрать лист Excel
sub parse_excel
{
my ($upload, $name, $only_list, $name_tr) = @_;
my $xls;
if ($name =~ /\.xlsx$/iso)
{
# OOXML
require Spreadsheet::XLSX;
$xls = Spreadsheet::XLSX->new($upload);
}
else
{
# Обычный формат
require Spreadsheet::ParseExcel;
$xls = Spreadsheet::ParseExcel->new->Parse($upload);
}
return { error => 'parse_error' } unless $xls;
my $r = { data => [] };
for my $page ($xls->worksheets())
{
# выбираем для обработки только лист с заданным именем
next if $only_list && $page->{Name} ne $only_list;
my ($row_min, $row_max) = $page->row_range;
my ($col_min, $col_max) = $page->col_range;
my $head = get_row($page, $row_min, $col_min, $col_max);
$r->{fields} = $head;
# обрабатываем саму таблицу
for my $row (($row_min+1) .. $row_max)
{
$row = get_row($page, $row, $col_min, $col_max) || next;
$row = { map { ($head->[$_] => $row->[$_]) } (0..$#$head) };
push @{$r->{data}}, $row;
}
}
return { error => 'empty' } unless @{$r->{data}};
return $r;
}
# вернуть строчку из экселя
sub get_row
{
my ($page, $row, $col_min, $col_max) = @_;
return [ map { $_ = $page->get_cell($row, $_); $_ ? trim($_->value) : '' } ($col_min .. $col_max) ];
}
# добавить баг
sub post_bug
{
my ($fields_in) = @_;
my $cgi = Bugzilla->cgi;
# имитируем почтовое использование с показом ошибок в браузер
my $um = Bugzilla->usage_mode;
Bugzilla->usage_mode(USAGE_MODE_EMAIL);
Bugzilla->error_mode(ERROR_MODE_WEBPAGE);
unless ($fields_in->{version})
{
# угадаем версию
my ($product, $component);
eval
{
$product = Bugzilla::Product->check({ name => $fields_in->{product} });
$component = Bugzilla::Component->check({
product => $product,
name => $fields_in->{component},
});
};
# если нет дефолтной версии в компоненте
unless ($fields_in->{version} = $component->default_version)
{
my $vers = [ map ($_->name, @{$product->versions}) ];
my $v;
if (($v = $cgi->cookie("VERSION-" . $product->name)) &&
(lsearch($vers, $v) != -1))
{
# возьмём из куки
$fields_in->{version} = $v;
}
else
{
# или просто последнюю, как и в enter_bug.cgi
$fields_in->{version} = $vers->[$#$vers];
}
}
}
# скармливаем параметры $cgi
foreach my $field (keys %$fields_in)
{
$cgi->param(-name => $field, -value => $fields_in->{$field});
}
# и дёргаем post_bug.cgi
my $bug_id = do 'post_bug.cgi';
Bugzilla->usage_mode($um);
return $bug_id;
}
1;
__END__
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): Terry Weissman <terry@mozilla.org>
Dan Mosedale <dmose@mozilla.org>
Joe Robins <jmrobins@tgix.com>
Gervase Markham <gerv@gerv.net>
Marc Schumann <wurblzap@gmail.com>