Bug 3066 - Overhaul guided reports

execute_query now refactored, returns reliable results, does
zero presentation-layer crap.  Arguments reduced, client scripts
adapted to new API and performance improved.  Text::CSV now used
to generate CSV output, ensuring portability, encoding and accuracy.

Replaced tools/runreport.pl with misc/cronjobs/runreport.pl:
    ~ security fixed
    ~ documentation improved
    ~ TODO: finish sendmail option.

Bug 3077 also fixed.

Signed-off-by: Galen Charlton <galen.charlton@liblime.com>
This commit is contained in:
Joe Atzberger 2009-03-31 15:08:10 -05:00 committed by Galen Charlton
parent 76d0e8d05b
commit 14be4400d8
6 changed files with 277 additions and 388 deletions

View file

@ -20,6 +20,7 @@ package C4::Reports::Guided;
use strict; use strict;
# use warnings; # FIXME: this module needs a lot of repair to run clean under warnings # use warnings; # FIXME: this module needs a lot of repair to run clean under warnings
use CGI; use CGI;
use Carp;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
use C4::Context; use C4::Context;
@ -41,6 +42,7 @@ BEGIN {
save_report get_saved_reports execute_query get_saved_report create_compound run_compound save_report get_saved_reports execute_query get_saved_report create_compound run_compound
get_column_type get_distinct_values save_dictionary get_from_dictionary get_column_type get_distinct_values save_dictionary get_from_dictionary
delete_definition delete_report format_results get_sql delete_definition delete_report format_results get_sql
select_2_select_count_value
); );
} }
@ -335,7 +337,7 @@ sub get_criteria {
=over =over
($results, $total, $error) = execute_query($sql, $type, $offset, $limit, $format, $id) ($results, $total, $error) = execute_query($sql, $offset, $limit)
=back =back
@ -355,133 +357,77 @@ sub get_criteria {
=cut =cut
# FIXME: This needs to be generalized to reports in general # returns $sql, $offset, $limit
# FIXME: This should NOT have ANY formatting responsibilities. # $sql returned will be transformed to:
# Instead, is should just be returning a prepared sth. # ~ remove any LIMIT clause
# FIXME: $type is a TOTALLY UNUSED "required" argument? # ~ repace SELECT clause w/ SELECT count(*)
sub execute_query ($$$$;$$) { sub select_2_select_count_value ($) {
my ( $sql, $type, $offset, $limit, $format, $id ) = @_; my $sql = shift or return;
$format or $format = ''; my $countsql = select_2_select_count($sql);
$debug and print STDERR "execute_query($sql, $type, $offset, $limit, $format, $id)\n"; $debug and warn "original query: $sql\ncount query: $countsql\n";
my @params; my $sth1 = C4::Context->dbh->prepare($countsql);
my $total = 0; $sth1->execute();
my ($useroffset, $userlimit); my $total = $sth1->fetchrow();
my @errors = (); $debug and warn "total records for this query: $total\n";
my $error = {}; return $total;
my $sqlerr = 0; }
sub select_2_select_count ($) {
# Modify the query passed in to create a count query... (I think this covers all cases -crn)
my ($sql) = strip_limit(shift) or return;
$sql =~ s/\bSELECT\W+(?:\w+\W+){1,}?FROM\b|\bSELECT\W\*\WFROM\b/SELECT count(*) FROM /ig;
return $sql;
}
sub strip_limit ($) {
my $sql = shift or return;
($sql =~ /\bLIMIT\b/i) or return ($sql, 0, undef);
$sql =~ s/\bLIMIT\b\s*\d+(\,\s*\d+)?\s*/ /ig;
return ($sql, (defined $1 ? $1 : 0), $2); # offset can default to 0, LIMIT cannot!
}
sub execute_query ($;$$$) {
my ( $sql, $offset, $limit, $no_count ) = @_;
# check parameters
unless ($sql) {
carp "execute_query() called without SQL argument";
return;
}
$offset = 0 unless $offset;
$limit = 9999 unless $limit;
$debug and print STDERR "execute_query($sql, $offset, $limit)\n";
if ($sql =~ /;?\W?(UPDATE|DELETE|DROP|INSERT|SHOW|CREATE)\W/i) { if ($sql =~ /;?\W?(UPDATE|DELETE|DROP|INSERT|SHOW|CREATE)\W/i) {
$sqlerr = 1; return (undef, { sqlerr => $1} );
$error->{'sqlerr'} = $1; } elsif ($sql !~ /^\s*SELECT\b\s*/i) {
push @errors, $error; return (undef, { queryerr => 'Missing SELECT'} );
} elsif ($sql !~ /^(SELECT)/i) {
$sqlerr = 1;
$error->{'queryerr'} = 'Missing SELECT';
push @errors, $error;
} }
if ($sqlerr == 0) {
my $dbh = C4::Context->dbh(); my ($useroffset, $userlimit);
unless ($format eq 'text' || $format eq 'tab' || $format eq 'csv' || $format eq 'url'){
# Grab offset/limit from user supplied LIMIT and drop the LIMIT so we can control pagination # Grab offset/limit from user supplied LIMIT and drop the LIMIT so we can control pagination
if ($sql =~ /LIMIT/i) { ($sql, $useroffset, $userlimit) = strip_limit($sql);
$sql =~ s/LIMIT\W?(\d+)?\,?\W+?(\d+)//ig; $debug and warn sprintf "User has supplied (OFFSET,) LIMIT = %s, %s",
$debug and warn "User has supplied LIMIT\n"; $useroffset,
$useroffset = $1; (defined($userlimit ) ? $userlimit : 'UNDEF');
$userlimit = $2; $offset += $useroffset;
$debug and warn "User supplied offset = $useroffset, limit = $userlimit\n"; my $total;
$offset += $useroffset if $useroffset; if (defined($userlimit)) {
# keep track of where we are if there is a user supplied LIMIT if ($offset + $limit > $userlimit ) {
if ( $offset + $limit > $userlimit ) { $limit = $userlimit - $offset;
$limit = $userlimit - $offset;
}
}
my $countsql = $sql;
$sql .= " LIMIT ?, ?";
$debug and warn "Passing query with params offset = $offset, limit = $limit\n";
@params = ($offset, $limit);
# Modify the query passed in to create a count query... (I think this covers all cases -crn)
$countsql =~ s/\bSELECT\W+(?:\w+\W+){1,}?FROM\b|\bSELECT\W\*\WFROM\b/SELECT count(*) FROM /ig;
$debug and warn "original query: $sql\n";
$debug and warn "count query: $countsql\n";
my $sth1 = $dbh->prepare($countsql);
$sth1->execute();
$total = $sth1->fetchrow();
$debug and warn "total records for this query: $total\n";
$total = $userlimit if defined($userlimit) and $userlimit < $total; # we will never exceed a user defined LIMIT and...
$userlimit = $total if defined($userlimit) and $userlimit > $total; # we will never exceed the total number of records available to satisfy the query
} }
my $sth = $dbh->prepare($sql); $total = $userlimit if $userlimit < $total; # we will never exceed a user defined LIMIT and...
$sth->execute(@params); $userlimit = $total if $userlimit > $total; # we will never exceed the total number of records available to satisfy the query
my $colnames=$sth->{'NAME'};
my @results;
my $row;
my %temphash;
$row = join ('</th><th>',@$colnames);
$row = "<tr><th>$row</th></tr>";
$temphash{'row'} = $row;
push @results, \%temphash;
my $string;
if ($format eq 'tab') {
$string = join("\t",@$colnames);
}
elsif ($format eq 'csv') {
$string = join(",",@$colnames);
}
my @xmlarray;
while ( my @data = $sth->fetchrow_array() ) {
# if the field is a date field, it needs formatting
foreach my $data (@data) {
unless (defined $data) {
$data = ''; # suppress undef, so fields are joinable
next;
}
next unless $data =~ C4::Dates->regexp("iso");
$data = C4::Dates->new($data, "iso")->output();
}
# tabular
my %temphash; # FIXME: already declared %temphash in same scope.
my $row = join( '</td><td>', @data );
$row = "<tr><td>$row</td></tr>";
$temphash{'row'} = $row;
if ($format eq 'text') {
$string .= "\n" . $row;
}
elsif ($format eq 'tab'){
$row = join("\t",@data);
$string .="\n" . $row;
}
elsif ($format eq 'csv'){
$row = join(",",@data);
$string .="\n" . $row;
}
elsif ($format eq 'url'){
my $temphash; # FIXME: already declared %temphash in same scope. TWICE!!
@$temphash{@$colnames}=@data;
push @xmlarray,$temphash;
}
push @results, \%temphash;
}
if (defined($sth->errstr)) {
$error->{'queryerr'} = $sth->errstr;
push @errors, $error;
warn "Database returned: $sth->errstr";
}
if ( $format eq 'text' || $format eq 'tab' || $format eq 'csv' ) {
return $string, $total, \@errors;
}
elsif ($format eq 'url') {
my $url = "/cgi-bin/koha/reports/guided_reports.pl?phase=retrieve%20results&id=$id";
my $dump = new XML::Dumper;
my $xml = $dump->pl2xml( \@xmlarray );
store_results($id,$xml);
return $url, $total, \@errors;
}
else {
return \@results, $total, \@errors;
}
} else {
return undef, undef, \@errors;
} }
$sql .= " LIMIT ?, ?";
my $sth = C4::Context->dbh->prepare($sql);
$sth->execute($offset, $limit);
return ( $sth );
# my @xmlarray = ... ;
# my $url = "/cgi-bin/koha/reports/guided_reports.pl?phase=retrieve%20results&id=$id";
# my $xml = XML::Dumper->new()->pl2xml( \@xmlarray );
# store_results($id,$xml);
} }
=item save_report($sql,$name,$type,$notes) =item save_report($sql,$name,$type,$notes)
@ -498,8 +444,6 @@ sub save_report {
"INSERT INTO saved_sql (borrowernumber,date_created,last_modified,savedsql,report_name,type,notes) VALUES (?,now(),now(),?,?,?,?)"; "INSERT INTO saved_sql (borrowernumber,date_created,last_modified,savedsql,report_name,type,notes) VALUES (?,now(),now(),?,?,?,?)";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute( 0, $sql, $name, $type, $notes ); $sth->execute( 0, $sql, $name, $type, $notes );
$sth->finish();
} }
sub store_results { sub store_results {
@ -512,15 +456,12 @@ sub store_results {
my $query2 = "UPDATE saved_reports SET report=?,date_run=now() WHERE report_id=?"; my $query2 = "UPDATE saved_reports SET report=?,date_run=now() WHERE report_id=?";
my $sth2 = $dbh->prepare($query2); my $sth2 = $dbh->prepare($query2);
$sth2->execute($xml,$id); $sth2->execute($xml,$id);
$sth2->finish();
} }
else { else {
my $query2 = "INSERT INTO saved_reports (report_id,report,date_run) VALUES (?,?,now())"; my $query2 = "INSERT INTO saved_reports (report_id,report,date_run) VALUES (?,?,now())";
my $sth2 = $dbh->prepare($query2); my $sth2 = $dbh->prepare($query2);
$sth2->execute($id,$xml); $sth2->execute($id,$xml);
$sth2->finish();
} }
$sth->finish();
} }
sub format_results { sub format_results {
@ -545,7 +486,6 @@ sub format_results {
$sth = $dbh->prepare($query); $sth = $dbh->prepare($query);
$sth->execute($id); $sth->execute($id);
$data = $sth->fetchrow_hashref(); $data = $sth->fetchrow_hashref();
$sth->finish();
return ($perl,$data->{'report_name'},$data->{'notes'}); return ($perl,$data->{'report_name'},$data->{'notes'});
} }
@ -555,7 +495,6 @@ sub delete_report {
my $query = "DELETE FROM saved_sql WHERE id = ?"; my $query = "DELETE FROM saved_sql WHERE id = ?";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute($id); $sth->execute($id);
$sth->finish();
} }
sub get_saved_reports { sub get_saved_reports {
@ -565,12 +504,7 @@ sub get_saved_reports {
ORDER by date_created"; ORDER by date_created";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute(); $sth->execute();
my @reports; return $sth->fetchall_arrayref({});
while ( my $data = $sth->fetchrow_hashref() ) {
push @reports, $data;
}
$sth->finish();
return ( \@reports );
} }
sub get_saved_report { sub get_saved_report {
@ -580,7 +514,6 @@ sub get_saved_report {
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute($id); $sth->execute($id);
my $data = $sth->fetchrow_hashref(); my $data = $sth->fetchrow_hashref();
$sth->finish();
return ( $data->{'savedsql'}, $data->{'type'}, $data->{'report_name'}, $data->{'notes'} ); return ( $data->{'savedsql'}, $data->{'type'}, $data->{'report_name'}, $data->{'notes'} );
} }
@ -635,7 +568,6 @@ sub get_column_type {
return $info->{'TYPE_NAME'}; return $info->{'TYPE_NAME'};
} }
} }
$sth->finish();
} }
=item get_distinct_values($column) =item get_distinct_values($column)
@ -653,12 +585,7 @@ sub get_distinct_values {
"SELECT distinct($column) as availablevalues FROM $table"; "SELECT distinct($column) as availablevalues FROM $table";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute(); $sth->execute();
my @values; return $sth->fetchall_arrayref({});
while ( my $row = $sth->fetchrow_hashref() ) {
push @values, $row;
}
$sth->finish();
return \@values;
} }
sub save_dictionary { sub save_dictionary {
@ -668,7 +595,6 @@ sub save_dictionary {
VALUES (?,?,?,?,now(),now())"; VALUES (?,?,?,?,now(),now())";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute($name,$description,$sql,$area) || return 0; $sth->execute($name,$description,$sql,$area) || return 0;
$sth->finish();
return 1; return 1;
} }
@ -699,27 +625,24 @@ sub get_from_dictionary {
push @loop,$data; push @loop,$data;
} }
$sth->finish();
return (\@loop); return (\@loop);
} }
sub delete_definition { sub delete_definition {
my ($id) = @_; my ($id) = @_ or return;
my $dbh = C4::Context->dbh(); my $dbh = C4::Context->dbh();
my $query = "DELETE FROM reports_dictionary WHERE id = ?"; my $query = "DELETE FROM reports_dictionary WHERE id = ?";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute($id); $sth->execute($id);
$sth->finish();
} }
sub get_sql { sub get_sql {
my ($id) = @_; my ($id) = @_ or return;
my $dbh = C4::Context->dbh(); my $dbh = C4::Context->dbh();
my $query = "SELECT * FROM saved_sql WHERE id = ?"; my $query = "SELECT * FROM saved_sql WHERE id = ?";
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
$sth->execute($id); $sth->execute($id);
my $data=$sth->fetchrow_hashref(); my $data=$sth->fetchrow_hashref();
$sth->finish();
return $data->{'savedsql'}; return $data->{'savedsql'};
} }

