Merge branch 'master' of vc.linet-services.de:public/lx-office-erp
[kivitendo-erp.git] / SL / Controller / Base.pm
index fff11ca..ac09057 100644 (file)
@@ -5,7 +5,10 @@ use strict;
 use parent qw(Rose::Object);
 
 use Carp;
+use IO::File;
 use List::Util qw(first);
+use SL::Request qw(flatten);
+use SL::MoreCommon qw(uri_encode);
 
 #
 # public/helper functions
@@ -20,7 +23,7 @@ sub url_for {
   my $controller  = delete($params{controller}) || $self->_controller_name;
   my $action      = delete($params{action})     || 'dispatch';
   $params{action} = "${controller}/${action}";
-  my $query       = join('&', map { $::form->escape($_) . '=' . $::form->escape($params{$_}) } keys %params);
+  my $query       = join '&', map { uri_encode($_->[0]) . '=' . uri_encode($_->[1]) } @{ flatten(\%params) };
 
   return "controller.pl?${query}";
 }
@@ -29,7 +32,12 @@ sub redirect_to {
   my $self = shift;
   my $url  = $self->url_for(@_);
 
-  print $::cgi->redirect($url);
+  if ($self->delay_flash_on_redirect) {
+    require SL::Helper::Flash;
+    SL::Helper::Flash::delay_flash();
+  }
+
+  print $::request->{cgi}->redirect($url);
 }
 
 sub render {
@@ -44,6 +52,9 @@ sub render {
   if ($options->{inline}) {
     $source = \$template;
 
+  } elsif($options->{raw}) {
+    $source =  $template;
+
   } else {
     $source = "templates/webpages/${template}." . $options->{type};
     croak "Template file ${source} not found" unless -f $source;
@@ -64,24 +75,46 @@ sub render {
   }
 
   my %params = ( %locals,
-                 AUTH     => $::auth,
-                 FORM     => $::form,
-                 LOCALE   => $::locale,
-                 LXCONFIG => \%::lx_office_conf,
-                 LXDEBUG  => $::lxdebug,
-                 MYCONFIG => \%::myconfig,
-                 SELF     => $self,
+                 AUTH          => $::auth,
+                 FLASH         => $::form->{FLASH},
+                 FORM          => $::form,
+                 INSTANCE_CONF => $::instance_conf,
+                 LOCALE        => $::locale,
+                 LXCONFIG      => \%::lx_office_conf,
+                 LXDEBUG       => $::lxdebug,
+                 MYCONFIG      => \%::myconfig,
+                 SELF          => $self,
                );
 
   my $output;
-  my $parser = $self->_template_obj;
-  $parser->process($source, \%params, \$output) || croak $parser->error;
+  if (!$options->{raw}) {
+    my $parser = $self->_template_obj;
+    $parser->process($source, \%params, \$output) || croak $parser->error;
+  } else {
+    $output = $$source;
+  }
 
   print $output unless $options->{inline} || $options->{no_output};
 
   return $output;
 }
 
+sub send_file {
+  my ($self, $file_name, %params) = @_;
+
+  my $file            = IO::File->new($file_name, 'r') || croak("Cannot open file '${file_name}'");
+  my $content_type    =  $params{type} || 'application/octet_stream';
+  my $attachment_name =  $params{name} || $file_name;
+  $attachment_name    =~ s:.*//::g;
+
+  print $::form->create_http_response(content_type        => $content_type,
+                                      content_disposition => 'attachment; filename="' . $attachment_name . '"',
+                                      content_length      => -s $file);
+
+  $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
+  $file->close;
+}
+
 #
 # Before/after run hooks
 #
@@ -126,6 +159,14 @@ sub _run_hooks {
   }
 }
 
+#
+#  behaviour. override these
+#
+
+sub delay_flash_on_redirect {
+  0;
+}
+
 #
 # private functions -- for use in Base only
 #
@@ -156,9 +197,13 @@ sub _dispatch {
   my $action  = first { $::form->{"action_${_}"} } @actions;
   my $sub     = "action_${action}";
 
-  $self->_run_hooks('before', $action);
-  $self->$sub(@_);
-  $self->_run_hooks('after', $action);
+  if ($self->can($sub)) {
+    $self->_run_hooks('before', $action);
+    $self->$sub(@_);
+    $self->_run_hooks('after', $action);
+  } else {
+    $::form->error($::locale->text('Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.'));
+  }
 }
 
 sub _template_obj {
@@ -299,6 +344,10 @@ containing the template code to interprete. Additionally the output
 will not be sent to the browser. Instead it is only returned to the
 caller.
 
+If C<< $options->{raw} >> is trueish, the function will treat the input as
+already parsed, and will not filter the input through Template. Unlike
+C<inline>, the input is taked as a reference.
+
 If C<< $options->{inline} >> is falsish then C<$template> is
 interpreted as the name of a template file. It is prefixed with
 "templates/webpages/" and postfixed with a file extension based on
@@ -367,6 +416,21 @@ browser. Typical use for actions called via AJAX:
   $self->render('todo/single_item', { type => 'js' },
                 item => $employee->most_important_todo_item);
 
+=item C<send_file $file_name, [%params]>
+
+Sends the file C<$file_name> to the browser including appropriate HTTP
+headers for a download. C<%params> can include the following:
+
+=over 2
+
+=item * C<type> -- the file's content type; defaults to
+'application/octet_stream'
+
+=item * C<name> -- the name presented to the browser; defaults to
+C<$file_name>
+
+=back
+
 =item C<url_for $url>
 
 =item C<url_for $params>
@@ -433,6 +497,12 @@ action.
 
 The hook's return values are discarded.
 
+=item delay_flash_on_redirect
+
+May be overridden by a controller. If this method returns true, redirect_to
+will delay all flash messages for the current request. Defaults to false for
+compatibility reasons.
+
 =back
 
 =head2 PRIVATE FUNCTIONS