File Coverage

blib/lib/Dotiac/DTL/Core.pm
Criterion Covered Total %
statement 233 302 77.1
branch 80 156 51.2
condition 20 48 41.6
subroutine 21 25 84.0
pod 17 17 100.0
total 371 548 67.7


line stmt bran cond sub pod time code
1             ###############################################################################
2             #Core.pm
3             #Last Change: 2009-01-19
4             #Copyright (c) 2009 Marc-Seabstian "Maluku" Lucksch
5             #Version 0.8
6             ####################
7             #This file is part of the Dotiac::DTL project.
8             #http://search.cpan.org/perldoc?Dotiac::DTL
9             #
10             #Core.pm is published under the terms of the MIT license, which basically
11             #means "Do with it whatever you want". For more information, see the
12             #license.txt file that should be enclosed with libsofu distributions. A copy of
13             #the license is (at the time of writing) also available at
14             #http://www.opensource.org/licenses/mit-license.php .
15             ###############################################################################
16            
17             package Dotiac::DTL::Core;
18            
19             our $VERSION = 0.8;
20            
21             package Dotiac::DTL;
22             require Dotiac::DTL::Value;
23             require Dotiac::DTL::Template;
24             require Dotiac::DTL::Filter;
25             require Dotiac::DTL::Compiled;
26            
27 12     12   74 use strict;
  12         22  
  12         460  
28 12     12   70 use warnings;
  12         24  
  12         410  
29 12     12   64 use Scalar::Util qw/reftype blessed/;
  12         19  
  12         1040  
30 12     12   61 use Carp;
  12         18  
  12         37394  
