X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FTemplate.pm;h=6da93d9b396806a0f6270c0f5054217dc3ddde36;hb=2868feee8fb33457e7562f02778186b5b5c1a2b6;hp=5763eee46e161ecd4387be4b7fd2a63661aac4dc;hpb=54e4131e091831e00a861fe2c4f53e344b87ddca;p=kivitendo-erp.git diff --git a/SL/Template.pm b/SL/Template.pm index 5763eee46..6da93d9b3 100644 --- a/SL/Template.pm +++ b/SL/Template.pm @@ -28,12 +28,25 @@ sub new { sub _init { my $self = shift; - $self->{"source"} = shift; - $self->{"form"} = shift; - $self->{"myconfig"} = shift; - $self->{"userspath"} = shift; + $self->{source} = shift; + $self->{form} = shift; + $self->{myconfig} = shift; + $self->{userspath} = shift; - $self->{"error"} = undef; + $self->{error} = undef; + + $self->set_tag_style('<%', '%>'); +} + +sub set_tag_style { + my $self = shift; + my $tag_start = shift; + my $tag_end = shift; + + $self->{tag_start} = $tag_start; + $self->{tag_end} = $tag_end; + $self->{tag_start_qm} = quotemeta $tag_start; + $self->{tag_end_qm} = quotemeta $tag_end; } sub cleanup { @@ -86,29 +99,7 @@ sub format_string { my ($self, $variable) = @_; my $form = $self->{"form"}; - my %replace = - ('order' => [ - '&', quotemeta("\n"), - '"', '\$', '%', '_', '#', quotemeta('^'), - '{', '}', '<', '>', '£', "\r" - ], - '"' => "''", - '&' => '\&', - '\$' => '\$', - '%' => '\%', - '_' => '\_', - '#' => '\#', - '{' => '\{', - '}' => '\}', - '<' => '$<$', - '>' => '$>$', - '£' => '\pounds ', - "\r" => "", - quotemeta('^') => '\^\\', - quotemeta("\n") => '\newline ' - ); - - map({ $variable =~ s/$_/$replace{$_}/g; } @{ $replace{"order"} }); + $variable = $main::locale->quote_special_chars('Template/LaTeX', $variable); # Allow some HTML markup to be converted into the output format's # corresponding markup code, e.g. bold or italic. @@ -121,231 +112,382 @@ sub format_string { $variable =~ s/\$\<\$${key}\$\>\$(.*?)\$<\$\/${key}\$>\$/\\${new}\{$1\}/gi; } + $variable =~ s/[\x00-\x1f]//g; + return $variable; } -sub parse { - my $self = $_[0]; - local *OUT = $_[1]; - my ($form, $myconfig) = ($self->{"form"}, $self->{"myconfig"}); +sub substitute_vars { + my ($self, $text, @indices) = @_; - # Some variables used for page breaks - my ($chars_per_line, $lines_on_first_page, $lines_on_second_page) = - (0, 0, 0); - my ($current_page, $current_line, $current_row) = (1, 1, 0); - my ($pagebreak, $sum, $two_passes, $nodiscount_sum) = ("", 0, 0, 0); - my ($par, $var); + my $form = $self->{"form"}; - # Do we have to run LaTeX two times? This is needed if - # the template contains page references. - $two_passes = 0; + while ($text =~ /$self->{tag_start_qm}(.+?)$self->{tag_end_qm}/) { + my ($tag_pos, $tag_len) = ($-[0], $+[0] - $-[0]); + my ($var, @options) = split(/\s+/, $1); + my $value = $form->{$var}; - if (!open(IN, "$form->{templates}/$form->{IN}")) { - $self->{"error"} = "$!"; - return 0; + for (my $i = 0; $i < scalar(@indices); $i++) { + last unless (ref($value) eq "ARRAY"); + $value = $value->[$indices[$i]]; + } + $value = $self->format_string($value) unless (grep(/^NOESCAPE$/, @options)); + substr($text, $tag_pos, $tag_len) = $value; } - @_ = ; - close(IN); - # first we generate a tmpfile - # read file and replace <%variable%> - while ($_ = shift) { - $par = ""; - $var = $_; + return $text; +} - $two_passes = 1 if (/\\pageref/); +sub parse_foreach { + my ($self, $var, $text, $start_tag, $end_tag, @indices) = @_; - # detect pagebreak block and its parameters - if (/\s*<%pagebreak ([0-9]+) ([0-9]+) ([0-9]+)%>/) { - $chars_per_line = $1; - $lines_on_first_page = $2; - $lines_on_second_page = $3; + my ($form, $new_contents) = ($self->{"form"}, ""); - while ($_ = shift) { - last if (/\s*<%end pagebreak%>/); - $pagebreak .= $_; - } - } + my $ary = $form->{$var}; + for (my $i = 0; $i < scalar(@indices); $i++) { + last unless (ref($ary) eq "ARRAY"); + $ary = $ary->[$indices[$i]]; + } - if (/\s*<%foreach /) { + my $sum = 0; + my $current_page = 1; + my ($current_line, $corrent_row) = (0, 1); - # this one we need for the count - chomp $var; - $var =~ s/\s*<%foreach (.+?)%>/$1/; - while ($_ = shift) { - last if (/\s*<%end /); + for (my $i = 0; $i < scalar(@{$ary}); $i++) { + $form->{"__first__"} = $i == 0; + $form->{"__last__"} = ($i + 1) == scalar(@{$ary}); + $form->{"__odd__"} = (($i + 1) % 2) == 1; + $form->{"__counter__"} = $i + 1; + + if ((scalar(@{$form->{"description"}}) == scalar(@{$ary})) && + $self->{"chars_per_line"}) { + my $lines = + int(length($form->{"description"}->[$i]) / $self->{"chars_per_line"}); + my $lpp; + + $form->{"description"}->[$i] =~ s/(\\newline\s?)*$//; + my $_description = $form->{"description"}->[$i]; + while ($_description =~ /\\newline/) { + $lines++; + $_description =~ s/\\newline//; + } + $lines++; - # store line in $par - $par .= $_; + if ($current_page == 1) { + $lpp = $self->{"lines_on_first_page"}; + } else { + $lpp = $self->{"lines_on_second_page"}; } - # Count the number of "lines" for our variable. Also find the forced pagebreak entries. - my $num_entries = scalar(@{$form->{$var}}); - my @forced_pagebreaks = (); - for (my $i = 0; $i < scalar(@{$form->{$var}}); $i++) { - if ($form->{$var}->[$i] =~ //) { - push(@forced_pagebreaks, $i); - } + # Yes we need a manual page break -- or the user has forced one + if ((($current_line + $lines) > $lpp) || + ($form->{"description"}->[$i] =~ //)) { + my $pb = $self->{"pagebreak_block"}; + + # replace the special variables <%sumcarriedforward%> + # and <%lastpage%> + + my $psum = $form->format_amount($self->{"myconfig"}, $sum, 2); + $pb =~ s/$self->{tag_start_qm}sumcarriedforward$self->{tag_end_qm}/$psum/g; + $pb =~ s/$self->{tag_start_qm}lastpage$self->{tag_end_qm}/$current_page/g; + + my $new_text = $self->parse_block($pb, (@indices, $i)); + return undef unless (defined($new_text)); + $new_contents .= $new_text; + + $current_page++; + $current_line = 0; } + $current_line += $lines; + } + if ($i < scalar(@{$form->{"linetotal"}})) { + $sum += $form->parse_amount($self->{"myconfig"}, + $form->{"linetotal"}->[$i]); + } + + $form->{"cumulatelinetotal"}[$i] = $form->format_amount($self->{"myconfig"}, $sum, 2); + + my $new_text = $self->parse_block($text, (@indices, $i)); + return undef unless (defined($new_text)); + $new_contents .= $start_tag . $new_text . $end_tag; + } + map({ delete($form->{"__${_}__"}); } qw(first last odd counter)); - $current_line = 1; - # display contents of $form->{number}[] array - for ($i = 0; $i < $num_entries; $i++) { - # Try to detect whether a manual page break is necessary - # but only if there was a <%pagebreak ...%> block before - - if ($chars_per_line) { - my $lines = - int(length($form->{"description"}->[$i]) / $chars_per_line + 0.95); - my $lpp; - - $form->{"description"}->[$i] =~ s/(\\newline\s?)*$//; - my $_description = $form->{"description"}->[$i]; - while ($_description =~ /\\newline/) { - $lines++; - $_description =~ s/\\newline//; - } - $lines++; + return $new_contents; +} - if ($current_page == 1) { - $lpp = $lines_on_first_page; - } else { - $lpp = $lines_on_second_page; - } +sub find_end { + my ($self, $text, $pos, $var, $not) = @_; - # Yes we need a manual page break -- or the user has forced one - if ((($current_line + $lines) > $lpp) || - grep(/^${current_row}$/, @forced_pagebreaks)) { - my $pb = $pagebreak; + my $tag_start_len = length $self->{tag_start}; - # replace the special variables <%sumcarriedforward%> - # and <%lastpage%> + my $depth = 1; + $pos = 0 unless ($pos); - my $psum = $form->format_amount($myconfig, $sum, 2); - my $nodiscount_psum = $form->format_amount($myconfig, $nodiscount_sum, 2); - $pb =~ s/<%nodiscount_sumcarriedforward%>/$nodiscount_psum/g; - $pb =~ s/<%sumcarriedforward%>/$psum/g; - $pb =~ s/<%lastpage%>/$current_page/g; + while ($pos < length($text)) { + $pos++; - # only "normal" variables are supported here - # (no <%if, no <%foreach, no <%include) + next if (substr($text, $pos - 1, length($self->{tag_start})) ne $self->{tag_start}); - while ($pb =~ /<%(.*?)%>/) { - substr($pb, $-[0], $+[0] - $-[0]) = - $self->format_string($form->{"$1"}->[$i]); - } + my $keyword_pos = $pos - 1 + $tag_start_len; - # page break block is ready to rock - print(OUT $pb); - $current_page++; - $current_line = 1; - } - $current_line += $lines; - $current_row++; - } - $sum += $form->parse_amount($myconfig, $form->{"linetotal"}->[$i]); - $nodiscount_sum += $form->parse_amount($myconfig, $form->{"nodiscount_linetotal"}->[$i]); - - # don't parse par, we need it for each line - $_ = $par; - while (/<%(.*?)%>/) { - substr($_, $-[0], $+[0] - $-[0]) = - $self->format_string($form->{"$1"}->[$i]); - } - print OUT; + if ((substr($text, $keyword_pos, 2) eq 'if') || (substr($text, $keyword_pos, 3) eq 'for')) { + $depth++; + + } elsif ((substr($text, $keyword_pos, 4) eq 'else') && (1 == $depth)) { + if (!$var) { + $self->{"error"} = + "$self->{tag_start}else$self->{tag_end} outside of " + . "$self->{tag_start}if$self->{tag_end} / " + . "$self->{tag_start}ifnot$self->{tag_end}."; + return undef; + } + + my $block = substr($text, 0, $pos - 1); + substr($text, 0, $pos - 1) = ""; + $text =~ s!^$self->{tag_start_qm}.+?$self->{tag_end_qm}!!; + $text = $self->{tag_start} . 'if' . ($not ? " " : "not ") . $var . $self->{tag_end} . $text; + + return ($block, $text); + + } elsif (substr($text, $keyword_pos, 3) eq 'end') { + $depth--; + if ($depth == 0) { + my $block = substr($text, 0, $pos - 1); + substr($text, 0, $pos - 1) = ""; + $text =~ s!^$self->{tag_start_qm}.+?$self->{tag_end_qm}!!; + + return ($block, $text); } - next; } + } - # if not comes before if! - if (/\s*<%if not /) { + return undef; +} - # check if it is not set and display - chop; - s/\s*<%if not (.+?)%>/$1/; +sub parse_block { + $main::lxdebug->enter_sub(); - unless ($form->{$_}) { - while ($_ = shift) { - last if (/\s*<%end /); + my ($self, $contents, @indices) = @_; - # store line in $par - $par .= $_; - } + my $new_contents = ""; - $_ = $par; + while ($contents ne "") { + my $pos_if = index($contents, $self->{tag_start} . 'if'); + my $pos_foreach = index($contents, $self->{tag_start} . 'foreach'); - } else { - while ($_ = shift) { - last if (/\s*<%end /); - } - next; - } + if ((-1 == $pos_if) && (-1 == $pos_foreach)) { + $new_contents .= $self->substitute_vars($contents, @indices); + last; } - if (/\s*<%if /) { + if ((-1 == $pos_if) || ((-1 != $pos_foreach) && ($pos_if > $pos_foreach))) { + $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_foreach), @indices); + substr($contents, 0, $pos_foreach) = ""; - # check if it is set and display - chop; - s/\s*<%if (.+?)%>/$1/; + if ($contents !~ m|^$self->{tag_start_qm}foreach (.+?)$self->{tag_end_qm}|) { + $self->{"error"} = "Malformed $self->{tag_start}foreach$self->{tag_end}."; + $main::lxdebug->leave_sub(); + return undef; + } - if ($form->{$_}) { - while ($_ = shift) { - last if (/\s*<%end /); + my $var = $1; - # store line in $par - $par .= $_; - } + substr($contents, 0, length($&)) = ""; - $_ = $par; + my $block; + ($block, $contents) = $self->find_end($contents); + if (!$block) { + $self->{"error"} = "Unclosed $self->{tag_start}foreach$self->{tag_end}." unless ($self->{"error"}); + $main::lxdebug->leave_sub(); + return undef; + } - } else { - while ($_ = shift) { - last if (/\s*<%end /); + my $new_text = $self->parse_foreach($var, $block, "", "", @indices); + if (!defined($new_text)) { + $main::lxdebug->leave_sub(); + return undef; + } + $new_contents .= $new_text; + + } else { + $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_if), @indices); + substr($contents, 0, $pos_if) = ""; + + if ($contents !~ m|^$self->{tag_start_qm}if\s*(not)?\s+(.*?)$self->{tag_end_qm}|) { + $self->{"error"} = "Malformed $self->{tag_start}if$self->{tag_end}."; + $main::lxdebug->leave_sub(); + return undef; + } + + my ($not, $var) = ($1, $2); + + substr($contents, 0, length($&)) = ""; + + ($block, $contents) = $self->find_end($contents, 0, $var, $not); + if (!$block) { + $self->{"error"} = "Unclosed $self->{tag_start}if${not}$self->{tag_end}." unless ($self->{"error"}); + $main::lxdebug->leave_sub(); + return undef; + } + + my $value = $self->{"form"}->{$var}; + for (my $i = 0; $i < scalar(@indices); $i++) { + last unless (ref($value) eq "ARRAY"); + $value = $value->[$indices[$i]]; + } + + if (($not && !$value) || (!$not && $value)) { + my $new_text = $self->parse_block($block, @indices); + if (!defined($new_text)) { + $main::lxdebug->leave_sub(); + return undef; } - next; + $new_contents .= $new_text; } } + } - # check for <%include filename%> - if (/\s*<%include /) { + $main::lxdebug->leave_sub(); - # get the filename - chomp $var; - $var =~ s/\s*<%include (.+?)%>/$1/; + return $new_contents; +} - # mangle filename - $var =~ s/(\/|\.\.)//g; +sub parse_first_line { + my $self = shift; + my $line = shift || ""; - # prevent the infinite loop! - next if ($form->{"$var"}); + if ($line =~ m/([^\s]+)set-tag-style([^\s]+)/) { + if ($1 eq $2) { + $self->{error} = "The tag start and end markers must not be equal."; + return 0; + } + + $self->set_tag_style($1, $2); + } - open(INC, $form->{templates} . "/$var") - or $form->error($self->cleanup . $form->{templates} . "/$var : $!"); - unshift(@_, ); - close(INC); + return 1; +} - $form->{"$var"} = 1; +sub _parse_config_option { + my $self = shift; + my $line = shift; + $line =~ s/^\s*//; + $line =~ s/\s*$//; + + my ($key, $value) = split m/\s*=\s*/, $line, 2; + + if ($key eq 'tag-style') { + $self->set_tag_style(split(m/\s+/, $value, 2)); + } +} + +sub _parse_config_lines { + my $self = shift; + my $lines = shift; + + my ($comment_start, $comment_end) = ("", ""); + + if (ref $self eq 'LaTeXTemplate') { + $comment_start = '\s*%'; + } elsif (ref $self eq 'HTMLTemplate') { + $comment_start = '\s*