View file

@ -1,10 +1,10 @@
<h5>Build and Run Reports</h5> <h5>Build and Run Reports</h5>
<ul> <ul>
<li><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build New</a></li> <li><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build New</a></li>
<li><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved">Use Saved</a></li> <li><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Use%20saved">Use Saved</a></li>
<li><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Create%20report%20from%20SQL">Create from SQL</a></li> <li><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Create%20report%20from%20SQL">Create from SQL</a></li>
</ul> </ul>
<h5>Reports Dictionary</h5> <h5>Reports Dictionary</h5>
<ul> <ul>
<li><a href="/cgi-bin/koha/reports/dictionary.pl?phase=View%20Dictionary">View Dictionary</a></li> <li><a href="/cgi-bin/koha/reports/dictionary.pl?phase=View%20Dictionary">View Dictionary</a></li>
</ul> </ul>

View file

@ -1,20 +1,33 @@
<!-- TMPL_INCLUDE NAME="doc-head-open.inc" --> <!-- TMPL_INCLUDE NAME="doc-head-open.inc" -->
<title>Koha &rsaquo; Reports <!-- TMPL_IF NAME="start" --> &rsaquo; Guided Reports Wizard <!-- /TMPL_IF --><!-- TMPL_IF NAME="saved1" --> &rsaquo; Guided Reports Wizard &rsaquo; Saved Reports<!-- /TMPL_IF --><!-- TMPL_IF NAME="create" --> &rsaquo; Guided Reports Wizard &rsaquo; Create from SQL<!-- /TMPL_IF --><!-- TMPL_IF NAME="showsql" --> <title>Koha &rsaquo; Reports &rsaquo; Guided Reports Wizard
&rsaquo; Guided Reports Wizard &rsaquo; Saved Reports &rsaquo; SQL View<!-- /TMPL_IF --> <!-- TMPL_IF NAME="saved1" -->&rsaquo; Saved Reports
<!-- TMPL_IF NAME="execute" --> &rsaquo; Guided Reports Wizard &rsaquo; Saved Reports &rsaquo; <!-- TMPL_VAR NAME="name" --> Report<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="create" -->&rsaquo; Create from SQL
<!-- TMPL_IF NAME="build1" -->&rsaquo; Guided Reports Wizard &rsaquo; Build A Report, Step 1: Choose a Module<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="showsql" -->&rsaquo; Saved Reports &rsaquo; SQL View
<!-- TMPL_IF NAME="build2" -->&rsaquo; Guided Reports Wizard &rsaquo; Build A Report, Step 2: Pick a Report Type<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="execute" -->&rsaquo; Saved Reports &rsaquo; <!-- TMPL_VAR NAME="name" --> Report
<!-- TMPL_IF NAME="build3" -->&rsaquo; Guided Reports Wizard &rsaquo; Build A Report, Step 3 of 6: Select Columns for Display<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="buildx" -->&rsaquo; Build A Report, Step <!-- TMPL_VAR NAME="buildx" --> of 6:
<!-- TMPL_IF NAME="build4" --> &rsaquo; Guided Reports Wizard &rsaquo; Build A Report, Step 4 of 6: Select Criteria to Limit on<!-- /TMPL_IF --> <!-- TMPL_IF NAME="build1" -->Choose a Module
<!-- TMPL_IF NAME="build5" --> &rsaquo; Guided Reports Wizard &rsaquo; Build A Report, Step 5 of 6: Pick which columns to total<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="build2" -->Pick a Report Type
<!-- TMPL_IF NAME="build6" -->&rsaquo; Guided Reports Wizard &rsaquo; Build A Report, Step 6 of 6: Select how you want the report ordered<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="build3" -->Select Columns for Display
<!-- TMPL_ELSIF NAME="build4" -->Select Criteria to Limit on
<!-- TMPL_ELSIF NAME="build5" -->Pick which columns to total
<!-- TMPL_ELSIF NAME="build6" -->Select how you want the report ordered
<!-- /TMPL_IF -->
<!-- /TMPL_IF -->
</title> </title>
<!-- TMPL_INCLUDE NAME="doc-head-close.inc" --> <!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
<!-- TMPL_INCLUDE NAME="calendar.inc" --> <!-- TMPL_INCLUDE NAME="calendar.inc" -->
<!-- TMPL_IF NAME="saved1" --> <style type="text/css">
#sql { width: 90%; height: 9em; border: 1px solid #EEE; }
</style>
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
$(document).ready(function(){ $(document).ready(function(){
<!-- TMPL_IF NAME="showsql" -->
$("#sql").focus(function() {
$(this).select();
});
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="saved1" -->
$(".confirmdelete").click(function(){ $(".confirmdelete").click(function(){
$(this).parents('tr').attr("class","warn"); $(this).parents('tr').attr("class","warn");
if(confirm("Are you sure you want to "+$(this).attr("title")+"?")){ if(confirm("Are you sure you want to "+$(this).attr("title")+"?")){
@ -24,46 +37,39 @@ $(document).ready(function(){
return false; return false;
} }
}); });
<!-- /TMPL_IF -->
}); });
//]]> //]]>
</script> </script>
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="showsql" -->
<style type="text/css">
#sql { width: 90%; height: 9em; border: 1px solid #EEE; }
</style>
<script type="text/javascript">
$(document).ready(function() {
$("#sql").focus(function() {
$(this).select();
});
});
</script><!-- /TMPL_IF -->
</head> </head>
<body> <body>
<!-- TMPL_INCLUDE NAME="header.inc" --> <!-- TMPL_INCLUDE NAME="header.inc" -->
<!-- TMPL_INCLUDE NAME="circ-search.inc" --> <!-- TMPL_INCLUDE NAME="circ-search.inc" -->
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> <div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a>
&rsaquo; <a href="/cgi-bin/koha/reports/reports-home.pl">Reports</a><!-- TMPL_IF NAME="start" --> &rsaquo; <strong>Guided Reports Wizard </strong><!-- /TMPL_IF --><!-- TMPL_IF NAME="saved1" --> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <strong>Saved Reports</strong><!-- /TMPL_IF --><!-- TMPL_IF NAME="create" --> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <strong>Create from SQL</strong><!-- /TMPL_IF --><!-- TMPL_IF NAME="showsql" --> &rsaquo; <a href="/cgi-bin/koha/reports/reports-home.pl">Reports</a>
&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved">Saved Reports</a> &rsaquo; <strong>SQL View</strong><!-- /TMPL_IF --> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a>
<!-- TMPL_IF NAME="execute" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved">Saved Reports</a> &rsaquo; <em><!-- TMPL_VAR NAME="name" --></em> Report<!-- /TMPL_IF --> &rsaquo;
<!-- TMPL_IF NAME="build1" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; Build A Report, Step 1: Choose a Module<!-- /TMPL_IF --> <!-- TMPL_IF NAME="saved1" --><strong>Saved Reports</strong>
<!-- TMPL_IF NAME="build2" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build A Report</a>, Step 2: Pick a Report Type<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="create" --><strong>Create from SQL</strong>
<!-- TMPL_IF NAME="build3" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build A Report</a>, Step 3 of 6: Select Columns for Display<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="showsql" --><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Use%20saved">Saved Reports</a> &rsaquo; <strong>SQL View</strong>
<!-- TMPL_IF NAME="build4" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build A Report</a>, Step 4 of 6: Select Criteria to Limit on<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="execute" --><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Use%20saved">Saved Reports</a> &rsaquo; <em><!-- TMPL_VAR NAME="name" --></em> Report
<!-- TMPL_IF NAME="build5" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build A Report</a>, Step 5 of 6: Pick which columns to total<!-- /TMPL_IF --> <!-- TMPL_ELSIF NAME="buildx" --><a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build A Report</a> &rsaquo; Step <!-- TMPL_VAR NAME="buildx" --> of 6:
<!-- TMPL_IF NAME="build6" -->&rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl">Guided Reports Wizard</a> &rsaquo; <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Build%20new">Build A Report</a>, Step 6 of 6: Select how you want the report ordered<!-- /TMPL_IF --> <!-- TMPL_IF NAME="build1" -->Choose a Module
<!-- TMPL_ELSIF NAME="build2" -->Pick a Report Type
<!-- TMPL_ELSIF NAME="build3" -->Select Columns for Display
<!-- TMPL_ELSIF NAME="build4" -->Select Criteria to Limit on
<!-- TMPL_ELSIF NAME="build5" -->Pick which columns to total
<!-- TMPL_ELSIF NAME="build6" -->Select how you want the report ordered
<!-- /TMPL_IF -->
<!-- /TMPL_IF -->
</div> </div>
<div id="doc3" class="yui-t1"> <div id="doc3" class="yui-t1">
<div id="bd">
<div id="bd"> <div id="yui-main">
<div id="yui-main">
<div class="yui-b"> <div class="yui-b">
<div class="details">
<div class="details">
<!-- TMPL_IF NAME="start" --> <!-- TMPL_IF NAME="start" -->
<h2>Guided Reports</h2> <h2>Guided Reports</h2>
@ -71,22 +77,21 @@ $(document).ready(function(){
This feature aims to provide some middle ground between the built in This feature aims to provide some middle ground between the built in
canned reports and writing custom SQL reports.</p> canned reports and writing custom SQL reports.</p>
<h3>Build And Run Reports</h3> <h3>Build And Run Reports</h3>
<form action="/cgi-bin/koha/reports/guided_reports.pl"> <form action="/cgi-bin/koha/reports/guided_reports.pl">
<input type="hidden" name="phase" value="Build new" /> <input type="hidden" name="phase" value="Build new" />
<input type="submit" name="submit" value="Build new"/> <input type="submit" name="submit" value="Build new"/>
</form> </form>
<form action="/cgi-bin/koha/reports/guided_reports.pl"> <form action="/cgi-bin/koha/reports/guided_reports.pl">
<input type="hidden" name="phase" value="Used saved"/> <input type="hidden" name="phase" value="Use saved"/>
<input type="submit" name="submit" value="Used saved"/> <input type="submit" name="submit" value="Use saved"/>
</form> </form>
<form action="/cgi-bin/koha/reports/guided_reports.pl"> <form action="/cgi-bin/koha/reports/guided_reports.pl">
<input type="hidden" name="phase" value="Create report from SQL"/> <input type="hidden" name="phase" value="Create report from SQL"/>
<input type="submit" name="submit" value="Create report from SQL"/> <input type="submit" name="submit" value="Create report from SQL"/>
</form> </form>
<h3>Reports Dictionary</h3> <h3>Reports Dictionary</h3>
<p>Use the reports dictionary to define custom criteria to use in your <p>Use the reports dictionary to define custom criteria to use in your reports</p>
reports</p>
<form action="/cgi-bin/koha/reports/dictionary.pl"> <form action="/cgi-bin/koha/reports/dictionary.pl">
<input type="hidden" name="phase" value="View Dictionary"/> <input type="hidden" name="phase" value="View Dictionary"/>
<input type="submit" name="submit" value="View Dictionary"/> <input type="submit" name="submit" value="View Dictionary"/>
@ -141,11 +146,13 @@ reports</p>
<form action="/cgi-bin/koha/reports/guided_reports.pl" method="post"> <form action="/cgi-bin/koha/reports/guided_reports.pl" method="post">
<input type="hidden" name="area" value="<!-- TMPL_VAR NAME="area" -->" /> <input type="hidden" name="area" value="<!-- TMPL_VAR NAME="area" -->" />
<fieldset class="rows"><legend>Step 2 of 6: Pick a Report Type</legend> <fieldset class="rows"><legend>Step 2 of 6: Pick a Report Type</legend>
<ol><li><label for="types">Choose: </label><select name="types" id="types"> <ol><li><label for="types">Choose: </label>
<!-- TMPL_LOOP NAME="types" --> <select id="types" name="types">
<option value="<!-- TMPL_VAR NAME="id" -->"><!-- TMPL_VAR NAME="name"--></option> <option value="1">Tabular</option>
<!-- /TMPL_LOOP --> <option value="2" disabled="disabled">Summary</option>
</select></li></ol></fieldset> <option value="3" disabled="disabled">Matrix</option>
</select>
</li></ol></fieldset>
<fieldset class="action"> <fieldset class="action">
<input type="hidden" name="phase" value="Choose this type" /> <input type="hidden" name="phase" value="Choose this type" />
@ -275,7 +282,7 @@ TMPL_VAR NAME="id" -->" /> <!-- TMPL_VAR NAME="name" --></td></tr>
<tr><td><input type="checkbox" name="total_by" id="<!-- TMPL_VAR <tr><td><input type="checkbox" name="total_by" id="<!-- TMPL_VAR
NAME="name" -->" value="<!-- TMPL_VAR NAME="name" -->" value="<!-- TMPL_VAR
NAME="name" -->" /> <label for="<!-- TMPL_VAR NAME="name" -->" /> <label for="<!-- TMPL_VAR
NAME="name" -->"><!-- TMPL_VAR NAME="name"--></label></td> NAME="name" -->"><!-- TMPL_VAR NAME="name" --></label></td>
<td><select name="<!-- TMPL_VAR NAME="name" -->_tvalue"> <td><select name="<!-- TMPL_VAR NAME="name" -->_tvalue">
<!-- TMPL_LOOP NAME="select" --> <!-- TMPL_LOOP NAME="select" -->
@ -359,11 +366,15 @@ NAME="name" -->"><!-- TMPL_VAR NAME="name"--></label></td><td>
<!-- TMPL_IF NAME="execute" --> <!-- TMPL_IF NAME="execute" -->
<h1><!-- TMPL_VAR NAME="name" --></h1> <h1><!-- TMPL_VAR NAME="name" --></h1>
<!-- TMPL_IF NAME="notes" --><p><!-- TMPL_VAR NAME="notes" --></p><!-- /TMPL_IF --> <!-- TMPL_IF NAME="notes" --><p><!-- TMPL_VAR NAME="notes" --></p><!-- /TMPL_IF -->
<!-- TMPL_IF name="pagination_bar" --><!-- TMPL_VAR name='pagination_bar'--><!-- /TMPL_IF --> <!-- TMPL_IF NAME="unlimited_total" --><p>Total number of rows matching the (unlimited) query is <!-- TMPL_VAR NAME="unlimited_total" -->.</p><!-- /TMPL_IF -->
<!-- TMPL_VAR NAME='pagination_bar' DEFAULT="" -->
<!-- TMPL_UNLESS name="errors" --> <!-- TMPL_UNLESS name="errors" -->
<table> <table>
<tr><!-- TMPL_LOOP NAME="header_row" --><th><!-- TMPL_VAR NAME="cell" --></th><!-- /TMPL_LOOP --></tr>
<!-- TMPL_LOOP NAME="results" --> <!-- TMPL_LOOP NAME="results" -->
<!-- TMPL_VAR NAME="row" --> <tr>
<!-- TMPL_LOOP NAME="cells" --><td><!-- TMPL_VAR NAME="cell" --></td><!-- /TMPL_LOOP -->
</tr>
<!-- /TMPL_LOOP --> <!-- /TMPL_LOOP -->
</table> </table>
<form action="/cgi-bin/koha/reports/guided_reports.pl" method="post"> <form action="/cgi-bin/koha/reports/guided_reports.pl" method="post">
@ -378,20 +389,6 @@ NAME="name" -->"><!-- TMPL_VAR NAME="name"--></label></td><td>
<input type="submit" name="submit" value="Download" /></fieldset> <input type="submit" name="submit" value="Download" /></fieldset>
</form> </form>
<!-- /TMPL_UNLESS --> <!-- /TMPL_UNLESS -->
<!-- TMPL_IF NAME="errors" -->
<form action="/cgi-bin/koha/reports/guided_reports.pl" method="post">
<div class="dialog alert">
<b>The following error was encountered:</b><br />
<!-- TMPL_LOOP name="errors" -->
<!-- TMPL_IF NAME="sqlerr" -->This report contains the SQL keyword <!-- TMPL_VAR name="sqlerr" -->.<br />Use of this keyword is not allowed in Koha reports due to security and data integrity risks.<br />Please return to the "Saved Reports" screen and delete this report.
<!-- TMPL_ELSIF NAME="queryerr" -->The database returned the following error: <br /><!-- TMPL_VAR NAME="queryerr" --><br />Please check the log for further details.
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</div>
<fieldset class="action"><input type="hidden" name="phase" value="Used saved" />
<input type="submit" name="submit" value="Saved Reports" /></fieldset>
</form>
<!-- /TMPL_IF -->
<!-- /TMPL_IF --> <!-- /TMPL_IF -->
<!-- TMPL_IF NAME="create" --> <!-- TMPL_IF NAME="create" -->
@ -399,10 +396,16 @@ NAME="name" -->"><!-- TMPL_VAR NAME="name"--></label></td><td>
<fieldset class="rows"> <fieldset class="rows">
<legend>Create Report From SQL</legend> <legend>Create Report From SQL</legend>
<ol> <ol>
<li><label for="reportname">Report Name:</label> <input type="text" id="reportname" name="reportname"<!--TMPL_IF NAME="reportname" --> value="<!-- TMPL_VAR NAME="reportname"-->"<!-- /TMPL_IF --> /> </li> <li><label for="reportname">Report Name:</label> <input type="text" id="reportname" name="reportname" <!-- TMPL_IF NAME="reportname" --> value="<!-- TMPL_VAR NAME="reportname" -->"<!-- /TMPL_IF --> /> </li>
<li><label for="notes">Notes:</label> <textarea id="notes" name="notes" cols="50" rows="2"><!--TMPL_IF NAME="notes" --><!-- TMPL_VAR NAME="notes"--><!-- /TMPL_IF --></textarea></li> <li><label for="notes">Notes:</label> <textarea id="notes" name="notes" cols="50" rows="2"><!-- TMPL_VAR NAME="notes" DEFAULT="" --></textarea></li>
<li><label for="types">Type:</label><select id="types" name="types"><!-- TMPL_LOOP NAME="types" --><option value="<!-- TMPL_VAR NAME="id" -->"<!-- TMPL_IF NAME="selected" --> selected="selected"<!-- /TMPL_IF -->><!-- TMPL_VAR NAME="name"--></option><!-- /TMPL_LOOP --></select></li> <li><label for="types">Type:</label>
<li><label for="sql">SQL: </label><textarea id="sql" name="sql" cols="50" rows="10"><!--TMPL_IF NAME="sql" --><!-- TMPL_VAR NAME="sql"--><!-- /TMPL_IF --></textarea></li> <select id="types" name="types">
<option value="1">Tabular</option>
<option value="2" disabled="disabled">Summary</option>
<option value="3" disabled="disabled">Matrix</option>
</select>
</li>
<li><label for="sql">SQL: </label><textarea id="sql" name="sql" cols="50" rows="10"><!-- TMPL_VAR NAME="sql" DEFAULT="" --></textarea></li>
</ol> </ol>
</fieldset> </fieldset>
@ -442,10 +445,9 @@ Sub report:<select name="subreport">
<p><!-- TMPL_VAR NAME="notes" --></p> <p><!-- TMPL_VAR NAME="notes" --></p>
<table> <table>
<!-- TMPL_LOOP NAME="results" --> <!-- TMPL_LOOP NAME="results" -->
<!-- TMPL_VAR NAME = "row" --> <!-- TMPL_VAR NAME="row" -->
<!-- /TMPL_LOOP --> <!-- /TMPL_LOOP -->
</table> </table>
<!-- /TMPL_IF --> <!-- /TMPL_IF -->
<!-- TMPL_IF NAME="showsql" --> <!-- TMPL_IF NAME="showsql" -->
@ -457,30 +459,30 @@ Sub report:<select name="subreport">
<h2>Your report has been saved</h2> <h2>Your report has been saved</h2>
<p>The report you have created has now been saved. You can now</p> <p>The report you have created has now been saved. You can now</p>
<ul> <ul>
<li>Access this report from the: <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved">Saved Reports Page</a></li> <li>Access this report from the: <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Use%20saved">Saved Reports Page</a></li>
<li>Schedule this report to run using the: <a href="/cgi-bin/koha/tools/scheduler.pl">Scheduler Tool</a></li> <li>Schedule this report to run using the: <a href="/cgi-bin/koha/tools/scheduler.pl">Scheduler Tool</a></li>
<li>Return to: <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved">Guided Reports</a></li> <li>Return to: <a href="/cgi-bin/koha/reports/guided_reports.pl?phase=Use%20saved">Guided Reports</a></li>
</ul> </ul>
<!-- /TMPL_UNLESS --> <!-- /TMPL_UNLESS -->
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="errors" --> <!-- TMPL_IF NAME="errors" -->
<form action="/cgi-bin/koha/reports/guided_reports.pl" method="post"> <form action="/cgi-bin/koha/reports/guided_reports.pl" method="post">
<div class="dialog alert"> <div class="dialog alert">
<b>The following error was encountered:</b><br /> <b>The following error was encountered:</b><br />
<!-- TMPL_LOOP name="errors" --> <!-- TMPL_LOOP name="errors" -->
<!-- TMPL_IF NAME="sqlerr" -->The SQL statement contains the keyword <!-- TMPL_VAR name="sqlerr" -->. Use of this keyword is not allowed in Koha reports due to security and data integrity risks. <!-- TMPL_IF NAME="sqlerr" -->This report contains the SQL keyword <b><!-- TMPL_VAR name="sqlerr" --></b>.
<!-- TMPL_ELSIF NAME="queryerr" -->The SQL statment is missing the SELECT keyword.<br />Please check the SQL statement syntax. <br />Use of this keyword is not allowed in Koha reports due to security and data integrity risks. Only SELECT queries are allowed.
<!-- /TMPL_IF --> <br />Please return to the &quot;Saved Reports&quot; screen and delete this report or retry creating a new one.
<!-- TMPL_ELSIF NAME="queryerr" -->The database returned the following error: <br /><!-- TMPL_VAR NAME="queryerr" --><br />Please check the log for further details.
<!-- TMPL_ELSE -->
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP --> <!-- /TMPL_LOOP -->
</div> </div>
<input type="hidden" name="sql" value="<!-- TMPL_VAR NAME="sql" ESCAPE="html" -->" /> <fieldset class="action"><input type="hidden" name="phase" value="Use saved" />
<input type="hidden" name="reportname" value="<!-- TMPL_VAR NAME="reportname" -->" /> <input type="submit" name="submit" value="Saved Reports" /></fieldset>
<input type="hidden" name="type" value="<!-- TMPL_VAR NAME="type" -->" />
<input type="hidden" name="notes" value="<!-- TMPL_VAR NAME="notes" -->" />
<fieldset class="action"><input type="hidden" name="phase" value="Create report from SQL" />
<input type="submit" name="submit" value="Edit SQL" /></fieldset>
</form> </form>
<!-- /TMPL_IF --> <!-- /TMPL_IF -->
<!-- /TMPL_IF -->
</div> </div>
</div> </div>

View file

@ -27,6 +27,7 @@ use Getopt::Long qw(:config auto_help auto_version);
use Pod::Usage; use Pod::Usage;
use Mail::Sendmail; use Mail::Sendmail;
use Text::CSV_XS; use Text::CSV_XS;
use CGI;
use vars qw($VERSION); use vars qw($VERSION);
@ -131,15 +132,22 @@ foreach my $report (@ARGV) {
} }
$verbose and print "SQL: $sql\n\n"; $verbose and print "SQL: $sql\n\n";
# my $results = execute_query($sql, undef, 0, 99999, $format, $report); # my $results = execute_query($sql, undef, 0, 99999, $format, $report);
my ($results) = execute_query($sql, undef, 0, 20, , ); my ($sth) = execute_query($sql);
# execute_query(sql, , 0, 20, , ) # execute_query(sql, , 0, 20, , )
my $count = scalar(@$results); my $count = scalar($sth->rows);
unless ($count) { unless ($count) {
print "NO OUTPUT: 0 results from execute_query\n"; print "NO OUTPUT: 0 results from execute_query\n";
next; next;
} }
$verbose and print "$count results from execute_query\n"; $verbose and print "$count results from execute_query\n";
my $message = "<table>\n" . join("\n", map {$_->{row}} @$results) . "\n</table>\n";
my $cgi = CGI->new();
my @rows = ();
while (my $line = $sth->fetchrow_arrayref) {
foreach (@$line) { defined($_) or $_ = ''; } # catch undef values, replace w/ ''
push @rows, $cgi->TR( join('', $cgi->td($line)) ) . "\n";
}
my $message = $cgi->table(join "", @rows);
if ($email){ if ($email){
my %mail = ( my %mail = (
@ -152,4 +160,8 @@ foreach my $report (@ARGV) {
} else { } else {
print $message; print $message;
} }
# my @xmlarray = ... ;
# my $url = "/cgi-bin/koha/reports/guided_reports.pl?phase=retrieve%20results&id=$id";
# my $xml = XML::Dumper->new()->pl2xml( \@xmlarray );
# store_results($id,$xml);
} }

View file

@ -20,6 +20,7 @@
use strict; use strict;
# use warnings; # FIXME # use warnings; # FIXME
use CGI; use CGI;
use Text::CSV;
use C4::Reports::Guided; use C4::Reports::Guided;
use C4::Auth; use C4::Auth;
use C4::Output; use C4::Output;
@ -39,7 +40,6 @@ Script to control the guided report creation
=cut =cut
my $input = new CGI; my $input = new CGI;
my $referer = $input->referer();
my ( $template, $borrowernumber, $cookie ) = get_template_and_user( my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
{ {
@ -52,43 +52,32 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
} }
); );
my @errors = ();
my $phase = $input->param('phase'); my $phase = $input->param('phase');
my $no_html = 0; # this will be set if we dont want to print out an html::template
if ( !$phase ) { if ( !$phase ) {
$template->param( 'start' => 1 ); $template->param( 'start' => 1 );
# show welcome page # show welcome page
} }
elsif ( $phase eq 'Build new' ) { elsif ( $phase eq 'Build new' ) {
# build a new report # build a new report
$template->param( 'build1' => 1 ); $template->param( 'build1' => 1 );
$template->param( 'areas' => get_report_areas() );
# get report areas
my $areas = get_report_areas();
$template->param( 'areas' => $areas );
} }
elsif ( $phase eq 'Use saved' ) {
elsif ( $phase eq 'Used saved' ) {
# use a saved report # use a saved report
# get list of reports and display them # get list of reports and display them
$template->param( 'saved1' => 1 ); $template->param( 'saved1' => 1 );
my $reports = get_saved_reports(); $template->param( 'savedreports' => get_saved_reports() );
$template->param( 'savedreports' => $reports );
} }
elsif ( $phase eq 'Delete Saved') { elsif ( $phase eq 'Delete Saved') {
# delete a report from the saved reports list # delete a report from the saved reports list
$no_html = 1;
my $id = $input->param('reports'); my $id = $input->param('reports');
delete_report($id); delete_report($id);
print $input->redirect("/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved"); print $input->redirect("/cgi-bin/koha/reports/guided_reports.pl?phase=Use%20saved");
exit;
} }
elsif ( $phase eq 'Show SQL'){ elsif ( $phase eq 'Show SQL'){
@ -98,7 +87,7 @@ elsif ( $phase eq 'Show SQL'){
$template->param( $template->param(
'sql' => $sql, 'sql' => $sql,
'showsql' => 1, 'showsql' => 1,
); );
} }
elsif ($phase eq 'retrieve results') { elsif ($phase eq 'retrieve results') {
@ -110,23 +99,17 @@ elsif ($phase eq 'retrieve results') {
'results' => $results, 'results' => $results,
'name' => $name, 'name' => $name,
'notes' => $notes, 'notes' => $notes,
); );
} }
elsif ( $phase eq 'Report on this Area' ) { elsif ( $phase eq 'Report on this Area' ) {
# they have choosen a new report and the area to report on # they have choosen a new report and the area to report on
# get area
my $area = $input->param('areas');
$template->param( $template->param(
'build2' => 1, 'build2' => 1,
'area' => $area 'area' => $input->param('areas'),
'types' => get_report_types(),
); );
# get report types
my $types = get_report_types();
$template->param( 'types' => $types );
} }
elsif ( $phase eq 'Choose this type' ) { elsif ( $phase eq 'Choose this type' ) {
@ -139,11 +122,8 @@ elsif ( $phase eq 'Choose this type' ) {
'build3' => 1, 'build3' => 1,
'area' => $area, 'area' => $area,
'type' => $type, 'type' => $type,
columns => get_columns($area,$input),
); );
# get columns
my $columns = get_columns($area,$input);
$template->param( 'columns' => $columns );
} }
elsif ( $phase eq 'Choose these columns' ) { elsif ( $phase eq 'Choose these columns' ) {
@ -154,16 +134,14 @@ elsif ( $phase eq 'Choose these columns' ) {
my $type = $input->param('type'); my $type = $input->param('type');
my @columns = $input->param('columns'); my @columns = $input->param('columns');
my $column = join( ',', @columns ); my $column = join( ',', @columns );
my $definitions = get_from_dictionary($area);
$template->param( $template->param(
'build4' => 1, 'build4' => 1,
'area' => $area, 'area' => $area,
'type' => $type, 'type' => $type,
'column' => $column, 'column' => $column,
definitions => get_from_dictionary($area),
criteria => get_criteria($area,$input),
); );
my $criteria = get_criteria($area,$input);
$template->param( 'criteria' => $criteria,
'definitions' => $definitions);
} }
elsif ( $phase eq 'Choose these criteria' ) { elsif ( $phase eq 'Choose these criteria' ) {
@ -229,7 +207,7 @@ elsif ( $phase eq 'Choose These Operations' ) {
'column' => $column, 'column' => $column,
'criteriastring' => $criteria, 'criteriastring' => $criteria,
'totals' => $totals, 'totals' => $totals,
'definition' => $definition, 'definition' => $definition,
); );
# get columns # get columns
@ -301,19 +279,14 @@ elsif ( $phase eq 'Save Report' ) {
my $name = $input->param('reportname'); my $name = $input->param('reportname');
my $type = $input->param('types'); my $type = $input->param('types');
my $notes = $input->param('notes'); my $notes = $input->param('notes');
my @errors = ();
my $error = {};
if ($sql =~ /;?\W?(UPDATE|DELETE|DROP|INSERT|SHOW|CREATE)\W/i) { if ($sql =~ /;?\W?(UPDATE|DELETE|DROP|INSERT|SHOW|CREATE)\W/i) {
$error->{'sqlerr'} = $1; push @errors, {sqlerr => $1};
push @errors, $error;
} }
elsif ($sql !~ /^(SELECT)/i) { elsif ($sql !~ /^(SELECT)/i) {
$error->{'queryerr'} = 1; push @errors, {queryerr => 1};
push @errors, $error;
} }
if (@errors) { if (@errors) {
$template->param( $template->param(
'save_successful' => 1,
'errors' => \@errors, 'errors' => \@errors,
'sql' => $sql, 'sql' => $sql,
'reportname'=> $name, 'reportname'=> $name,
@ -329,44 +302,45 @@ elsif ( $phase eq 'Save Report' ) {
} }
} }
# This condition is not used currently
#elsif ( $phase eq 'Execute' ) {
# # run the sql, and output results in a template
# my $sql = $input->param('sql');
# my $type = $input->param('type');
# my ($results, $total, $errors) = execute_query($sql, $type);
# $template->param(
# 'results' => $results,
# 'sql' => $sql,
# 'execute' => 1,
# );
#}
elsif ($phase eq 'Run this report'){ elsif ($phase eq 'Run this report'){
# execute a saved report # execute a saved report
# FIXME The default limit should not be hardcoded... my $limit = 20; # page size. # TODO: move to DB or syspref?
my $limit = 20; my $offset = 0;
my $offset;
my $report = $input->param('reports'); my $report = $input->param('reports');
# offset algorithm # offset algorithm
if ($input->param('page')) { if ($input->param('page')) {
$offset = ($input->param('page') - 1) * 20; $offset = ($input->param('page') - 1) * $limit;
}
else {
$offset = 0;
} }
my ($sql,$type,$name,$notes) = get_saved_report($report); my ($sql,$type,$name,$notes) = get_saved_report($report);
my ($results, $total, $errors) = execute_query($sql, $type, $offset, $limit); unless ($sql) {
push @errors, {no_sql_for_id=>$report};
}
my @rows = ();
my ($sth, $errors) = execute_query($sql, $offset, $limit);
my $total = select_2_select_count_value($sql) || 0;
unless ($sth) {
die "execute_query failed to return sth for report $report: $sql";
} else {
my $headref = $sth->{NAME} || [];
my @headers = map { +{ cell => $_ } } @$headref;
$template->param(header_row => \@headers);
while (my $row = $sth->fetchrow_arrayref()) {
my @cells = map { +{ cell => $_ } } @$row;
push @rows, { cells => \@cells };
}
}
my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0); my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0);
my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report&phase=Run%20this%20report"; my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report&phase=Run%20this%20report";
$template->param( $template->param(
'results' => $results, 'results' => \@rows,
'sql' => $sql, 'sql' => $sql,
'execute' => 1, 'execute' => 1,
'name' => $name, 'name' => $name,
'notes' => $notes, 'notes' => $notes,
'pagination_bar' => pagination_bar($url, $totpages, $input->param('page'), "page"), 'errors' => $errors,
'errors' => $errors, 'pagination_bar' => pagination_bar($url, $totpages, $input->param('page')),
'unlimited_total' => $total,
); );
} }
@ -374,59 +348,62 @@ elsif ($phase eq 'Export'){
binmode STDOUT, ':utf8'; binmode STDOUT, ':utf8';
# export results to tab separated text # export results to tab separated text
my $sql = $input->param('sql'); my $sql = $input->param('sql'); # FIXME: use sql from saved report ID#, not new user-supplied SQL!
my $format = $input->param('format'); my $format = $input->param('format');
my ($results, $total, $errors) = execute_query($sql,1,0,0,$format); my ($sth, $q_errors) = execute_query($sql);
if (!@$errors) { unless ($q_errors and @$q_errors) {
$no_html=1; print $input->header( -type => 'application/octet-stream',
print $input->header( -type => 'application/octet-stream', -attachment=>'reportresults.csv'
-attachment=>'reportresults.csv' );
); my $csv = Text::CSV->new({binary => 1});
print $results; $csv or die "Text::CSV->new({binary => 1}) FAILED: " . Text::CSV->error_diag();
if ($csv->combine(header_cell_values($sth))) {
print $csv->string(), "\n";
} else { } else {
$template->param( push @$q_errors, { combine => 'HEADER ROW: ' . $csv->error_diag() } ;
'results' => $results,
'sql' => $sql,
'execute' => 1,
'name' => 'Error exporting report!',
'notes' => '',
'pagination_bar' => '',
'errors' => $errors,
);
} }
while (my $row = $sth->fetchrow_arrayref()) {
if ($csv->combine(@$row)) {
print $csv->string(), "\n";
} else {
push @$q_errors, { combine => $csv->error_diag() } ;
}
}
foreach my $err (@$q_errors, @errors) {
print "# ERROR: " . (map {$_ . ": " . $err->{$_}} keys %$err) . "\n";
} # here we print all the non-fatal errors at the end. Not super smooth, but better than nothing.
exit;
}
$template->param(
'sql' => $sql,
'execute' => 1,
'name' => 'Error exporting report!',
'notes' => '',
'errors' => $q_errors,
);
} }
elsif ($phase eq 'Create report from SQL') { elsif ($phase eq 'Create report from SQL') {
# allow the user to paste in sql # allow the user to paste in sql
if ($input->param('sql')) { if ($input->param('sql')) {
$template->param( $template->param(
'sql' => $input->param('sql'), 'sql' => $input->param('sql'),
'reportname' => $input->param('reportname'), 'reportname' => $input->param('reportname'),
'notes' => $input->param('notes'), 'notes' => $input->param('notes'),
); );
} }
$template->param('create' => 1); $template->param('create' => 1);
my $types = get_report_types();
if (my $type = $input->param('type')) {
for my $i ( 0 .. $#{@$types}) {
@$types[$i]->{'selected'} = 1 if @$types[$i]->{'id'} eq $type;
}
}
$template->param( 'types' => $types );
} }
elsif ($phase eq 'Create Compound Report'){ elsif ($phase eq 'Create Compound Report'){
my $reports = get_saved_reports(); $template->param( 'savedreports' => get_saved_reports(),
$template->param( 'savedreports' => $reports,
'compound' => 1, 'compound' => 1,
); );
} }
elsif ($phase eq 'Save Compound'){ elsif ($phase eq 'Save Compound'){
my $master = $input->param('master'); my $master = $input->param('master');
my $subreport = $input->param('subreport'); my $subreport = $input->param('subreport');
# my $compound_report = create_compound($master,$subreport);
# my $results = run_compound($compound_report);
my ($mastertables,$subtables) = create_compound($master,$subreport); my ($mastertables,$subtables) = create_compound($master,$subreport);
$template->param( 'save_compound' => 1, $template->param( 'save_compound' => 1,
master=>$mastertables, master=>$mastertables,
@ -434,10 +411,23 @@ elsif ($phase eq 'Save Compound'){
); );
} }
$template->param( 'referer' => $referer, # pass $sth, get back an array of names for the column headers
sub header_cell_values ($) {
my $sth = shift or return ();
return @{$sth->{NAME}};
}
# pass $sth, get back a TMPL_LOOP-able set of names for the column headers
sub header_cell_loop ($) {
my @headers = map { +{ cell => $_ } } header_cell_values (shift);
return \@headers;
}
foreach (1..6) {
$template->param('build' . $_) and $template->param(buildx => $_) and last;
}
$template->param( 'referer' => $input->referer(),
'DHTMLcalendar_dateformat' => C4::Dates->DHTMLcalendar(), 'DHTMLcalendar_dateformat' => C4::Dates->DHTMLcalendar(),
); );
if (!$no_html){ output_html_with_http_headers $input, $cookie, $template->output;
output_html_with_http_headers $input, $cookie, $template->output;
}

View file

@ -1,38 +0,0 @@
#!/usr/bin/perl
# fix this line
use C4::Reports::Guided;
use C4::Context;
use Mail::Sendmail;
my ($report,$format,$email) = @ARGV;
my ($sql,$type) = get_saved_report($report);
my $results = execute_query($sql,$type,$format,$report);
my $message;
if ($format eq 'text'){
$message="<table>$results</table>";
}
if ($format eq 'url'){
$message="$results";
}
if ($email){
my $to = $email;
# should be koha admin email
my $from = C4::Context->preference('KohaAdminEmailAddress');
my $subject = 'Automated job run';
my %mail = (
To => $to,
From => $from,
Subject => $subject,
Message => $message
);
if (not(sendmail %mail)) {
warn "mail not sent";
}
}