31             require File::Spec;
32             require File::Basename;
33            
34             #These go into the context.
35             our $TEMPLATE_STRING_IF_INVALID=""; #If there was no parameter found
36             our $ALLOW_METHOD_CALLS=1;
37             our $ALLOWED_INCLUDE_ROOTS=0; #Allows the ssi tag
38             our $AUTOESCAPING=1; #Default auto escape or not
39             our $DATETIME_FORMAT='N j, Y, P';
40             our $DATE_FORMAT='N j, Y';
41             our $TIME_FORMAT='P';
42             our @TEMPLATE_DIRS=(); #Only used by Template();
43             our $Max_Depth=3;
44             our $CURRENTDIR="";
45             our $PARSER="Dotiac::DTL::Parser";
46            
47             #This has to change someday. not global
48             our %blocks; #this needs to be global, sadly.
49             our %cycle; #Also needs to be global.
50             our %globals; #Well we already have other globals, this saves me the init() trough the whole tree/list.
51            
52            
53             our %included;
54             our %params;
55            
56            
57             # Template cache, needs to be global
58             my %cache;
59            
60             sub new {
61 2     2 1 41 my $class=shift;
62 2         3 my $data=shift;
63 2         4 my $t="";
64 2         5 %params=();
65 2 50       11 if (ref $data eq "SCALAR") {
    50          
66 0         0 die "Dotiac::DTL::Reduced can only work with compiled templates, use Dotiac::DTL for the full interface";
67             }
68             elsif (not ref $data) {
69 2         4 $t=$data;
70 2         64 my @f = File::Basename::fileparse($data);
71 2         5 $Dotiac::DTL::currentdir=$f[1];
72 2 100       36 if (-e "$data.pm") {
73 1 0 33     10 if ($cache{"$data.pm"} and exists $cache{"$data.pm"}->{changetime} and $cache{"$data.pm"}->{changetime} < (stat("$data.pm"))[9]) {
      33        
74 0         0 carp "Foo";
75 0         0 delete $cache{"$data.pm"};
76 0         0 delete $INC{"$data.pm"};
77             }
78 1 50       11 if (-e $data) {
79 0 0       0 if ((stat("$data.pm"))[9] >= (stat("$data"))[9]) {
80             eval {
81 0 0       0 $cache{"$data.pm"}={
82             template=>Dotiac::DTLCompiled->new("Dotiac::DTL::Compiled::".require "$data.pm"),
83             currentdir=>$Dotiac::DTL::currentdir,
84             params=>{%Dotiac::DTL::params},
85             parser=>$Dotiac::DTL::PARSER,
86             changetime=>(stat("$data.pm"))[9]
87             } if not $cache{"$data.pm"};# or (exists $cache{"$data.pm"}->{changetime} and $cache{"$data.pm"}->{changetime} > -M "$data.pm"); #Can't do it, Require won't return the filename a second time, has to be solved differently by manually modifing %INC
88 0         0 $t="$data.pm";
89 0         0 1;
90 0 0       0 } or do {
91 0         0 croak "Error while getting compiled template $data.pm and can't use $data, because this is Reduced:\n $@\n.";
92 0         0 undef $@;
93             };
94             }
95             else {
96 0         0 carp "$data seem to outdate $data.pm, but Dotiac::DTL::Reduced can only work with compiled templates, use Dotiac::DTL to recompile";
97             eval {
98 0 0       0 $cache{"$data.pm"}={
99             template=>Dotiac::DTLCompiled->new("Dotiac::DTL::Compiled::".require "$data.pm"),
100             currentdir=>$Dotiac::DTL::currentdir,
101             params=>{%Dotiac::DTL::params},
102             parser=>$Dotiac::DTL::PARSER,
103             changetime=>(stat("$data.pm"))[9]
104             } if not $cache{"$data.pm"};# or (exists $cache{"$data.pm"}->{changetime} and $cache{"$data.pm"}->{changetime} > -M "$data.pm");
105 0         0 $t="$data.pm";
106 0         0 1;
107 0 0       0 } or do {
108 0         0 croak "Error while getting compiled template $data.pm and can't use $data, because this is Reduced:\n $@\n.";
109 0         0 undef $@;
110             };
111             }
112             }
113             else {
114             eval {
115 1 50       708 $cache{"$data.pm"}={
116             template=>Dotiac::DTL::Compiled->new("Dotiac::DTL::Compiled::".require "$data.pm"),
117             currentdir=>$Dotiac::DTL::currentdir,
118             params=>{%Dotiac::DTL::params},
119             parser=>$Dotiac::DTL::PARSER,
120             changetime=>(stat("$data.pm"))[9]
121             } if not $cache{"$data.pm"};# or (exists $cache{"$data.pm"}->{changetime} and $cache{"$data.pm"}->{changetime} > -M "$data.pm");
122 1         6 $t="$data.pm";
123 1         5 1;
124 1 50       3 } or do {
125 0         0 croak "Error while getting compiled template $data.pm and $data is gone:\n $@\n.";
126 0         0 undef $@;
127             };
128             }
129             }
130 2 100       8 unless ($cache{$t}) {
131 1         227 croak "Dotiac::DTL::Reduced can only work with compiled templates, use Dotiac::DTL for the full interface";
132             }
133             }
134             else {
135 0         0 die "Can't work with $data!";
136             }
137             #$self->{data}=$data;
138 1         5 Dotiac::DTL::Addon::restore();
139 1 50       4 if ($cache{$t}) {
140 1         13 return "Dotiac::DTL::Template"->new($cache{$t}->{template},$cache{$t}->{currentdir},$cache{$t}->{parser},$cache{$t}->{params});
141             }
142             else {
143 0           croak "Dotiac::DTL::Reduced can only work with compiled templates, use Dotiac::DTL for the full interface";
144             }
145             }
146            
147             our $currentdir="";
148            
149             sub safenew {
150 30     30 1 13137 my $class=shift;
151 30         52 my $file=shift;
152 30 50 66     114 unless ($ALLOWED_INCLUDE_ROOTS and int($ALLOWED_INCLUDE_ROOTS) > 2) {
153 30         87 $file=~s/^[\\\/]//g;
154 30         54 $file=~s/^\w+\://g; #Windows GRR
155 30         173 1 while $file=~s/^\.\.[\\\/]//g;
156             }
157 30 100 66     639 unless ( -e $file or -e "$file.pm") {
158 2         38 my $rfile=File::Spec->catfile(".",$currentdir,$file);
159 2 50 33     56 return Dotiac::DTL->new($rfile) if -e $rfile or -e "$rfile.pm";
160             }
161 28         47 my $p=$Dotiac::DTL::PARSER;
162 28         140 my $r=Dotiac::DTL->new($file);
163 28         58 $Dotiac::DTL::PARSER=$p;
164 28         79 return $r;
165             }
166            
167             sub compiled {
168 1     1 1 711 my $class=shift;
169 1         3 my $name=shift;
170 1         2 my $f;
171 1         3 $Dotiac::DTL::currentdir=$Dotiac::DTL::CURRENTDIR;
172 1         3 %params=();
173             eval {
174 1         7 $f=Dotiac::DTL::Compiled->new($name);
175 1         6 1;
176 1 50       3 } or do {
177 0         0 croak "Error while getting compiled template from $name\n $@\n.";
178 0         0 undef $@;
179             };
180 1         2 undef $@;
181 1         7 return "Dotiac::DTL::Template"->new($f,$Dotiac::DTL::CURRENTDIR);
182             }
183            
184            
185            
186             sub urlencode {
187 0     0 1 0 my $val=shift;
188 0   0     0 $val = eval { pack("C*", unpack("U0C*", $val))} || pack("C*", unpack("C*", $val));
189 0         0 $val=~s/([^a-zA-Z0-9_.~-])/uc sprintf("%%%02x",ord($1))/eg;
  0         0  
190 0         0 return $val;
191             }
192            
193             sub escap { #Escape is used too much these days.
194 309     309 1 538 my $string=shift;
195 309         423 $string=~s/\\n/\n/g;
196 309         376 $string=~s/\\t/\t/g;
197 309         370 $string=~s/\\r/\r/g;
198 309         328 $string=~s/\\b/\b/g;
199 309         380 $string=~s/\\f/\f/g;
200 309         338 $string=~s/\\x([\dA-Fa-f]{2})/chr(hex($1))/eg;
  1         6  
201 309         344 $string=~s/\\u([\dA-Fa-f]{4})/chr(hex($1))/eg;
  3         14  
202 309         338 $string=~s/\\U([\dA-Fa-f]{8})/chr(hex($1))/eg;
  0         0  
203 309         392 $string=~s/\\(["'{}])/$1/g;
204             #$string=~s/\\([^\\])/die/eg;
205 309         350 $string=~s/\\\\/\\/g;
206             #TODO more pyhton escape seq.
207 309         618 $string=~s/([\|\s\,\"\'\`\%\:;=])/sprintf("%%%02X",ord($1))/eg;
  151         609  
208 309         848 return "`$string`";
209             }
210            
211             sub descap {
212 1204     1204 1 2442 my $string=shift;
213 1204         2258 $string=~s/%([\da-fA-F]{2})/chr(hex($1))/eg;
  502         1643  
214 1204         4904 return $string;
215             }
216            
217             sub get_variables {
218 412     412 1 7460 my $x=shift;
219 412 100 66     2042 $x="" if not defined $x or ref $x;
220 412         2701 while ($x=~m/[^\"\']*([\"\'])/g) {
221 302         490 my $opos=pos($x);
222 302 100       720 if ($1 eq '"') {
223 298         1202 $x=~m/((?>(?:(?>[^"\\]+)|\\.)*))"/g;
224 298 50       598 die "Syntax error in $1..$1 of $x" unless pos($x);
225 298         529 my $replace=escap($1);
226 298         996 substr($x,$opos-1,pos($x)+1-$opos)=$replace;
227 298         708 pos($x)=$opos+length($replace);
228             }
229             else {
230 4         24 $x=~m/((?>(?:(?>[^'\\]+)|\\.)*))'/g;
231 4 50       14 die "Syntax error in $1..$1 of $x" unless pos($x);
232 4         11 my $replace=escap($1);
233 4         17 substr($x,$opos-1,pos($x)+1-$opos)=$replace;
234 4         50 pos($x)=$opos+length($replace);
235             }
236 302 50       1742 die "Syntax error in $1..$1 of $x" unless pos($x);
237             }
238             #warn "var::$x";
239 412 100       1054 if (@_) {
240 71         104 my %words;
241 71         271 @words{@_}=(1) x scalar @_;
242 71         101 my %ret;
243 71         212 my $keywords = "(?:^|\\s+)".join ("(?:\\s+|\$)|(?:^|\\s+)",@_)."(?:\\s+|\$)";
244             #print STDERR "@_: $keywords\n";
245 71         1562 my @l = split /($keywords)/,$x;
246             #print STDERR join(", ",@l)."\n";
247 71         204 unshift @l,"";
248 71         231 while (defined(my $k=shift @l)) {
249 136         359 $k=~s/^\s+//g;
250 136         276 $k=~s/\s+$//g;
251 136 100       267 if (@l) {
252 124         185 my $next=$l[0];
253 124         268 $next=~s/^\s+//g;
254 124         213 $next=~s/\s+$//g;
255 124 100       304 if ($words{$next}) {
256 4         18 $ret{$k}=[];
257             }
258             else {
259 120         306 my @a=split /\s+/,shift(@l);
260 120         346 $ret{$k}=[@a];
261 120         219 foreach my $a (@a) {
262 138         672 $Dotiac::DTL::params{$a}++;
263             }
264             }
265             }
266             else {
267 12         86 $ret{$k}=[];
268             }
269             }
270 71         554 return %ret;
271             }
272 341         977 my @a= split /\s+/,$x;
273 341         642 foreach my $a (@a) {
274 407         1331 $Dotiac::DTL::params{$a}++;
275             }
276 341         1356 return @a;
277             }
278            
279             sub Escape {
280 8     8 1 17 my $var=shift;
281 8 100       47 return Dotiac::DTL::Value->escape($var)->string() if $_[0];
282 4         18 return $var;
283             }
284            
285             sub Conditional {
286 0     0 1 0 my $var=shift;
287 0 0       0 return "" unless $var;
288 0 0       0 return $var unless ref $var;
289 0 0 0     0 return $var->count() if Scalar::Util::blessed($var) and $var->can("count");
290 0 0       0 return 1 if Scalar::Util::blessed($var);
291 0 0       0 return scalar @{$var} if ref $var eq "ARRAY";
  0         0  
292 0 0       0 return scalar keys %{$var} if ref $var eq "HASH";
  0         0  
293 0         0 return 1;
294             }
295            
296             sub apply_filters {
297 1368     1368 1 2921 my $value=shift;
298 1368         1404 my $vars=shift;
299 1368         1551 my $escape=shift;
300             #$escape=0 if $STRING_IS_LITERAL; #TODO
301             #$VARIABLE_IS_SAFE=!$escape;
302 1368 100 66     9272 unless (Scalar::Util::blessed($value) and $value->isa("Dotiac::DTL::Value")) {
303 12         55 $value=Dotiac::DTL::Value->new($value,!$escape);
304             }
305 1368         2810 foreach my $f (@_) {
306 936         2553 my ($filter,$param)=split /:/,$f,2;
307 936         1458 $filter=lc $filter;
308 936         1074 eval {
309 12     12   93 no strict "refs"; #I hate to do this, does anyone know a better one without eval?
  12         26  
  12         8005  
310 936 100       4051 $value="Dotiac::DTL::Filter::$filter"->($value,defined $param?(map {devar_var($_,$vars,0)} split /[,;]/,$param):());
  536         895  
311             };
312 936 50       2354 if ($@) {
313 0         0 die "Filter '$filter' couldn't be found or an error occoured. The filter has to be in the Dotiac::DTL::Filter namespace\n$@";
314             }
315 936 50 33     6859 die "Filter Error: $filter did not return a Dotiac::DTL::Value" unless Scalar::Util::blessed($value) and $value->isa("Dotiac::DTL::Value");
316             }
317 1368         4976 return $value;
318             }
319            
320             sub devar {
321 393     393 1 3661 my $name=shift;
322 393 50       825 return "" unless defined $name;
323 393         1211 my @data= split/\|/,$name;
324 393         666 $name=shift @data;
325 393         574 my $param=shift;
326 393         537 my $escape=shift;
327 393         1088 my $var=devar_var($name,$param,$escape,@_);
328 393 100       1155 unless (@data) {
329 365         1083 return $var->string();
330             }
331 28         68 $var=apply_filters($var,$param,$escape,@data);
332 28         94 return $var->string();
333            
334             }
335            
336             sub devar_nodefault {
337 0     0 1 0 my $name=shift;
338 0 0       0 return "" unless defined $name;
339 0         0 my @data= split/\|/,$name;
340 0         0 $name=shift @data;
341 0         0 my $param=shift;
342 0         0 my $escape=shift;
343 0         0 my $var=devar_var($name,$param,$escape,@_);
344 0 0       0 unless (@data) {
345 0         0 return $var->stringnodefault();
346             }
347 0         0 $var=apply_filters($var,$param,$escape,@data);
348 0         0 return $var->stringnodefault();
349            
350             }
351            
352             sub devar_raw {
353 1194     1194 1 11514 my $name=shift;
354 1194 50       2576 return "" unless defined $name;
355 1194         3447 my @data= split/\|/,$name;
356 1194         1967 $name=shift @data;
357 1194         1665 my $param=shift;
358 1194         1359 my $escape=shift;
359 1194         2529 my $var=devar_var($name,$param,$escape,@_);
360 1194 100       3478 unless (@data) {
361 1170         4746 return $var;
362             }
363 24         50 $var=apply_filters($var,$param,$escape,@data);
364 24         104 return $var;
365            
366             }
367            
368             sub devar_content {
369 172     172 1 1028 my $name=shift;
370 172 50       455 return "" unless defined $name;
371 172         551 my @data= split/\|/,$name;
372 172         289 $name=shift @data;
373 172         237 my $param=shift;
374 172         215 my $escape=shift;
375 172         425 my $var=devar_var($name,$param,$escape,@_);
376 172 100       499 unless (@data) {
377 12     12   75 use Carp qw/confess/;
  12         27  
  12         2484  
378 132 50       307 confess unless ref $var;
379 132         427 return $var->content();
380             }
381 40         89 $var=apply_filters($var,$param,$escape,@data);
382 40         106 return $var->content();
383            
384             }
385            
386             sub devar_repr {
387 16     16 1 151 my $name=shift;
388 16 50       33 return "" unless defined $name;
389 16         42 my @data= split/\|/,$name;
390 16         24 $name=shift @data;
391 16         25 my $param=shift;
392 16         18 my $escape=shift;
393 16         31 my $var=devar_var($name,$param,$escape,@_);
394 16 50       35 unless (@data) {
395 12     12   69 use Carp qw/confess/;
  12         21  
  12         1977  
396 16 50       41 confess unless ref $var;
397 16         41 return $var->repr();
398             }
399 0         0 $var=apply_filters($var,$param,$escape,@data);
400 0         0 return $var->repr();
401            
402             }
403            
404             sub devar_var {
405 3359     3359 1 5436 my $name=shift;
406 3359         3810 my $n=$name;
407 3359 50       6597 return Dotiac::DTL::Value->safe(undef) unless defined $name;
408 3359         4131 my $param=shift;
409 3359         5099 my $f=substr $name,0,1;
410 3359         4139 my $l=substr $name,-1,1;
411 3359         4315 my $escape=shift;
412             #TODO
413 12     12   82 use Carp;
  12         39  
  12         11993  
414 3359 50       13748 confess $param unless ref $param;
415 3359 50       6597 confess $escape unless defined $escape;
416             #confess @_ unless @_;
417             #TODO
418 3359 50 33     22575 return Dotiac::DTL::Value->safe(substr $name,1,-1) if $f eq "'" and $l eq "'" or $f eq '"' and $l eq '"';
      33        
      33        
419 3359 100 66     12058 return Dotiac::DTL::Value->safe(descap(substr $name,1,-1)) if $f eq "`" and $l eq "`";
420 2274 50 33     5365 if ($name eq "block.super" and $param->{"block.super"}) {
421 0 0       0 return Dotiac::DTL::Value->safe($param->{"block.super"}->string($param,@_)) if Scalar::Util::blessed($param->{"block.super"});
422 0 0       0 return Dotiac::DTL::Value->safe($param->{"block.super"}->($param,@_)) if ref $param->{"block.super"} eq "CODE";
423             }
424 2274 100       9176 return Dotiac::DTL::Value->new($param->{$name},!$escape) if exists $param->{$name};
425 776         2250 my @tree=split/\./,$name;
426 776         1192 $name=shift @tree;
427 776 100       1782 unless (exists $param->{$name}) {
428 136 100       575 return Dotiac::DTL::Value->safe($n) if $n!~/[^\d\-\.\,\e]/;
429 72 100 66     1198 if ($cycle{$name} and $cycle{$name}->[1]) {
430 4 50       12 return Dotiac::DTL::Value->safe("") if $included{"cycle_$name"}++;
431 4         25 my $r=devar_raw($cycle{$name}->[2]->[$cycle{$name}->[0]-1 % $cycle{$name}->[1]],$param,$escape,@_);
432 4         9 $included{"cycle_$name"}=0;
433 4         11 return $r;
434             }
435 68         315 return Dotiac::DTL::Value->safe(undef) ;
436             }
437 640         978 $param=$param->{$name};
438 640         1944 while (defined(my $name = shift @tree)) {
439 684         1556 my $r = reftype $param;
440 684 50       1291 if ($r) {
441 684 100       1238 if ($r eq "HASH") {
    50          
442 628 100       1218 if (not exists $param->{$name}) {
443 16 100       72 return Dotiac::DTL::Value->safe(undef) unless blessed $param;
444             }
445             else {
446 612         1051 $param=$param->{$name};
447 612         2014 next;
448             }
449             }
450             elsif ($r eq "ARRAY") {
451 56 100       133 if ($name=~m/\D/) {
452 8 50       35 return Dotiac::DTL::Value->safe(undef) unless blessed $param;
453             }
454             else {
455 48 50       99 if (not exists $param->[$name]) {
456 0 0       0 return Dotiac::DTL::Value->safe(undef) unless blessed $param;
457             }
458             else {
459 48         68 $param=$param->[$name];
460 48         431 next;
461             }
462             }
463             }
464             }
465 12 50       39 if (blessed $param) {
466 12 50       26 return Dotiac::DTL::Value->safe(undef) unless $ALLOW_METHOD_CALLS;
467 12 50       58 if ($param->can($name)) {
    0          
468 12         39 $param=$param->$name();
469 12         62 next;
470             }
471             elsif ($param->can("__getitem__")) {
472 0         0 my $x;
473 0 0       0 eval {
474 0         0 $x=$param->__getitem__($name);
475 0         0 1;
476             } or return Dotiac::DTL::Value->safe(undef);
477 0 0       0 if (defined $x) {
478 0         0 $param=$x;
479 0         0 next;
480             }
481             }
482 0         0 return Dotiac::DTL::Value->safe(undef);
483             }
484 0 0       0 return Dotiac::DTL::Value->safe($n) if $n!~/[^\d\-\.\,\e]/;
485 0         0 return Dotiac::DTL::Value->safe(undef);
486             }
487 628         2185 return Dotiac::DTL::Value->new($param,!$escape);
488             }
489            
490             sub devar_var_default {
491 0     0 1 0 my $var = devar_var(@_);
492 0         0 return $var->string();
493             }
494            
495             1;
496             __END__