Merge branch 'master' of ssh://lx-office/~/lx-office-erp
[kivitendo-erp.git] / SL / Form.pm
index 6fa5132..f3fe6e5 100644 (file)
@@ -56,7 +56,7 @@ use SL::User;
 use Template;
 use URI;
 use List::Util qw(first max min sum);
-use List::MoreUtils qw(any);
+use List::MoreUtils qw(any apply);
 
 use strict;
 
@@ -257,21 +257,19 @@ sub new {
   my $db_charset   = $main::dbcharset;
   $db_charset    ||= Common::DEFAULT_CHARSET;
 
-  if ($self->{INPUT_ENCODING}) {
-    if (lc $self->{INPUT_ENCODING} ne lc $db_charset) {
-      require Text::Iconv;
-      my $iconv = Text::Iconv->new($self->{INPUT_ENCODING}, $db_charset);
+  my $encoding     = $self->{INPUT_ENCODING} || $db_charset;
+  delete $self->{INPUT_ENCODING};
 
-      _recode_recursively($iconv, $self);
-    }
-
-    delete $self->{INPUT_ENCODING};
-  }
+  _recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
 
   $self->{action}  =  lc $self->{action};
   $self->{action}  =~ s/( |-|,|\#)/_/g;
 
-  $self->{version} =  "2.6.1";
+  #$self->{version} =  "2.6.1";                 # Old hardcoded but secure style
+  open VERSION_FILE, "VERSION";                 # New but flexible code reads version from VERSION-file
+  $self->{version} =  <VERSION_FILE>;
+  close VERSION_FILE;
+  $self->{version}  =~ s/[^0-9A-Za-z\.\_\-]//g; # only allow numbers, letters, points, underscores and dashes. Prevents injecting of malicious code.
 
   $main::lxdebug->leave_sub();
 
@@ -472,13 +470,22 @@ sub info {
 
     if (!$self->{header}) {
       $self->header;
-      print qq|
-      <body>|;
+      print qq|<body>|;
     }
 
     print qq|
+    <p class="message_ok"><b>$msg</b></p>
+
+    <script type="text/javascript">
+    <!--
+    // If JavaScript is enabled, the whole thing will be reloaded.
+    // The reason is: When one changes his menu setup (HTML / XUL / CSS ...)
+    // it now loads the correct code into the browser instead of do nothing.
+    setTimeout("top.frames.location.href='login.pl'",500);
+    //-->
+    </script>
 
-    <p><b>$msg</b>
+</body>
     |;
 
   } else {
@@ -555,6 +562,20 @@ sub _get_request_uri {
   return $uri;
 }
 
+sub _add_to_request_uri {
+  my $self              = shift;
+
+  my $relative_new_path = shift;
+  my $request_uri       = shift || $self->_get_request_uri;
+  my $relative_new_uri  = URI->new($relative_new_path);
+  my @request_segments  = $request_uri->path_segments;
+
+  my $new_uri           = $request_uri->clone;
+  $new_uri->path_segments(@request_segments[0..scalar(@request_segments) - 2], $relative_new_uri->path_segments);
+
+  return $new_uri;
+}
+
 sub create_http_response {
   $main::lxdebug->enter_sub();
 
@@ -651,6 +672,20 @@ sub header {
     </script>
     | if $self->{"fokus"};
 
+  # if there is a title, we put some JavaScript in to the page, wich writes a
+  # meaningful title-tag for our frameset.
+    my $title_hack;
+    if ($self->{"title"}){
+               $title_hack = qq|
+               <script type="text/javascript">
+               <!--
+                 // Write a meaningful title-tag for our frameset.
+                 top.document.title="| . $self->{"title"} . qq| - | . $self->{"login"} . qq| - | . $::myconfig{dbname} . qq| - V| . $self->{"version"} . qq|";
+               //-->
+               </script>
+               |;
+       }
+
     #Set Calendar
     my $jsscript = "";
     if ($self->{jsscript} == 1) {
@@ -686,13 +721,12 @@ sub header {
   $favicon
   $jsscript
   $ajax
-
   $fokus
+  $title_hack
 
   <link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
 
   <meta name="robots" content="noindex,nofollow" />
-  <script type="text/javascript" src="js/highlight_input.js"></script>
 
   <link rel="stylesheet" type="text/css" href="css/tabcontent.css" />
   <script type="text/javascript" src="js/tabcontent.js">
@@ -794,13 +828,13 @@ sub _prepare_html_template {
   }
 
   if (%main::myconfig) {
-    map({ $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys(%main::myconfig));
-    my $jsc_dateformat = $main::myconfig{"dateformat"};
-    $jsc_dateformat =~ s/d+/\%d/gi;
-    $jsc_dateformat =~ s/m+/\%m/gi;
-    $jsc_dateformat =~ s/y+/\%Y/gi;
-    $additional_params->{"myconfig_jsc_dateformat"} = $jsc_dateformat;
+    $::myconfig{jsc_dateformat} = apply {
+      s/d+/\%d/gi;
+      s/m+/\%m/gi;
+      s/y+/\%Y/gi;
+    } $::myconfig{"dateformat"};
     $additional_params->{"myconfig"} ||= \%::myconfig;
+    map { $additional_params->{"myconfig_${_}"} = $main::myconfig{$_}; } keys %::myconfig;
   }
 
   $additional_params->{"conf_dbcharset"}              = $main::dbcharset;
@@ -833,39 +867,42 @@ sub parse_html_template {
 
   $additional_params ||= { };
 
-  $file = $self->_prepare_html_template($file, $additional_params);
-
-  my $template = Template->new({ 'INTERPOLATE'  => 0,
-                                 'EVAL_PERL'    => 0,
-                                 'ABSOLUTE'     => 1,
-                                 'CACHE_SIZE'   => 0,
-                                 'PLUGIN_BASE'  => 'SL::Template::Plugin',
-                                 'INCLUDE_PATH' => '.:templates/webpages',
-                               }) || die;
+  my $real_file = $self->_prepare_html_template($file, $additional_params);
+  my $template  = $self->template || $self->init_template;
 
   map { $additional_params->{$_} ||= $self->{$_} } keys %{ $self };
 
-  my $in = IO::File->new($file, 'r');
-
-  if (!$in) {
-    print STDERR "Error opening template file: $!";
-    $main::lxdebug->leave_sub();
-    return '';
-  }
-
-  my $input = join('', <$in>);
-  $in->close();
-
   my $output;
-  if (!$template->process(\$input, $additional_params, \$output)) {
-    print STDERR $template->error();
-  }
+  $template->process($real_file, $additional_params, \$output) || die $template->error;
 
   $main::lxdebug->leave_sub();
 
   return $output;
 }
 
+sub init_template {
+  my $self = shift;
+
+  return if $self->template;
+
+  return $self->template(Template->new({
+     'INTERPOLATE'  => 0,
+     'EVAL_PERL'    => 0,
+     'ABSOLUTE'     => 1,
+     'CACHE_SIZE'   => 0,
+     'PLUGIN_BASE'  => 'SL::Template::Plugin',
+     'INCLUDE_PATH' => '.:templates/webpages',
+     'COMPILE_EXT'  => '.tcc',
+     'COMPILE_DIR'  => $::userspath . '/templates-cache',
+  })) || die;
+}
+
+sub template {
+  my $self = shift;
+  $self->{template_object} = shift if @_;
+  return $self->{template_object};
+}
+
 sub show_generic_error {
   $main::lxdebug->enter_sub();
 
@@ -1181,7 +1218,7 @@ sub parse_template {
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig, $userspath) = @_;
-  my ($template, $out);
+  my $out;
 
   local (*IN, *OUT);
 
@@ -1190,31 +1227,29 @@ sub parse_template {
 
   my $ext_for_format;
 
+  my $template_type;
   if ($self->{"format"} =~ /(opendocument|oasis)/i) {
-    $template       = OpenDocumentTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+    $template_type  = 'OpenDocument';
     $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
 
   } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
     $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
-    $template         = LaTeXTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+    $template_type    = 'LaTeX';
     $ext_for_format   = 'pdf';
 
   } elsif (($self->{"format"} =~ /html/i) || (!$self->{"format"} && ($self->{"IN"} =~ /html$/i))) {
-    $template       = HTMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+    $template_type  = 'HTML';
     $ext_for_format = 'html';
 
   } elsif (($self->{"format"} =~ /xml/i) || (!$self->{"format"} && ($self->{"IN"} =~ /xml$/i))) {
-    $template       = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+    $template_type  = 'XML';
     $ext_for_format = 'xml';
 
-  } elsif ( $self->{"format"} =~ /elsterwinston/i ) {
-    $template = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
-
-  } elsif ( $self->{"format"} =~ /elstertaxbird/i ) {
-    $template = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+  } elsif ( $self->{"format"} =~ /elster(?:winston|taxbird)/i ) {
+    $template_type = 'xml';
 
   } elsif ( $self->{"format"} =~ /excel/i ) {
-    $template = ExcelTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+    $template_type  = 'Excel';
     $ext_for_format = 'xls';
 
   } elsif ( defined $self->{'format'}) {
@@ -1227,6 +1262,12 @@ sub parse_template {
     $self->error("Outputformat not defined: $self->{'format'}");
   }
 
+  my $template = SL::Template::create(type      => $template_type,
+                                      file_name => $self->{IN},
+                                      form      => $self,
+                                      myconfig  => $myconfig,
+                                      userspath => $userspath);
+
   # Copy the notes from the invoice/sales order etc. back to the variable "notes" because that is where most templates expect it to be.
   $self->{"notes"} = $self->{ $self->{"formname"} . "notes" };
 
@@ -1259,20 +1300,23 @@ sub parse_template {
     $self->{OUT} = ">$self->{tmpfile}";
   }
 
+  my $result;
+
   if ($self->{OUT}) {
-    open(OUT, "$self->{OUT}") or $self->error("$self->{OUT} : $!");
+    open OUT, "$self->{OUT}" or $self->error("$self->{OUT} : $!");
+    $result = $template->parse(*OUT);
+    close OUT;
+
   } else {
-    open(OUT, ">-") or $self->error("STDOUT : $!");
     $self->header;
+    $result = $template->parse(*STDOUT);
   }
 
-  if (!$template->parse(*OUT)) {
+  if (!$result) {
     $self->cleanup();
     $self->error("$self->{IN} : " . $template->get_error());
   }
 
-  close(OUT);
-
   if ($template->uses_temp_file() || $self->{media} eq 'email') {
 
     if ($self->{media} eq 'email') {
@@ -1529,15 +1573,21 @@ sub datetonum {
 
 # Database routines used throughout
 
+sub _dbconnect_options {
+  my $self    = shift;
+  my $options = { pg_enable_utf8 => $::locale->is_utf8,
+                  @_ };
+
+  return $options;
+}
+
 sub dbconnect {
   $main::lxdebug->enter_sub(2);
 
   my ($self, $myconfig) = @_;
 
   # connect to database
-  my $dbh =
-    DBI->connect($myconfig->{dbconnect},
-                 $myconfig->{dbuser}, $myconfig->{dbpasswd})
+  my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options)
     or $self->dberror;
 
   # set db options
@@ -1556,9 +1606,7 @@ sub dbconnect_noauto {
   my ($self, $myconfig) = @_;
 
   # connect to database
-  my $dbh =
-    DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser},
-                 $myconfig->{dbpasswd}, { AutoCommit => 0 })
+  my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, $self->_dbconnect_options(AutoCommit => 0))
     or $self->dberror;
 
   # set db options
@@ -1574,7 +1622,8 @@ sub dbconnect_noauto {
 sub get_standard_dbh {
   $main::lxdebug->enter_sub(2);
 
-  my ($self, $myconfig) = @_;
+  my $self     = shift;
+  my $myconfig = shift || \%::myconfig;
 
   if ($standard_dbh && !$standard_dbh->{Active}) {
     $main::lxdebug->message(LXDebug->INFO(), "get_standard_dbh: \$standard_dbh is defined but not Active anymore");
@@ -1775,8 +1824,9 @@ sub check_exchangerate {
 sub get_all_currencies {
   $main::lxdebug->enter_sub();
 
-  my ($self, $myconfig) = @_;
-  my $dbh = $self->get_standard_dbh($myconfig);
+  my $self     = shift;
+  my $myconfig = shift || \%::myconfig;
+  my $dbh      = $self->get_standard_dbh($myconfig);
 
   my $query = qq|SELECT curr FROM defaults|;
 
@@ -1968,7 +2018,7 @@ sub add_shipto {
   my @values;
 
   foreach my $item (qw(name department_1 department_2 street zipcode city country
-                       contact phone fax email)) {
+                       contact cp_gender phone fax email)) {
     if ($self->{"shipto$item"}) {
       $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
     }
@@ -1986,6 +2036,7 @@ sub add_shipto {
                        shiptocity = ?,
                        shiptocountry = ?,
                        shiptocontact = ?,
+                       shiptocp_gender = ?,
                        shiptophone = ?,
                        shiptofax = ?,
                        shiptoemail = ?
@@ -2001,6 +2052,7 @@ sub add_shipto {
                        shiptocity = ? AND
                        shiptocountry = ? AND
                        shiptocontact = ? AND
+                       shiptocp_gender = ? AND
                        shiptophone = ? AND
                        shiptofax = ? AND
                        shiptoemail = ? AND
@@ -2011,8 +2063,8 @@ sub add_shipto {
         $query =
           qq|INSERT INTO shipto (trans_id, shiptoname, shiptodepartment_1, shiptodepartment_2,
                                  shiptostreet, shiptozipcode, shiptocity, shiptocountry,
-                                 shiptocontact, shiptophone, shiptofax, shiptoemail, module)
-             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
+                                 shiptocontact, shiptocp_gender, shiptophone, shiptofax, shiptoemail, module)
+             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)|;
         do_query($self, $dbh, $query, $id, @values, $module);
       }
     }
@@ -2351,9 +2403,13 @@ sub _get_customers {
   my $options        = ref $key eq 'HASH' ? $key : { key => $key };
   $options->{key}  ||= "all_customers";
   my $limit_clause   = "LIMIT $options->{limit}" if $options->{limit};
-  my $where          = $options->{business_is_salesman} ? qq| AND business_id IN (SELECT id FROM business WHERE salesman)| : '';
 
-  my $query = qq|SELECT * FROM customer WHERE NOT obsolete $where ORDER BY name $limit_clause|;
+  my @where;
+  push @where, qq|business_id IN (SELECT id FROM business WHERE salesman)| if  $options->{business_is_salesman};
+  push @where, qq|NOT obsolete|                                            if !$options->{with_obsolete};
+  my $where_str = @where ? "WHERE " . join(" AND ", map { "($_)" } @where) : '';
+
+  my $query = qq|SELECT * FROM customer $where_str ORDER BY name $limit_clause|;
   $self->{ $options->{key} } = selectall_hashref_query($self, $dbh, $query);
 
   $main::lxdebug->leave_sub();
@@ -2615,7 +2671,7 @@ sub all_vc {
   my ($self, $myconfig, $table, $module) = @_;
 
   my $ref;
-  my $dbh = $self->get_standard_dbh($myconfig);
+  my $dbh = $self->get_standard_dbh;
 
   $table = $table eq "customer" ? "customer" : "vendor";
 
@@ -3023,8 +3079,8 @@ sub lastname_used {
 sub current_date {
   $main::lxdebug->enter_sub();
 
-  my $self              = shift;
-  my $myconfig          = shift  || \%::myconfig;
+  my $self     = shift;
+  my $myconfig = shift || \%::myconfig;
   my ($thisdate, $days) = @_;
 
   my $dbh = $self->get_standard_dbh($myconfig);
@@ -3227,8 +3283,8 @@ sub save_status {
 sub save_history {
   $main::lxdebug->enter_sub();
 
-  my $self = shift();
-  my $dbh = shift();
+  my $self = shift;
+  my $dbh  = shift || $self->get_standard_dbh;
 
   if(!exists $self->{employee_id}) {
     &get_employee($self, $dbh);
@@ -3241,6 +3297,8 @@ sub save_history {
                 $self->{addition}, $self->{what_done}, "$self->{snumbers}");
   do_query($self, $dbh, $query, @values);
 
+  $dbh->commit;
+
   $main::lxdebug->leave_sub();
 }