File Coverage

blib/lib/ZM/Template.pm
Criterion Covered Total %
statement 15 315 4.7
branch 0 76 0.0
condition 0 21 0.0
subroutine 5 35 14.2
pod 0 23 0.0
total 20 470 4.2


line stmt bran cond sub pod time code
1             # Zet Maximum template parser
2             #
3             # 2002-2009
4             # Version 0.7.2 production
5             # Author Maxim Kashliak (max@zmaximum.ru)
6             # Aleksey V. Ivanov (avimail@zmaximum.ru)
7             #
8             # For latest releases, documentation, faqs, etc see the homepage at
9             # http://perl.zmaximum.ru
10             #
11             #
12             package ZM::Template;
13              
14 1     1   746 use strict;
  1         2  
  1         46  
15 1     1   7 use vars qw($AUTOLOAD);
  1         1  
  1         64  
16 1     1   5 use Carp;
  1         6  
  1         99  
17 1     1   1168 use MIME::Base64;
  1         1400  
  1         76  
18 1     1   7 no strict 'refs';
  1         2  
  1         4493  
19              
20             $ZM::Template::VERSION = '0.7.2';
21              
22             sub new()
23             {
24 0     0 0   my $class = shift;
25 0           my %baseHtml=@_;
26 0 0 0       $baseHtml{tag}="__" if (!defined $baseHtml{tag} || $baseHtml{tag} eq "");
27 0           $baseHtml{_loops_filled}=0;
28 0           bless \%baseHtml, $class;
29 0           return \%baseHtml
30             }
31              
32             sub src()
33             {
34 0 0   0 0   if ($#_ !=1)
35             {
36 0           die_msg("Error! template function requires a single parameter\n");
37             }
38              
39 0           my $self = shift;
40 0           my $src = shift;
41              
42 0           my $suxx=$/;
43 0           undef $/;
44 0 0         open(HTML, "<$src") || die_msg("Cannot open html template file!
($src)");
45 0           my $tmplString = ;
46 0           close HTML;
47 0           $/=$suxx;
48             # обработка SSI деректив внутри темплейта
49 0           eval('require ZM::SSI;');
50 0 0         $tmplString = ZM::SSI::parse($tmplString) unless $@;
51             # задаем папку для хранения кеша, если она не была указана при создании объекта
52 0 0         unless (defined $self->{cacheDIR})
53             {
54 0           $src=~s/\/.*?$//;
55 0           $self->{cacheDIR}=$src.'/cache';
56             }
57            
58 0           $self->srcString($tmplString);
59             }
60              
61             sub srcString
62             {
63 0     0 0   my $self = shift;
64 0           my $str = shift;
65 0           $self->{html}=$str;
66 0           _parse_tokens($self,$str);
67             }
68             sub srcStringSSI
69             {
70 0     0 0   my $self = shift;
71 0           my $str = shift;
72 0           eval('require ZM::SSI;');
73 0 0         $str = ZM::SSI::parse($str) unless $@;
74 0           $self->{html}=$str;
75 0           _parse_tokens($self,$str);
76             }
77              
78              
79             sub listAllTokens
80             {
81 0     0 0   my $self=shift;
82 0           return(keys %{$self->{tokens}});
  0            
83             }
84              
85             sub _parse_tokens
86             {
87 0     0     my $self=shift;
88 0           my $htmlString=shift;
89 0           my ($padding, $token, $remainder);
90              
91             # while ($htmlString =~ /.*?(($self->{tag}x_.+?$self->{tag}\n)|($self->{tag}([^_]).*?$self->{tag}))/sg)
92             # while ($htmlString =~ /.*?$self->{tag}((x_)?.+?)$self->{tag}/sg) #orig
93 0           while ($htmlString =~ /.*?$self->{tag}([\da-zA-Z]([\_\-]*[\da-zA-Z])*?)$self->{tag}/sg)
94             {
95 0           $token = $1;
96 0           $token =~ s/\n$//g; # chomp $token (chomp bust as $/ undef'd)
97             # $token =~ s/(^$self->{tag}|$self->{tag}$)//g;
98 0           $self->{tokens}{$token}=1;
99             }
100             }
101              
102             sub AUTOLOAD
103             {
104 0     0     my $token = $AUTOLOAD;
105 0           my ($self, $value, $block) = @_;
106 0           $token =~ s/.*:://;
107             # здесь надо проверить не выглядит ли наша замена как токен, иначе она сотрется в последствии
108             #while ($value =~ /.*?$self->{tag}([^_].*?)$self->{tag}/s)
109             #{
110             # my $vtoken = $1;
111             # $vtoken =~ s/\n$//g;
112             # $nottokens{$vtoken}=1;
113             #}
114 0 0         if (defined $self->{tokens}{$token})
115             {
116 0 0 0       if (defined $block and $block ne "")
117             {
118 0           my $bl_new=$block."_new";
119 0           $bl_new=~s/^x_//; #надо обойтись без регекспа
120 0           my $loop;
121 0 0         if (defined $self->{loops}{$bl_new})
122             {
123 0           my $flag=0;
124 0           foreach $loop(@{$self->{loops}{$bl_new}})
  0            
125             {
126 0 0         if(strstr($loop,"$self->{tag}".$token."$self->{tag}"))
127             {
128 0           $flag++;
129 0           last;
130             }
131             }
132 0 0         $self->_post_loop($block) unless($flag);
133             }
134 0           $self->_set_loop($token,$value,$block);
135             }
136             else
137             {
138 0           $self->{html}=$self->set_to_str($token,$value,$self->{html});
139             }
140             }
141             }
142              
143             sub setif
144             {
145 0     0 0   my $self = shift;
146 0           my $token = shift;
147 0           my $loop = shift;
148            
149 0 0 0       if(!defined $loop or $loop eq "")
150             {
151 0           $self->{ifs}->{$token}=1;
152             }
153             else
154             {
155 0           $self->{ifs_loop}->{$loop}->{$token}=1;
156             }
157             }
158              
159             sub delif
160             {
161 0     0 0   my $self = shift;
162 0           my $token = shift;
163 0           my $loop = shift;
164            
165 0 0 0       if(!defined $loop or $loop eq "")
166             {
167 0           delete $self->{ifs}->{$token};
168             }
169             else
170             {
171 0           delete $self->{ifs_loop}->{$loop}->{$token};
172             }
173             }
174              
175             sub fromfile
176             {
177 0     0 0   my $self = shift;
178 0           my $token = shift;
179 0           my $file = shift;
180 0           my $block = shift;
181            
182 0 0         open (my $f,"<$file") or return;
183 0           my $suxx=$/;
184 0           undef $/;
185 0           my $filecontent=<$f>;
186 0           $/=$suxx;
187 0           close($f);
188 0           $self->$token($filecontent,$block);
189             }
190              
191             sub _set_loop
192             {
193 0     0     my $self=shift;
194 0           my $token=shift;
195 0           my $value=shift;
196 0           my $block=shift;
197              
198 0           $block=~s/^x_//;
199 0           my ($loop, $loop_name, $loop_begin, $num, $loop2, $loop3, $loop_after, $loops_ref);
200 0           my @loops=();
201 0 0         if(defined $self->{loops}{$block."_new"})
202             {
203             # если уже выставлялись токены в этом цикле
204 0           $loops_ref=$self->{loops}{$block."_new"};
205             }
206             else
207             {
208             # если еще не вставлялись токены в этом цикле,
209             # то выдираем тело цикла из всего шаблона
210 0           $loop_name="$self->{tag}x_".$block."$self->{tag}";
211             # мы ищем все циклы с таким именем во всем документе
212 0           $loop_after=$self->{html};
213 0           while($loop_begin=strstr($loop_after,$loop_name))
214             {
215 0           $loop_begin=substr($loop_begin,length($loop_name));
216 0           ($loop,$loop_after)=str_split($loop_begin,$loop_name);
217 0           push(@{$loops_ref},$loop);
  0            
218             }
219             }
220 0           my $loop_number=0;
221 0           foreach $loop(@$loops_ref)
222             {
223 0 0         if(strstr($loop,"$self->{tag}z_".$block."$self->{tag}"))
224             {
225 0           $self->{strnum}{$block}++;
226 0           $num=$self->{strnum}{$block};
227 0           $loop2=$loop3=$loop;
228 0   0       while(($num)&&(($loop2=str_before($loop3,"$self->{tag}z_".$block."$self->{tag}")) ne $loop3))
229             {
230 0           $num--;
231 0           $loop3=str_after($loop3,"$self->{tag}z_".$block."$self->{tag}");
232             }
233 0 0         if($num==0)
    0          
234             {
235 0           $loop=$loop2;
236             #$self->{strnum}{$block}++;
237             }
238             elsif($num>=1)
239             {
240             #$loop=str_before($loop,"$self->{tag}z_".$block."$self->{tag}");
241 0           $loop=$loop3;
242 0           $self->{strcount}{$block}=$self->{strnum}{$block}-1; #запомним номер последнего блока перед переходом на первый
243 0           $self->{strnum}{$block}=0;
244             }
245             }
246 0           $self->{loops}{$block.'_new'}[$loop_number]=$self->set_to_str($token,$value,$loop);
247 0           $loop_number++;
248             }
249             }
250              
251             sub _post_loop
252             {
253 0     0     my $self=shift;
254 0           my $block=shift;
255              
256 0           $block=~s/^x_//;
257              
258 0           my ($text,$pos, $before_loop, $loop, $loop_name, $after_loop);
259 0           my $loop_number;
260 0 0 0       if($self->{strnum}{$block}>1)
    0          
261             {
262 0           $loop_number=$self->{strnum}{$block}-1;
263             }
264             elsif(defined $self->{strnum}{$block} && $self->{strnum}{$block}==0)
265             {
266 0           $loop_number=$self->{strcount}{$block};
267             }
268             else
269             {
270 0           $loop_number=0;
271             }
272 0           my $i=0;
273 0           foreach $text (@{$self->{loops}{$block."_new"}})
  0            
274             {
275             #Find and post inner loops
276 0           my %found_loops;
277 0           while($pos=strstr($text,"$self->{tag}x_"))
278             {
279 0           $before_loop=substr($text,0,length($text)-length($pos));
280 0           $loop_name=substr($pos,4);
281 0           $loop_name=substr($loop_name,0,length($loop_name)-length(strstr($loop_name,"$self->{tag}")));
282 0           $loop=strstr($text,"$self->{tag}x_".$loop_name."$self->{tag}");
283 0           $after_loop=str_after(str_after($loop,"$self->{tag}x_".$loop_name."$self->{tag}"),"$self->{tag}x_".$loop_name."$self->{tag}");
284 0           $loop=substr($loop,0,length($loop)-length($after_loop));
285 0 0         $self->_post_loop($loop_name) if (defined $self->{loops}{$loop_name.'_new'});
286 0           $found_loops{$loop_name}++;
287 0           $text=$before_loop.$self->{loops}{$loop_name}[$loop_number].$after_loop;
288             }
289             # delete inner loops
290 0           foreach $loop_name (keys %found_loops)
291             {
292 0           delete $self->{loops}{$loop_name};
293 0           delete $self->{strnum}{$loop_name};
294 0           delete $self->{strcount}{$loop_name};
295             }
296 0           $text=_fill_ifs($self,$text,'x_'.$block);
297             #$self->{loops}{$block."_new"}=$text;
298             #POST
299 0           $self->{loops}{$block}[$i] .= $text;
300 0           $i++;
301             }
302 0           delete $self->{ifs_loop}->{'x_'.$block}; # we must clear IFs for this loop
303 0           delete $self->{loops}{$block.'_new'};
304             }
305              
306             sub set_to_str
307             {
308 0     0 0   my $self=shift;
309 0           my $token=shift;
310 0           my $value=shift;
311 0           my $str=shift;
312              
313 0           my $ret="";
314 0           my $sub_str;
315             my $loop_name;
316 0           while(($sub_str=str_before($str,"$self->{tag}x_")) ne $str)
317             {
318             # получаем имя цикла
319 0           $loop_name=str_before(substr($str,length($sub_str)+4),"$self->{tag}");
320             # заменяем переменную перед циклом, если она там есть
321 0           $ret.=str_replace("$self->{tag}".$token."$self->{tag}",$value,$sub_str);
322 0           $ret.="$self->{tag}x_".$loop_name."$self->{tag}".str_before(substr($str,length($sub_str)+length($loop_name)+6),"$self->{tag}x_".$loop_name."$self->{tag}")."$self->{tag}x_".$loop_name."$self->{tag}";
323 0           $str=str_after(substr($str,length($sub_str)+length($loop_name)+6),"$self->{tag}x_".$loop_name."$self->{tag}");
324             }
325 0           $ret.=str_replace("$self->{tag}".$token."$self->{tag}",$value,$str);
326 0           return($ret);
327             }
328              
329             sub _fill_ifs
330             {
331 0     0     my $self=shift;
332 0           my $text=shift;
333 0           my $l_name=shift;
334 0           my ($pos,$before_loop,$if_name,$loop,$loop_name,$after_loop,$h_ifs);
335 0 0 0       if(!defined $l_name || $l_name eq '')
336             {
337 0           $h_ifs=$self->{ifs};
338             }
339             else
340             {
341             # надо заполнить IF для цикла
342 0           $h_ifs=$self->{ifs_loop}->{$l_name};
343             }
344             # удаляем незаполненные IF и вставляем заполненные в окончательный текст
345 0           while($pos=strstr($text,"$self->{tag}if_"))
346             {
347 0           $before_loop=substr($text,0,length($text)-length($pos));
348 0           $loop_name=substr($pos,5);
349 0           $loop_name=substr($loop_name,0,length($loop_name)-length(strstr($loop_name,"$self->{tag}")));
350 0           $if_name="$self->{tag}if_".$loop_name."$self->{tag}";
351 0           $loop=$pos;
352 0           $after_loop=str_after(str_after($loop,$if_name),$if_name);
353 0           $loop=substr($loop,length($if_name),length($loop)-length($after_loop)-length($if_name)*2);
354 0 0         unless(defined $h_ifs->{$loop_name})
355             {
356             #IF не выставлялся
357 0 0         if($pos=strstr($loop,"$self->{tag}else_".$loop_name))
358             {
359             #else есть и его надо оставить
360 0           $loop=substr($pos,9+length($loop_name));
361             }
362             else
363             {
364             #else нет
365 0           $loop="";
366             }
367             }
368             else
369             {
370             #убираем содержимое ELSE, если таковой есть
371 0 0         if($pos=strstr($loop,"$self->{tag}else_".$loop_name))
372             {
373             #else есть и его надо оставить
374 0           $loop=str_before($loop,$pos);
375             }
376             #undef $h_ifs->{$loop_name} if(!defined $l_name || $l_name eq '')
377            
378             }
379 0           $text=$before_loop.$loop.$after_loop;
380             }
381 0           return($text);
382             }
383              
384             sub _fill_loops
385             {
386 0     0     my $self=shift;
387 0           my $text=shift;
388 0 0         unless($self->{_loops_filled})
389             {
390 0           my ($pos, $before_loop, $loop_name, $loop, $after_loop);
391              
392             # постим те лупы, что запонились, но не заполнились
393 0           foreach(keys %{$self->{loops}})
  0            
394             {
395 0 0         if (($loop_name=str_before($_,"_new")) ne $_)
396             {
397 0 0         $self->_post_loop($loop_name) if($self->{loops}{$_} ne "");
398             }
399             }
400             # удаляем незаполненные лупы и вставляем заполненные в окончательный текст
401 0           while($pos=strstr($text,"$self->{tag}x_"))
402             {
403 0           $before_loop=substr($text,0,length($text)-length($pos));
404 0           $loop_name=substr($pos,4);
405 0           $loop_name=substr($loop_name,0,length($loop_name)-length(strstr($loop_name,"$self->{tag}")));
406             #$loop=strstr($text,"$self->{tag}x_".$loop_name."$self->{tag}");
407 0           $loop=$pos;
408 0           $after_loop=str_after(str_after($loop,"$self->{tag}x_".$loop_name."$self->{tag}"),"$self->{tag}x_".$loop_name."$self->{tag}");
409 0           $loop=substr($loop,0,length($loop)-length($after_loop));
410 0           $text=$before_loop.shift(@{$self->{loops}{$loop_name}}).$after_loop;
  0            
411             }
412 0           my $tag=$self->{tag};
413 0           my $if_name;
414 0           $text=_fill_ifs($self,$text);
415 0           $text=~s/($tag)[\da-zA-Z\-][\w\-]*?($tag)//g;
416 0           $self->{_loops_filled}=1;
417             }
418 0           return($text);
419             }
420              
421              
422             sub strstr
423             {
424 0     0 0   my $str=shift;
425 0           my $str2=shift;
426 0           my $index=index($str,$str2);
427 0 0         if ($index>-1)
428             {
429 0           $str=substr($str,$index,length($str)-$index);
430 0           return($str);
431             }
432             else
433             {
434 0           return undef;
435             }
436             }
437              
438             sub str_before
439             {
440 0     0 0   my $str=shift;
441 0           my $str2=shift;
442 0           my $indx=index($str,$str2);
443 0 0         if($indx!=-1)
444             {
445 0           $str=substr($str,0,$indx);
446             }
447 0           return $str;
448             }
449             sub str_split
450             {
451 0     0 0   my $str=shift;
452 0           my $str2=shift;
453 0           my $indx=index($str,$str2);
454 0 0         if($indx!=-1)
455             {
456 0           my $lstr2=length($str2);
457 0           $str2=substr($str,$indx+$lstr2,length($str)-$indx-$lstr2);
458 0           $str=substr($str,0,$indx);
459             }
460             else
461             {
462 0           $str2='';
463             }
464 0           return ($str,$str2);
465             }
466             sub str_3_split
467             {
468 0     0 0   my $str=shift;
469 0           my $str1=shift;
470 0           my $str2=shift;
471 0           ($str1,$str)=str_split($str,$str1);
472 0 0         if($str ne '')
473             {
474 0           ($str2,$str)=str_split($str,$str2);
475             }
476             else
477             {
478 0           $str2='';
479             }
480 0           return($str1,$str2,$str);
481             }
482             sub str_after
483             {
484 0     0 0   my $str=shift;
485 0           my $str2=shift;
486 0           my $indx=index($str,$str2);
487 0 0         if($indx!=-1)
488             {
489 0           $str=substr($str,$indx+length($str2),length($str)-$indx-length($str2));
490             }
491 0           return($str);
492             }
493              
494              
495             sub str_between
496             {
497 0     0 0   my $str=shift;
498 0           my $str1=shift;
499 0           my $str2=shift;
500 0           my $ret=str_after($str,$str1);
501 0           return(str_before($ret,$str2));
502             }
503              
504             sub str_replace
505             {
506 0     0 0   my $str=shift;
507 0           my $str1=shift;
508 0           my $str2=shift;
509            
510 0           $str2=~s/$str/$str1/g;
511 0           return($str2);
512             }
513              
514             sub output()
515             {
516 0     0 0   my $self = shift;
517 0           my $hdr;
518              
519 0           foreach $hdr (@_)
520             {
521 0           print "$hdr\n";
522             }
523              
524 0           print "\n";
525             # $self->{html}=$self->_fill_loops($self->{html});
526 0           print $self->htmlString();
527             }
528              
529             sub setCache
530             {
531 0     0 0   my $self = shift;
532 0           my $cacheKey = shift;
533 0 0         return 0 unless (defined $self->{cacheDIR});
534 0 0         mkdir($self->{cacheDIR}) unless(-d $self->{cacheDIR});
535 0           my $encodedKey=encode_base64($cacheKey);
536             # chomp($encodedKey);
537 0           $encodedKey=~s/[\n\s]//sg;
538 0           open(my $f,'>'.$self->{cacheDIR}.'/'.$encodedKey);
539 0           print $f $self->htmlString();
540 0           close($f);
541 0           return 1;
542             }
543              
544             sub getCache
545             {
546 0     0 0   my $self = shift;
547 0           my $cacheKey = shift;
548 0 0         return 0 unless (defined $self->{cacheDIR});
549 0           my $encodedKey=encode_base64($cacheKey);
550             # chomp($encodedKey);
551 0           $encodedKey=~s/[\n\s]//sg;
552 0           my $cacheFile = $self->{cacheDIR}.'/'.$encodedKey;
553 0 0         return 0 unless(-f $cacheFile);
554 0           my $suxx=$/;
555 0           undef $/;
556 0           open(my $f, $cacheFile);
557 0           $self->{html}=<$f>;
558 0           close($f);
559 0           $/=$suxx;
560 0           $self->{_loops_filled}=1;
561 0           return 1;
562             }
563              
564             sub rmCache
565             {
566 0     0 0   my $self = shift;
567 0           my $cacheKey = shift;
568 0           my $encodedKey=encode_base64($cacheKey);
569 0           $encodedKey=~s/[\n\s]//sg;
570             # chomp($encodedKey);
571 0           return unlink($self->{cacheDIR}.'/'.$encodedKey);
572             }
573              
574             sub clearCache
575             {
576 0     0 0   my $self = shift;
577 0 0         return 0 unless (defined $self->{cacheDIR});
578 0           unlink (glob($self->{cacheDIR}.'/*'));
579 0           return 1;
580             }
581              
582             sub htmlString()
583             {
584 0     0 0   my $self = shift;
585 0           $self->{html}=$self->_fill_loops($self->{html});
586 0           return $self->{html};
587             }
588              
589             sub DESTROY()
590 0     0     {
591             }
592              
593             sub die_msg
594             {
595 0     0 0   my $msg = shift;
596 0           print "Content-type: text/html\n\n";
597 0           print <
598            
599            
600            
601             ZM::Template Error: $msg
602            
603            
604             EOF
605 0           exit;
606             }
607              
608             1;
609              
610             __END__