use SL::Template;
use SL::User;
use Template;
+use URI;
use List::Util qw(first max min sum);
use List::MoreUtils qw(any);
my $standard_dbh;
END {
- if ($standard_dbh) {
- $standard_dbh->disconnect();
- undef $standard_dbh;
- }
+ disconnect_standard_dbh();
+}
+
+sub disconnect_standard_dbh {
+ return unless $standard_dbh;
+ $standard_dbh->disconnect();
+ undef $standard_dbh;
}
sub _store_value {
tie %{ $self }, 'SL::Watchdog';
}
- read(STDIN, $_, $ENV{CONTENT_LENGTH});
+ bless $self, $type;
- if ($ENV{QUERY_STRING}) {
- $_ = $ENV{QUERY_STRING};
- }
+ $self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
+ $self->_input_to_hash($ARGV[0]) if @ARGV && $ARGV[0];
- if ($ARGV[0]) {
- $_ = $ARGV[0];
+ if ($ENV{CONTENT_LENGTH}) {
+ my $content;
+ read STDIN, $content, $ENV{CONTENT_LENGTH};
+ $self->_request_to_hash($content);
}
- bless $self, $type;
-
- $self->_request_to_hash($_);
-
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);
-
- _recode_recursively($iconv, $self);
- }
+ my $encoding = $self->{INPUT_ENCODING} || $db_charset;
+ delete $self->{INPUT_ENCODING};
- 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.0";
+ #$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();
$self->show_generic_error($msg);
} else {
-
- die "Error: $msg\n";
+ print STDERR "Error: $msg\n";
+ ::end_of_request();
}
$main::lxdebug->leave_sub();
if (!$self->{header}) {
$self->header;
- print qq|
- <body>|;
+ print qq|<body>|;
}
print qq|
-
- <p><b>$msg</b>
+ <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>
+
+</body>
|;
} else {
$main::lxdebug->leave_sub();
}
+sub _get_request_uri {
+ my $self = shift;
+
+ return URI->new($ENV{HTTP_REFERER})->canonical() if $ENV{HTTP_X_FORWARDED_FOR};
+
+ my $scheme = $ENV{HTTPS} && (lc $ENV{HTTPS} eq 'on') ? 'https' : 'http';
+ my $port = $ENV{SERVER_PORT} || '';
+ $port = undef if (($scheme eq 'http' ) && ($port == 80))
+ || (($scheme eq 'https') && ($port == 443));
+
+ my $uri = URI->new("${scheme}://");
+ $uri->scheme($scheme);
+ $uri->port($port);
+ $uri->host($ENV{HTTP_HOST} || $ENV{SERVER_ADDR});
+ $uri->path_query($ENV{REQUEST_URI});
+ $uri->query('');
+
+ 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();
my $cgi = $main::cgi;
$cgi ||= CGI->new('');
- my $base_path;
-
- if ($ENV{HTTP_X_FORWARDED_FOR}) {
- $base_path = $ENV{HTTP_REFERER};
- $base_path =~ s|^.*?://.*?/|/|;
- } else {
- $base_path = $ENV{REQUEST_URI};
- }
- $base_path =~ s|[^/]+$||;
- $base_path =~ s|/$||;
-
my $session_cookie;
if (defined $main::auth) {
+ my $uri = $self->_get_request_uri;
+ my @segments = $uri->path_segments;
+ pop @segments;
+ $uri->path_segments(@segments);
+
my $session_cookie_value = $main::auth->get_session_id();
$session_cookie_value ||= 'NO_SESSION';
$session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
'-value' => $session_cookie_value,
- '-path' => $base_path,
+ '-path' => $uri->path,
'-secure' => $ENV{HTTPS});
}
</script>
| if $self->{"fokus"};
+ my $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| - Ver. | . $self->{"version"} . qq|";
+ //-->
+ </script>
+ |;
+
#Set Calendar
my $jsscript = "";
if ($self->{jsscript} == 1) {
? "$self->{title} - $self->{titlebar}"
: $self->{titlebar};
my $ajax = "";
- foreach my $item (@ { $self->{AJAX} }) {
+ for my $item (@ { $self->{AJAX} || [] }) {
$ajax .= $item->show_javascript();
}
$favicon
$jsscript
$ajax
-
$fokus
-
+ $title_hack
+
<link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
<meta name="robots" content="noindex,nofollow" />
return $output;
}
+sub redirect_header {
+ my $self = shift;
+ my $new_url = shift;
+
+ my $base_uri = $self->_get_request_uri;
+ my $new_uri = URI->new_abs($new_url, $base_uri);
+
+ die "Headers already sent" if $::self->{header};
+ $self->{header} = 1;
+
+ my $cgi = $main::cgi || CGI->new('');
+ return $cgi->redirect($new_uri);
+}
+
+sub set_standard_title {
+ $::lxdebug->enter_sub;
+ my $self = shift;
+
+ $self->{titlebar} = "Lx-Office " . $::locale->text('Version') . " $self->{version}";
+ $self->{titlebar} .= "- $::myconfig{name}" if $::myconfig{name};
+ $self->{titlebar} .= "- $::myconfig{dbname}" if $::myconfig{name};
+
+ $::lxdebug->leave_sub;
+}
+
sub _prepare_html_template {
$main::lxdebug->enter_sub();
my ($self, $file, $additional_params) = @_;
my $language;
- if (!defined(%main::myconfig) || !defined($main::myconfig{"countrycode"})) {
+ if (!%::myconfig || !$::myconfig{"countrycode"}) {
$language = $main::language;
} else {
$language = $main::myconfig{"countrycode"};
}
$language = "de" unless ($language);
- if (-f "templates/webpages/${file}_${language}.html") {
- if ((-f ".developer") &&
- (-f "templates/webpages/${file}_master.html") &&
- ((stat("templates/webpages/${file}_master.html"))[9] >
- (stat("templates/webpages/${file}_${language}.html"))[9])) {
- my $info = "Developer information: templates/webpages/${file}_master.html is newer than the localized version.\n" .
+ if (-f "templates/webpages/${file}.html") {
+ if ((-f ".developer") && ((stat("templates/webpages/${file}.html"))[9] > (stat("locale/${language}/all"))[9])) {
+ my $info = "Developer information: templates/webpages/${file}.html is newer than the translation file locale/${language}/all.\n" .
"Please re-run 'locales.pl' in 'locale/${language}'.";
print(qq|<pre>$info</pre>|);
- die($info);
+ ::end_of_request();
}
- $file = "templates/webpages/${file}_${language}.html";
- } elsif (-f "templates/webpages/${file}.html") {
$file = "templates/webpages/${file}.html";
+
} else {
my $info = "Web page template '${file}' not found.\n" .
"Please re-run 'locales.pl' in 'locale/${language}'.";
print(qq|<pre>$info</pre>|);
- die($info);
+ ::end_of_request();
}
if ($self->{"DEBUG"}) {
$jsc_dateformat =~ s/m+/\%m/gi;
$jsc_dateformat =~ s/y+/\%Y/gi;
$additional_params->{"myconfig_jsc_dateformat"} = $jsc_dateformat;
+ $additional_params->{"myconfig"} ||= \%::myconfig;
}
$additional_params->{"conf_dbcharset"} = $main::dbcharset;
$additional_params->{"conf_latex_templates"} = $main::latex;
$additional_params->{"conf_opendocument_templates"} = $main::opendocument_templates;
$additional_params->{"conf_vertreter"} = $main::vertreter;
+ $additional_params->{"conf_show_best_before"} = $main::show_best_before;
if (%main::debug_options) {
map { $additional_params->{'DEBUG_' . uc($_)} = $main::debug_options{$_} } keys %main::debug_options;
$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');
+ my $output;
+ $template->process($real_file, $additional_params, \$output) || die $template->error;
- if (!$in) {
- print STDERR "Error opening template file: $!";
- $main::lxdebug->leave_sub();
- return '';
- }
+ $main::lxdebug->leave_sub();
- my $input = join('', <$in>);
- $in->close();
+ return $output;
+}
- if ($main::locale) {
- $input = $main::locale->{iconv}->convert($input);
- }
+sub init_template {
+ my $self = shift;
- my $output;
- if (!$template->process(\$input, $additional_params, \$output)) {
- print STDERR $template->error();
- }
+ return if $self->template;
- $main::lxdebug->leave_sub();
+ 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;
+}
- return $output;
+sub template {
+ my $self = shift;
+ $self->{template_object} = shift if @_;
+ return $self->{template_object};
}
sub show_generic_error {
$self->header();
print $self->parse_html_template("generic/error", $add_params);
+ print STDERR "Error: $error\n";
+
$main::lxdebug->leave_sub();
- die("Error: $error\n");
+ ::end_of_request();
}
sub show_generic_information {
$main::lxdebug->leave_sub();
- die("Information: $text\n");
+ ::end_of_request();
}
# write Trigger JavaScript-Code ($qty = quantity of Triggers)
my ($self, $msg) = @_;
- if ($self->{callback}) {
-
- my ($script, $argv) = split(/\?/, $self->{callback}, 2);
- $script =~ s|.*/||;
- $script =~ s|[^a-zA-Z0-9_\.]||g;
- exec("perl", "$script", $argv);
-
- } else {
+ if (!$self->{callback}) {
$self->info($msg);
- exit;
+ ::end_of_request();
}
+# my ($script, $argv) = split(/\?/, $self->{callback}, 2);
+# $script =~ s|.*/||;
+# $script =~ s|[^a-zA-Z0-9_\.]||g;
+# exec("perl", "$script", $argv);
+
+ print $::form->redirect_header($self->{callback});
+
$main::lxdebug->leave_sub();
}
$main::lxdebug->enter_sub();
my ($self, $myconfig, $userspath) = @_;
- my ($template, $out);
+ my $out;
local (*IN, *OUT);
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"} =~ /elster(?:winston|taxbird)/i ) {
+ $template_type = 'xml';
- } elsif ( $self->{"format"} =~ /elstertaxbird/i ) {
- $template = XMLTemplate->new($self->{"IN"}, $self, $myconfig, $userspath);
+ } elsif ( $self->{"format"} =~ /excel/i ) {
+ $template_type = 'Excel';
+ $ext_for_format = 'xls';
} elsif ( defined $self->{'format'}) {
$self->error("Outputformat not defined. This may be a future feature: $self->{'format'}");
$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" };
$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') {
#print(STDERR "OUT $self->{OUT}\n");
for my $i (1 .. $self->{copies}) {
if ($self->{OUT}) {
- open(OUT, $self->{OUT})
- or $self->error($self->cleanup . "$self->{OUT} : $!");
+ open OUT, $self->{OUT} or $self->error($self->cleanup . "$self->{OUT} : $!");
+ print OUT while <IN>;
+ close OUT;
+ seek IN, 0, 0;
+
} else {
$self->{attachment_filename} = ($self->{attachment_filename})
? $self->{attachment_filename}
|;
- open(OUT, ">-") or $self->error($self->cleanup . "$!: STDOUT");
-
+ $::locale->with_raw_io(\*STDOUT, sub { print while <IN> });
}
-
- while (<IN>) {
- print OUT $_;
-
- }
-
- close(OUT);
-
- seek IN, 0, 0;
}
close(IN);
my $extension = $self->{format} =~ /pdf/i ? ".pdf"
: $self->{format} =~ /postscript/i ? ".ps"
: $self->{format} =~ /opendocument/i ? ".odt"
+ : $self->{format} =~ /excel/i ? ".xls"
: $self->{format} =~ /html/i ? ".html"
: "";
# 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
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
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");
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|;
$amounts{invtotal} = $self->{invtotal};
$amounts{total} = $self->{total};
}
+ $amounts{skonto_in_percent} = 100.0 * $self->{percent_skonto};
map { $amounts{$_} = $self->parse_amount($myconfig, $amounts{$_}) } keys %amounts;
map { $self->{payment_terms} =~ s/<%${_}%>/$formatted_amounts{$_}/g; } keys %formatted_amounts;
+ $self->{skonto_in_percent} = $formatted_amounts{skonto_in_percent};
+
$main::lxdebug->leave_sub();
}
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();
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";
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);
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);
$self->{addition}, $self->{what_done}, "$self->{snumbers}");
do_query($self, $dbh, $query, @values);
+ $dbh->commit;
+
$main::lxdebug->leave_sub();
}
special behaviour for empty strings in customerinitnumber field:
will in this case not increase the value, and return undef.
+=item redirect_header $url
+
+Generates a HTTP redirection header for the new C<$url>. Constructs an
+absolute URL including scheme, host name and port. If C<$url> is a
+relative URL then it is considered relative to Lx-Office base URL.
+
+This function C<die>s if headers have already been created with
+C<$::form-E<gt>header>.
+
+Examples:
+
+ print $::form->redirect_header('oe.pl?action=edit&id=1234');
+ print $::form->redirect_header('http://www.lx-office.org/');
+
=back
=cut