ReportGenerator::generate_with_header kann jetzt auch no_layout
[kivitendo-erp.git] / SL / ReportGenerator.pm
index b90b655..3ea62f9 100644 (file)
@@ -1,16 +1,11 @@
 package SL::ReportGenerator;
 
 use Data::Dumper;
-use Encode;
-use IO::Wrap;
 use List::Util qw(max);
 use Text::CSV_XS;
-use Text::Iconv;
 #use PDF::API2;    # these two eat up to .75s on startup. only load them if we actually need them
 #use PDF::Table;
 
-use SL::Form;
-
 use strict;
 
 # Cause locales.pl to parse these files:
@@ -28,6 +23,7 @@ sub new {
   $self->{options}  = {
     'std_column_visibility' => 0,
     'output_format'         => 'HTML',
+    'controller_class   '   => '',
     'allow_pdf_export'      => 1,
     'allow_csv_export'      => 1,
     'html_template'         => 'report_generator/html_report',
@@ -77,6 +73,12 @@ sub set_columns {
     $column->{visible} = $self->{options}->{std_column_visibility} unless defined $column->{visible};
   }
 
+  if( $::form->{report_generator_csv_options_for_import} ) {
+    foreach my $key (keys %{ $self->{columns} }) {
+      $self->{columns}{$key}{text} = $key;
+    }
+  }
+
   $self->set_column_order(sort keys %{ $self->{columns} });
 }
 
@@ -208,14 +210,17 @@ sub set_custom_headers {
 sub get_attachment_basename {
   my $self     = shift;
   my $filename =  $self->{options}->{attachment_basename} || 'report';
+
+  # FIXME: this is bonkers. add a real sluggify method somewhere or import one.
   $filename    =~ s|.*\\||;
   $filename    =~ s|.*/||;
+  $filename    =~ s| |_|g;
 
   return $filename;
 }
 
 sub generate_with_headers {
-  my $self   = shift;
+  my ($self, %params) = @_;
   my $format = lc $self->{options}->{output_format};
   my $form   = $self->{form};
 
@@ -226,16 +231,19 @@ sub generate_with_headers {
   if ($format eq 'html') {
     my $title      = $form->{title};
     $form->{title} = $self->{title} if ($self->{title});
-    $form->header();
+    $form->header(no_layout => $params{no_layout});
     $form->{title} = $title;
 
     print $self->generate_html_content();
 
   } elsif ($format eq 'csv') {
+    # FIXME: don't do mini http in here
     my $filename = $self->get_attachment_basename();
     print qq|content-type: text/csv\n|;
     print qq|content-disposition: attachment; filename=${filename}.csv\n\n|;
-    $self->generate_csv_content();
+    $::locale->with_raw_io(\*STDOUT, sub {
+      $self->generate_csv_content();
+    });
 
   } elsif ($format eq 'pdf') {
     $self->generate_pdf_content();
@@ -392,6 +400,7 @@ sub prepare_html_content {
     'EXPORT_VARIABLE_LIST' => join(' ', @{ $self->{export}->{variable_list} }),
     'EXPORT_NEXTSUB'       => $self->{export}->{nextsub},
     'DATA_PRESENT'         => $self->{data_present},
+    'CONTROLLER_DISPATCH'  => $opts->{controller_class},
   };
 
   return $variables;
@@ -401,7 +410,9 @@ sub generate_html_content {
   my $self      = shift;
   my $variables = $self->prepare_html_content();
 
-  return $self->{form}->parse_html_template($self->{options}->{html_template}, $variables);
+  my $stuff  = $self->{form}->parse_html_template($self->{options}->{html_template}, $variables);
+  $::lxdebug->dump(0,  "stuff", $stuff);
+  return $stuff;
 }
 
 sub _cm2bp {
@@ -410,15 +421,6 @@ sub _cm2bp {
   return $_[0] * 72 / 2.54;
 }
 
-sub _decode_text {
-  my $self = shift;
-  my $text = shift;
-
-  $text    = decode('UTF-8', $text) if ($self->{text_is_utf8});
-
-  return $text;
-}
-
 sub generate_pdf_content {
   eval {
     require PDF::API2;
@@ -436,12 +438,11 @@ sub generate_pdf_content {
   my (@data, @column_props, @cell_props);
 
   my ($data_row, $cell_props_row);
-  my @visible_columns = $self->get_visible_columns('HTML');
+  my @visible_columns = $self->get_visible_columns('PDF');
   my $num_columns     = scalar @visible_columns;
   my $num_header_rows = 1;
 
-  my $font_encoding     = $main::dbcharset || 'ISO-8859-15';
-  $self->{text_is_utf8} = $font_encoding =~ m/^utf-?8$/i;
+  my $font_encoding   = $::lx_office_conf{system}->{dbcharset} || 'ISO-8859-15';
 
   foreach my $name (@visible_columns) {
     push @column_props, { 'justify' => $self->{columns}->{$name}->{align} eq 'right' ? 'right' : 'left' };
@@ -456,7 +457,7 @@ sub generate_pdf_content {
     foreach my $name (@visible_columns) {
       my $column = $self->{columns}->{$name};
 
-      push @{ $data_row },       $self->_decode_text($column->{text});
+      push @{ $data_row },       $column->{text};
       push @{ $cell_props_row }, {};
     }
 
@@ -470,7 +471,7 @@ sub generate_pdf_content {
       push @cell_props, $cell_props_row;
 
       foreach my $custom_header_col (@{ $custom_header_row }) {
-        push @{ $data_row }, $self->_decode_text($custom_header_col->{text});
+        push @{ $data_row }, $custom_header_col->{text};
 
         my $num_output  = ($custom_header_col->{colspan} * 1 > 1) ? $custom_header_col->{colspan} : 1;
         if ($num_output > 1) {
@@ -488,7 +489,7 @@ sub generate_pdf_content {
   foreach my $row_set (@{ $self->{data} }) {
     if ('HASH' eq ref $row_set) {
       if ($row_set->{type} eq 'colspan_data') {
-        push @data, [ $self->_decode_text($row_set->{data}) ];
+        push @data, [ $row_set->{data} ];
 
         $cell_props_row = [];
         push @cell_props, $cell_props_row;
@@ -512,7 +513,7 @@ sub generate_pdf_content {
       my $col_idx = 0;
       foreach my $col_name (@visible_columns) {
         my $col = $row->{$col_name};
-        push @{ $data_row }, $self->_decode_text(join("\n", @{ $col->{data} }));
+        push @{ $data_row }, join("\n", @{ $col->{data} || [] });
 
         $column_props[$col_idx]->{justify} = 'right' if ($col->{align} eq 'right');
 
@@ -577,13 +578,13 @@ sub generate_pdf_content {
   my $font_height       = $font_size + 2 * $padding;
   my $title_font_height = $font_size + 2 * $padding;
 
-  my $header_height     = 2 * $title_font_height if ($opts->{title});
-  my $footer_height     = 2 * $font_height       if ($pdfopts->{number});
+  my $header_height     = $opts->{title}     ? 2 * $title_font_height : undef;
+  my $footer_height     = $pdfopts->{number} ? 2 * $font_height       : undef;
 
   my $top_text_height   = 0;
 
   if ($self->{options}->{top_info_text}) {
-    my $top_text     =  $self->_decode_text($self->{options}->{top_info_text});
+    my $top_text     =  $self->{options}->{top_info_text};
     $top_text        =~ s/\r//g;
     $top_text        =~ s/\n+$//;
 
@@ -631,7 +632,7 @@ sub generate_pdf_content {
     my $curpage  = $pdf->openpage($page_num);
 
     if ($pdfopts->{number}) {
-      my $label    = $self->_decode_text($main::locale->text("Page #1/#2", $page_num, $pdf->pages()));
+      my $label    = $main::locale->text("Page #1/#2", $page_num, $pdf->pages());
       my $text_obj = $curpage->text();
 
       $text_obj->font($font, $font_size);
@@ -640,7 +641,7 @@ sub generate_pdf_content {
     }
 
     if ($opts->{title}) {
-      my $title    = $self->_decode_text($opts->{title});
+      my $title    = $opts->{title};
       my $text_obj = $curpage->text();
 
       $text_obj->font($font, $title_font_size);
@@ -671,7 +672,9 @@ sub generate_pdf_content {
     print qq|content-type: application/pdf\n|;
     print qq|content-disposition: attachment; filename=${filename}.pdf\n\n|;
 
-    print $content;
+    $::locale->with_raw_io(\*STDOUT, sub {
+      print $content;
+    });
   }
 }
 
@@ -697,19 +700,26 @@ sub _print_content {
   }
 }
 
-sub unescape_string {
-  my $self  = shift;
-  my $text  = shift;
-  my $iconv = $main::locale->{iconv};
+sub _handle_quoting_and_encoding {
+  my ($self, $text, $do_unquote) = @_;
 
-  $text     = $main::locale->unquote_special_chars('HTML', $text);
-  $text     = $main::locale->{iconv}->convert($text) if ($main::locale->{iconv});
+  $text = $main::locale->unquote_special_chars('HTML', $text) if $do_unquote;
+  $text = Encode::encode('UTF-8', $text) if $::locale->is_utf8;
 
   return $text;
 }
 
 sub generate_csv_content {
-  my $self = shift;
+  my $self   = shift;
+  my $stdout = ($::dispatcher->get_standard_filehandles)[1];
+
+  # Text::CSV_XS seems to downgrade to bytes already (see
+  # SL/FCGIFixes.pm). Therefore don't let FCGI do that again.
+  $::locale->with_raw_io($stdout, sub { $self->_generate_csv_content($stdout) });
+}
+
+sub _generate_csv_content {
+  my ($self, $stdout) = @_;
 
   my %valid_sep_chars    = (';' => ';', ',' => ',', ':' => ':', 'TAB' => "\t");
   my %valid_escape_chars = ('"' => 1, "'" => 1);
@@ -729,12 +739,11 @@ sub generate_csv_content {
                                 'quote_char'  => $quote_char,
                                 'eol'         => $eol, });
 
-  my $stdout          = wraphandle(\*STDOUT);
   my @visible_columns = $self->get_visible_columns('CSV');
 
   if ($opts->{headers}) {
     if (!$self->{custom_headers}) {
-      $csv->print($stdout, [ map { $self->unescape_string($self->{columns}->{$_}->{text}) } @visible_columns ]);
+      $csv->print($stdout, [ map { $self->_handle_quoting_and_encoding($self->{columns}->{$_}->{text}, 1) } @visible_columns ]);
 
     } else {
       foreach my $row (@{ $self->{custom_headers} }) {
@@ -742,7 +751,7 @@ sub generate_csv_content {
 
         foreach my $col (@{ $row }) {
           my $num_output = ($col->{colspan} && ($col->{colspan} > 1)) ? $col->{colspan} : 1;
-          push @{ $fields }, ($self->unescape_string($col->{text})) x $num_output;
+          push @{ $fields }, ($self->_handle_quoting_and_encoding($col->{text}, 1)) x $num_output;
         }
 
         $csv->print($stdout, $fields);
@@ -764,7 +773,7 @@ sub generate_csv_content {
         my $num_output = ($row->{$col}{colspan} && ($row->{$col}->{colspan} > 1)) ? $row->{$col}->{colspan} : 1;
         $skip_next     = $num_output - 1;
 
-        push @data, join($eol, map { s/\r?\n/$eol/g; $_ } @{ $row->{$col}->{data} });
+        push @data, join($eol, map { s/\r?\n/$eol/g; $self->_handle_quoting_and_encoding($_, 0) } @{ $row->{$col}->{data} });
         push @data, ('') x $skip_next if ($skip_next);
       }
 
@@ -773,6 +782,10 @@ sub generate_csv_content {
   }
 }
 
+sub check_for_pdf_api {
+  return eval { require PDF::API2; 1; } ? 1 : 0;
+}
+
 1;
 
 __END__
@@ -926,6 +939,12 @@ Used to determine if a button for CSV export should be displayed. Default is yes
 
 The template to be used for HTML reports. Default is 'report_generator/html_report'.
 
+=item controller_class
+
+If this is used from a C<SL::Controller::Base> based controller class, pass the
+class name here and make sure C<SL::Controller::Helper::ReportGenerator> is
+used in the controller. That way the exports stay functional.
+
 =back
 
 =head2 PDF Options