File Coverage

blib/lib/Nile/View.pm
Criterion Covered Total %
statement 9 261 3.4
branch 0 70 0.0
condition 0 29 0.0
subroutine 3 34 8.8
pod 0 27 0.0
total 12 421 2.8


line stmt bran cond sub pod time code
1             # Copyright Infomation
2             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3             # Author : Dr. Ahmed Amin Elsheshtawy, Ph.D.
4             # Website: https://github.com/mewsoft/Nile, http://www.mewsoft.com
5             # Email : mewsoft@cpan.org, support@mewsoft.com
6             # Copyrights (c) 2014-2015 Mewsoft Corp. All rights reserved.
7             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8             package Nile::View;
9              
10             our $VERSION = '0.54';
11             our $AUTHORITY = 'cpan:MEWSOFT';
12              
13             =pod
14              
15             =encoding utf8
16              
17             =head1 NAME
18              
19             Nile::View - The template processing system.
20              
21             =head1 SYNOPSIS
22            
23             # get view home.html in current active theme
24             my $view = $self->app->view("home");
25             # get view home.html in specific arabic theme
26             my $view = $app->view("home", "arabic");
27            
28             # set view variables
29             $view->var(
30             fname => 'Ahmed',
31             lname => 'Elsheshtawy',
32             email => 'ahmed@mewsoft.com',
33             website => 'http://www.mewsoft.com',
34             singleline => 'Single line variable <b>Good</b>',
35             multiline => 'Multi line variable <b>Nice</b>',
36             );
37            
38             # set variable
39             $view->set('email', 'sales@mewsoft.com');
40              
41             # get variable
42             $email = $view->get('email');
43              
44             # automatic getter/setter for variables
45             $view->email('sales@mewsoft.com');
46             $view->website('http://www.mewsoft.com');
47             $email = $view->email;
48             $website = $view->website;
49              
50             # replace marked blocks or iterators
51             $view->block("first", "1st Block New Content ");
52             $view->block("six", "6th Block New Content ");
53            
54             # process variables and blocks and text language variables
55             $view->process;
56              
57             # get the output
58             $content = $view->out;
59              
60             =head1 DESCRIPTION
61              
62             Nile::View - The template processing system.
63              
64             Templates or views are pure html files with special xml tags which can be used to insert
65             the application dynamic output. These xml sepcial tags also can be used to pass
66             parameters to the plugins.
67              
68             Templates also has special comment tags to mark blocks and iterators.
69              
70             Since the framework supports multi lingual, the template can contain the language
71             variables names instead of the actual text surrounded by the Curly braces C<{> and C<}>.
72             Templates also allow embedded Perl code.
73              
74             =head1 TEMPLATE LANGUAGE VARIABLES
75              
76             The template can contain the language variables names instead of the actual text
77             surrounded by the Curly braces C<{> and C<}>.
78              
79             {first_name} <input type="text" name="fname" id="fname" value="" />
80             {last_name} <input type="text" name="lname" id="lname" value="" />
81             {phone} <input type="text" name="phone" id="phone" value="" />
82              
83             The language variables {first_name}, {last_name}, and {phone} will be replaced
84             by their actual text from the loaded langauge file. So after processing the template,
85             this code will look like this:
86              
87             Your first name: <input type="text" name="fname" id="fname" value="" />
88             Your second name: <input type="text" name="lname" id="lname" value="" />
89             Your phone numger: <input type="text" name="phone" id="phone" value="" />
90              
91             If the language variables is not found in the loaded language files, it will not be processed so
92             you can add it to the correct language file.
93              
94             =head1 TEMPLATE VARS TAGS
95              
96             The template xml tag used to insert dynamic output and to pass parameters to the plugin or module has
97             the following format:
98              
99             <vars type="plugin" method="Plugin->method" arg1="value_1" arg2="value_2" argxx="value_xx" />
100             <vars type="module" method="Module->method" arg1="value_1" arg2="value_2" argxx="value_xx" />
101              
102             The xml tag name is fixed `vars`. The attribute C<type> defines the type of the action to be called to handle
103             this tag.
104              
105             The rest of the attributes is optional parameters which will be passed to the action called.
106              
107             The first type or the C<vars> tags is the B<var> in the form C<type="var">.
108             These var tags are used to insert dynamic variables when processing the view:
109              
110             <vars name="website"/>
111             <vars type="var" name='email' />
112              
113             If the vars tag type attribute is empty or omitted, it means this tag is a type C<var>, type="var", so the
114             following are the same:
115              
116             <vars name="email"/>
117             <vars type="var" name='email' />
118              
119             To replace these variables when working with the view, just do it like this:
120            
121             $view = $self->app->view("home");
122             $view->set("email", 'sales@mewsoft.com');
123             $view->set("website", 'http://mewsoft.com');
124              
125             Then when processing the template, these variables will replace the vars xml tags.
126              
127             The second type or the C<vars> tags is the C<plugin> and C<module> in the form C<type="plugin"> and <type="module">.
128              
129             Use these tags to call plugins and modules methods and insert their output to the template. You can also
130             pass any number of optional parameters to the plugin and module method through these tags
131              
132             Example to insert dynamic plugins and modules output when processing the view:
133              
134             <vars type="plugin" method="Date->date" format="%a, %d %b %Y %H:%M:%S" /><br>
135             <vars type="plugin" method="Date->time" format="%A %d, %B %Y %T %p" /><br>
136             <vars type="plugin" method="Date::now" capture="1" format="%B %d, %Y %r" /><br>
137              
138             <vars type="module" method="Home::Home->welcome" message="Welcome back!" />
139              
140             These vars tags of type C<plugin> is used to call the plugins in the C<method> attribute and will pass the
141             parameter C<format> to the plugin method, the vars of the type module will pass the parameter c<message>
142             to the module c<Home::Home> method c<welcome>.
143              
144             If the attribute C<capture> is set to any value, the output of the print statements of the method
145             will be captured and will ignore any returns.
146              
147             The third type or the C<vars> tags is the C<Perl> tags which is used to execute Perl code and capture
148             the output and insert it in the template.
149              
150             Example to insert embedded Perl code output when processing the view:
151              
152             <vars type="perl">print $self->app->VERSION; return;</vars>
153              
154             You can run any Perl code in this tag, here is example to call a system function and display its results:
155              
156             <vars type="perl">system ('dir c:\\*.bat');</vars>
157              
158             You can also include your Perl code in an CDATA like this:
159              
160             <vars type="perl"><![CDATA[
161             say "";
162             say "<br>active language: " . $self->app->var->get("lang");
163             say "<br>active theme: " . $self->app->var->get("theme");
164             say "<br>app path: " . $self->app->var->get("path");
165             say "<br>";
166             ]]></vars>
167              
168             The fourth type or the C<vars> tags is the C<widget> tags which is used to include small templates
169             or widgets in the template. Widgets are small templates and have the same structure as the templates.
170             Widgets are used for the repeated template blocks like dividing your template to sections say header,
171             footer, top_navigation, bottom_navigation, left, right, top_banner, etc. Then you just insert
172             the widget tag inside the templates you want to use these widgets instead of repeating the same code
173             in every template.
174              
175             Widgets templates files should be located in the theme C<widget> folder with the default C<.html> extension.
176              
177             You can pass any number of parameters to the widgets and it will be replaced when processed.
178              
179             Example to insert the widget header in your templates:
180              
181             <vars type="widget" name="header" charset_name="UTF-8" lang_name="en" />
182              
183             If you insert the above tag in your template, it will load the contents of the widget file "header.html" and
184             insert it to the template and will replace the variables passed C<charset_name> and C<lang_name> by their
185             values. Variables can be of different values from call to call based on your need.
186              
187             =head1 TEMPLATE BLOCKS AND ITERATORS
188              
189             Sometimes you need to replace a block of code in your templates by some other contents. For example
190             you may want to show a block of template code if user if logged in and another block of template code
191             if the user is not logged in.
192              
193             In this case use the block comment tag in the following form to handle this:
194              
195             <!--block:user_login-->
196             <span style="color: green;">
197             {user_login_message}
198             </span>
199             <!--endblock-->
200              
201             <!--block:user_logout-->
202             <span style="color: red;">
203             {user_logout_message}
204             </span>
205             <!--endblock-->
206              
207             Inside the plugin code, you can access these blocks simply like this
208            
209             # get the block user_login hash ref
210             $login_block = $view->block("user_login");
211             say $login_block->{content};
212             say $login_block->{match};
213            
214             # set the block user_login new content
215             $view->block("user_login", "Login Block New Content ");
216            
217             # set the block user_logout new content
218             $view->block("user_logout", "Logout Block New Content ");
219              
220             # or
221             if (user_is_loggedin()) {
222             # hide or clear the logout block
223             $view->block("user_logout", "");
224             }
225             else {
226             # hide or clear the login block
227             $view->block("user_login", "");
228             }
229              
230             Calling $view->block() method without any block name will return the entire hash tree of all the template
231             blocks.
232              
233             Blocks can be nested to any levels, for example:
234              
235             html content 1-5 top
236             <!--block:first-->
237             <table border="1" style="color:red;">
238             <tr class="lines">
239             <td align="left" valign="<--valign-->">
240             <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a>
241             <!--hello--> <--again--><!--world-->
242             some html content here 1 top
243             <!--block:second-->
244             some html content here 2 top
245             <!--block:third-->
246             some html content here 3 top
247             <!--block:fourth-->
248             some html content here 4 top
249             <!--block:fifth-->
250             some html content here 5a
251             some html content here 5b
252             <!--endblock-->
253             <!--endblock-->
254             some html content here 3a
255             some html content here 3b
256             <!--endblock-->
257             some html content here 2 bottom
258             </tr>
259             <!--endblock-->
260             some html content here 1 bottom
261             </table>
262             <!--endblock-->
263             html content 1-5 bottom
264              
265             html content 6-8 top
266             <!--block:six-->
267             some html content here 6 top
268             <!--block:seven-->
269             some html content here 7 top
270             <!--block:eight-->
271             some html content here 8a
272             some html content here 8b
273             <!--endblock-->
274             some html content here 7 bottom
275             <!--endblock-->
276             some html content here 6 bottom
277             <!--endblock-->
278             html content 6-8 bottom
279              
280             You can get and access these nested blocks in many ways:
281              
282             $fifth = $view->block("first/second/third/fourth/fifth");
283             $fifth = $view->block->{first}->{second}->{third}->{fourth}->{fifth};
284             $all = $view->block;
285             $fifth = $all->{first}->{second}->{third}->{fourth}->{fifth};
286              
287             Blocks also used for iterators, if you want to build a table or data for example, then you get the bock of the
288             repeated table row and process it then replace the entire data with the block in the view.
289              
290             =cut
291              
292 1     1   5 use Nile::Base;
  1         1  
  1         7  
293 1     1   5353 use Capture::Tiny ();
  1         2  
  1         23  
294 1     1   693 use IO::Compress::Gzip qw(gzip $GzipError);
  1         28420  
  1         4358  
295             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
296             sub AUTOLOAD {
297 0 0   0     my ($self) = shift or return undef; # ignore functions call like Nile::View::xxx();
298 0           my ($class, $method) = our $AUTOLOAD =~ /^(.*)::(\w+)$/;
299            
300 0 0         if ($self->can($method)) {
301 0           return $self->$method(@_);
302             }
303              
304 0 0         if (@_) {
305 0           $self->{var}->{$method} = $_[0];
306             }
307             else {
308 0           return $self->{var}->{$method};
309             }
310             }
311             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
312             sub main { # our sub new{...}, called automatically after the constructor new
313 0     0 0   my ($self, $view, $theme) = @_;
314 0 0         $self->{theme} = $theme if ($theme);
315 0 0         $self->view($view) if ($view);
316             }
317             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
318             =head2 view()
319              
320             my $view = $self->app->view([$view, $theme]);
321              
322             Creates new view object or returns the current view name. The first option is the view name with or without file extension $view, the default
323             view extension is B<html>. The second optional argument is the theme name, if not supplied the current default theme will be used.
324              
325             =cut
326              
327             sub view {
328 0     0 0   my ($self, $view, $theme) = @_;
329            
330 0           my $app = $self->app;
331              
332 0 0         if ($view) {
333 0 0         $view .= ".html" unless ($view =~ /\.html$/i);
334 0   0       $theme ||= $self->{theme} ||= $app->var->get("theme");
      0        
335 0           my $file = $app->file->catfile($app->var->get("themes_dir"), $theme, "view", $view);
336 0           $self->{content} = $app->file->get($file);
337 0           $self->{file} = $file;
338 0           $self->{view} = $view;
339 0   0       $self->{lang} ||= $app->var->get("lang");
340 0           $self->{theme} = $theme;
341 0           $self->parse;
342 0           return $self;
343             }
344              
345 0           $self->{view};
346             }
347             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
348             =head2 lang()
349            
350             $view->lang('en-US');
351             my $lang = $view->lang();
352              
353             Sets or returns the language for processing the template text. Language must be already installed in the lang folder.
354              
355             =cut
356              
357             sub lang {
358 0     0 0   my ($self, $lang) = @_;
359 0 0         if ($lang) {
360 0           $self->{lang} = $lang;
361 0           return $self;
362             }
363 0           $self->{lang};
364             }
365             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
366             =head2 theme()
367            
368             $view->theme('arabic');
369             my $theme = $view->theme();
370              
371             Sets or returns the theme for loading template file. Theme must be already installed in the theme folder.
372              
373             =cut
374              
375             sub theme {
376 0     0 0   my ($self, $theme) = @_;
377 0 0         if ($theme) {
378 0           $self->{theme} = $theme;
379 0           return $self;
380             }
381 0           $self->{theme};
382             }
383             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
384             =head2 var() and set()
385            
386             $view->var(email=>'nile@cpan.org');
387             $view->var(%vars);
388              
389             $view->var(
390             fname => 'Ahmed',
391             lname => 'Elsheshtawy',
392             email => 'sales@domain.com',
393             website => 'http://www.mewsoft.com',
394             htmlnode => 'html code variable <b>Nile</b>',
395             );
396              
397             Sets one of more template variables. This method can be chained.
398              
399             =cut
400              
401             sub var {
402 0     0 0   my ($self, %vars) = @_;
403 0           map {$self->{vars}->{$_} = $vars{$_}} keys %vars;
  0            
404 0           $self;
405             }
406             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
407             =head2 set()
408              
409             $view->set(email=>'nile@cpan.org');
410             $view->set(%vars);
411              
412             Same as method var() above.
413              
414             =cut
415              
416             sub set {
417 0     0 0   my ($self, %vars) = @_;
418 0           map {$self->{vars}->{$_} = $vars{$_}} keys %vars;
  0            
419 0           $self;
420             }
421             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
422             =head2 get()
423              
424             $email = $view->get("email");
425             @user = $view->get(qw(fname lname email website));
426              
427             Returns one or more template variables values.
428              
429             =cut
430              
431             sub get {
432 0     0 0   my ($self, @name) = @_;
433             #@{ $h{'a'} }{ @keys }
434 0           @{ $self->{vars} }{@name};
  0            
435             }
436             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437             =head2 content()
438            
439             # get current template content
440             $content = $view->content;
441              
442             # set current template content direct
443             $view->content($content);
444              
445             Get or set current template content.
446              
447             =cut
448              
449             sub content {
450 0     0 0   my ($self) = shift;
451 0 0         if (@_) {
452 0           $self->{content} = $_[0];
453 0           return $self;
454             }
455 0           $self->{content};
456             }
457             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
458             sub parse_vars {
459            
460 0     0 0   my ($self) = @_;
461            
462 0           my ($match, $attrs, $content, $cdata, $cdata_content, $closing, %attr, $type, $k, $v);
463 0           my $tag = "vars";
464            
465 0           $self->{tag} = +{};
466 0           my $counter = 0;
467            
468             #(<$tag(\s+[^\!\?\s<>](?:"[^"]*"|'[^']*'|[^"'<>])*)/>([^<]*)(<\!\[CDATA\[(.*?)\]\]>)?(</$tag>)?)
469 0           while ( $self->{content} =~ m{
470             (<$tag(\s+[^\!\?\s<>](?:"[^"]*"|'[^']*'|[^"'<>])*)/>)|(<$tag(\s+[^\!\?\s<>](?:"[^"]*"|'[^']*'|[^"'<>\/])*)>(.*?)<\/$tag>)
471             }sxgi ) {
472            
473 0 0         if ($1) {
474 0           ($match, $attrs, $content) = ($1, $2, undef);
475             }
476             else {
477 0           ($match, $attrs, $content) = ( $3, $4, $5);
478 0 0         if ($content =~ /<\!\[CDATA\[(.*?)\]\]>/is) {
479 0           $content = $1;
480             }
481             }
482             #print "match:\n$match \nattrs: $attrs\nvalue: $value\n";
483            
484             # parse attributes to key and value pairs
485 0           %attr = ();
486            
487             #while ( $attrs =~ /\G(?:\s+([^=]+)=(?:"([^"]*)"|'([^']*)'|(\S+))|(.+))/sg ) {
488 0           while ( $attrs =~ m{([^\s\=\"\']+)\s*=\s*(?:(")(.*?)"|'(.*?)')}sxg ) {
489 0 0         $attr{$1} = ( $2 ? $3 : $4 );
490             }
491              
492 0 0 0       if ($attr{name}) {
    0          
493 0 0 0       $type = (exists $attr{type} and $attr{type} ne "")? $attr{type} : "var";
494 0           $self->{tag}->{$type}->{$attr{name}} = {attr=>{%attr}, match=>$match, content=>$content};
495             }
496             elsif (exists $attr{type} and $attr{type} ne "") {
497             # handle <vars type="perl">print "Hello world";</vars>
498             # <vars type="plugin" method="Date->date" />
499 0           $counter++;
500 0           $attr{name} = $tag."_".$counter;
501 0           $type = $attr{type};
502             #say "$attr{name}: $attr{method}, $attr{type}";
503 0           $self->{tag}->{$type}->{$attr{name}} = {attr=>{%attr}, match=>$match, content=>$content};
504             }
505              
506             #print "\n";
507             }
508            
509             #$self->app->dump($self->{tag});
510              
511 0           $self;
512             }
513             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
514             sub parse_blocks {
515 0     0 0   my ($self) = @_;
516 0           $self->{block} = +{};
517 0           $self->parse_nest_blocks($self->{block}, $self->{content});
518 0           return $self;
519             }
520             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
521             sub parse_nest_blocks {
522              
523 0     0 0   my ($self, $aref, $core) = @_;
524              
525 0           my ($k, $v);
526            
527             #The (?<xxx> syntax requires perl 5.10 or above: http://perldoc.perl.org/perl5100delta.html#Named-Capture-Buffers
528             #while ($core =~ /(<!--block:(.*?)-->((?:(?:(?!<!--block:(?:.*?)-->).)|(?R))*?)<!--endblock-->|((?:(?!<!--.*?-->).)+))/igsx )
529             #while ( $core =~ /(?is)(<!--block:(.*?)-->((?:(?:(?!<!--block:(?:.*?)-->).)|(?R))*?)<!--endblock-->|((?:(?!<!--block:.*?-->).)+))/g )
530            
531 0           while ( $core =~ /(?is)(?:((?&content))|(?><!--block:(.*?)-->)((?&core)|)<!--endblock-->|(<!--(?:block:.*?|endblock)-->))(?(DEFINE)(?<core>(?>(?&content)|(?><!--block:.*?-->)(?:(?&core)|)<!--endblock-->)+)(?<content>(?>(?!<!--(?:block:.*?|endblock)-->).)+))/g )
532             {
533 0 0         if (defined $2) {
    0          
534             # CORE
535 0           $k = $2; $v = $3;
  0            
536 0           $aref->{$k} = +{};
537 0           $aref->{$k}->{content} = $v;
538 0           $aref->{$k}->{match} = $&;
539             # print "1:{{$1}}\n2:[[$2]]\n";
540 0           my $curraref = $aref->{$k};
541 0           my $ret = $self->parse_nest_blocks($aref->{$k}, $v);
542 0 0         if (defined $ret) {
543 0           $curraref->{'#next'} = $ret;
544             }
545             }
546             elsif (defined $1) {
547             # CONTENT
548             #$aref->{$k}->{content} .= $1;
549             #say "CONTENT: $1";
550             }
551             else {
552             # ERRORS
553             #say "Error in View->parse_nest_blocks. Unbalanced '$4' at position = ", $-[0];
554             #$IsError = 1;
555             # Decide to continue here ..
556             # If BailOnError is set, just unwind recursion.
557             #if ($BailOnError) {last;}
558             }
559             }
560 0           return $k;
561             }
562             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
563             =head2 block()
564            
565             # get all blocks as a hashref
566             $blocks = $view->block;
567              
568             # get one block as a hashref
569             $block = $view->block("first");
570            
571             # set a block new content
572             $view->block("first", "1st Block New Content ");
573              
574             Get and set blocks. Blocks or iterators are a block of the template code marked or processing and replacing with
575             dynamic content. For example you can use blocks to show or hide a part of the template based on conditions. Another
576             example is using nested blocks as iterators for displaying lists or tables of repeated rows.
577              
578             =cut
579              
580             sub block {
581 0     0 0   my ($self) = shift;
582 0 0         if (@_ == 1) {
    0          
583             # return one block by its complete path like first/second/third/fourth
584 0           my $path = shift;
585             #---------------------------------------
586 0 0         if ($path !~ /\//) {
587 0           return $self->{block}->{$path};
588             }
589             #---------------------------------------
590 0           $path =~ s/^\/+|\/+$//g;
591 0           my @path = split /\//, $path;
592 0           my $v = $self->{block};
593            
594 0           while (my $k = shift @path) {
595 0 0         if (!exists $v->{$k}) {
596 0           return;
597             }
598 0           $v = $v->{$k};
599             }
600              
601 0           return $v;
602             }
603             elsif (@_ > 1) {
604             #set blocks
605 0           my %blocks = @_;
606 0           while (my($k, $v) = each %blocks) {
607             #say "[($k, $v)] " .$self->block($k);
608 0           $self->block($k)->{content} = $v;
609             }
610             #return all blocks hash object
611 0           $self->{block};
612             }
613             else {
614             #return all blocks hash object
615 0           $self->{block};
616             }
617             }
618             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
619             sub process_blocks {
620 0     0 0   my ($self) = $_[0];
621 0           my ($name, $var, $match);
622             #say "Pass...";
623             # process root blocks
624 0           while (($name, $var) = each %{$self->{block}}) {
  0            
625             #say "block: $name";
626             #$self->{block}->{first} = {match=>, content=>, #next=>};
627 0           $match = $var->{match};
628 0           $self->{content} =~ s/\Q$match\E/$var->{content}/gex;
  0            
629             }
630             }
631             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
632             =head2 replace()
633            
634             $view->replace('find text' => 'replaced text');
635             $view->replace(%vars);
636              
637             Replace some template text or code with another one. This method will replace all instances of the found text. This method can be chained.
638              
639             =cut
640              
641             sub replace {
642 0     0 0   my ($self, %vars) = @_;
643 0           while (my ($k, $v) = each %vars) {
644 0           $self->{content} =~ s/\Q$k\E/$v/g;
645             }
646 0           $self;
647             }
648             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
649             =head2 replace_once()
650            
651             $view->replace_once('find text' => 'replaced text');
652             $view->replace_once(%vars);
653              
654             Replace some template text or code with another one. This method will replace only one instance of the found text. This method can be chained.
655              
656             =cut
657              
658             sub replace_once {
659 0     0 0   my ($self, %vars) = @_;
660 0           while (my ($k, $v) = each %vars) {
661 0           $self->{content} =~ s/\Q$k\E/$v/;
662             }
663 0           $self;
664             }
665             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
666             =head2 translate()
667            
668             # scan and replace the template language variables for the default 2 times
669             $view->translate;
670              
671             # scan and replace the template language variables for 3 times
672             $view->translate(3);
673              
674             This method normally used internally when processing the template. It scans the template for the langauge variables
675             surrounded by the curly braces B<{var_name}> and replaces them with their values from the loaded language files.
676             This method can be chained.
677              
678             =cut
679              
680             sub translate {
681 0     0 0   my ($self, $passes) = @_;
682 0           $passes += 0;
683 0   0       $passes ||= 2;
684 0           $self->app->lang->translate(\$self->{content}, $self->{lang}, $passes);
685 0           $self;
686             }
687             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
688             =head2 process_vars()
689            
690             $view->process_vars;
691              
692             This method normally used internally when processing the template. This method can be chained.
693              
694             =cut
695              
696             sub process_vars {
697              
698 0     0 0   my ($self) = $_[0];
699 0           my ($name, $var, $match);
700            
701 0           my $vars = $self->{vars};
702            
703             # get a hash reference to the global variables
704 0           my $shared = $self->app->var->vars();
705              
706 0           while (($name, $var) = each %{$self->{tag}->{var}}) {
  0            
707             #$self->{tag}->{$type}->{$attr{name}} = {attr=>{%attr}, match=>$match, content=>$content};
708 0 0         if (exists $vars->{$name}) {
    0          
709 0           $match = $var->{match};
710 0           $self->{content} =~ s/\Q$match\E/$vars->{$name}/gex;
  0            
711             }
712             elsif (exists $shared->{$name}) {
713 0           $match = $var->{match};
714 0           $self->{content} =~ s/\Q$match\E/$shared->{$name}/gex;
  0            
715             }
716             }
717 0           $self;
718             }
719             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
720             =head2 process_perl()
721            
722             $view->process_perl;
723              
724             This method normally used internally when processing the template. This method can be chained.
725              
726             =cut
727              
728             sub process_perl {
729 0     0 0   my ($self) = $_[0];
730 0           my ($name, $var, $match);
731            
732 0           while (($name, $var) = each %{$self->{tag}->{perl}}) {
  0            
733             #$self->{tag}->{$type}->{$attr{name}} = {attr=>{%attr}, match=>$match, content=>$content};
734 0           $match = $var->{match};
735 0           $self->{content} =~ s/\Q$match\E/$self->capture($var->{content})/gex;
  0            
736             }
737 0           $self;
738             }
739             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
740             =head2 capture()
741            
742             $result = $view->capture($perl_code);
743              
744             This method normally used internally when processing the template.
745              
746             =cut
747              
748             sub capture {
749              
750 0     0 0   my ($self, $code) = @_;
751            
752 0           undef $@;
753 0     0     my ($merged, @result) = Capture::Tiny::capture_merged {eval $code};
  0            
754             #$merged .= join "", @result;
755 0 0         if ($@) {
756 0           $merged = "Embeded Perl code error: $@\n $code\n $merged\n";
757             }
758              
759 0           return $merged;
760             }
761             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
762             =head2 get_widget()
763            
764             $view->get_widget($widget, $theme);
765              
766             This method normally used internally when processing the template. Returns the widget file content.
767              
768             =cut
769              
770             sub get_widget {
771            
772 0     0 0   my ($self, $view, $theme) = @_;
773            
774 0 0         $view .= ".html" unless ($view =~ /\.html$/i);
775 0   0       $theme ||= $self->{theme} ||= $self->app->var->get("theme");
      0        
776 0           my $file = $self->app->file->catfile($self->app->var->get("themes_dir"), $theme, "widget", $view);
777              
778 0 0         if (exists $self->{cash}->{$file}) {
779 0           return $self->{cash}->{$file};
780             }
781              
782 0           my $content = $self->app->file->get($file);
783 0           $self->{cash}->{$file} = $content;
784 0           return $content;
785             }
786             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
787             =head2 process_widgets()
788            
789             $view->process_widgets;
790              
791             This method normally used internally when processing the template. This method can be chained.
792              
793             =cut
794              
795             sub process_widgets {
796              
797 0     0 0   my ($self) = $_[0];
798 0           my ($name, $var, $match, $content, $k, $v);
799            
800 0           while (($name, $var) = each %{$self->{tag}->{widget}}) {
  0            
801             #$self->{tag}->{$type}->{$attr{name}} = {attr=>{%attr}, match=>$match, content=>$content};
802 0           $match = $var->{match};
803 0           $content = $self->get_widget($name);
804             # replace widget args named as [:name:]
805 0           while (($k, $v) = each %{$var->{attr}}) {
  0            
806 0           $content =~ s/\[:$k:\]/$v/g;
807             }
808 0           $self->{content} =~ s/\Q$match\E/$content/g;
809             }
810 0           $self;
811             }
812             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
813             =head2 process_plugins()
814            
815             $view->process_plugins ;
816              
817             This method normally used internally when processing the template. This method can be chained.
818              
819             =cut
820              
821             sub process_plugins {
822              
823 0     0 0   my ($self) = $_[0];
824              
825 0           my ($app, $name, $var, $match, $content, $k, $v, $class, $plugin, $vars, %attr);
826 0           my ($tags, $type, $method, $object, $meta, $capture, $merged, @result);
827              
828 0           $app = $self->app;
829            
830 0   0       $self->{tag}->{plugin} ||= +{};
831 0   0       $self->{tag}->{module} ||= +{};
832            
833 0           foreach $tags($self->{tag}->{plugin}, $self->{tag}->{module}) {
834              
835 0           while (($vars, $var) = each %$tags ) {
836            
837 0           %attr = %{$var->{attr}};
  0            
838             #$self->{tag}->{$type}->{$attr{name}} = {attr=>{%attr}, match=>$match, content=>$content};
839            
840 0           $content = "";
841 0           $name = $attr{method};
842 0           $match = $var->{match};
843 0           $type = lc($attr{type});
844              
845             # delete the attr keys used by the vars tag itself
846 0           delete $attr{$_} for (qw(type method));
847 0           $capture = delete $attr{capture};
848            
849 0           $name =~ s/->/::/;
850 0           my ($plugin, $method) = $name =~ /^(.*)::(\w+)$/;
851              
852 0           $plugin = ucfirst($plugin);
853              
854 0           $class = "Nile::" .ucfirst($type) ."::".$plugin;
855              
856 0 0         if (exists $self->{class_object}->{$class}) {
857 0           $object = $self->{class_object}->{$class};
858             }
859             else {
860 0 0         if (!$app->is_loaded($class)) {
861 0           eval "use $class;";
862 0 0         if ($@) {
863 0           $content = " View Error: $type $plugin\->$method does not exist method $name";
864 0           $self->{content} =~ s/\Q$match\E/$content/gex;
  0            
865 0           undef $@;
866 0           next;
867             }
868             }
869              
870 0           $object = $class->new();
871 0           $self->{class_object}->{$class} = $object;
872 0           $meta = $object->meta;
873              
874             # add method "me" or one of its alt
875 0           $self->app->add_object_context($object, $meta);
876             }
877            
878 0 0         if ($object->can($method)) {
879 0 0         if ($capture) {
880 0     0     ($merged, @result) = Capture::Tiny::capture_merged {eval {$object->$method(%attr)}};
  0            
  0            
881             #$merged .= join "", @result;
882             }
883             else {
884 0           $merged = eval {$object->$method(%attr)};
  0            
885             }
886            
887 0 0         if ($@) {
888 0           $content = "View error: $type method='$name' $@\n $class->$method. $merged\n";
889             }
890             else {
891 0           $content = $merged;
892             }
893             }
894             else {
895 0           $content = " View error: $type '$class' does not have subroutine '$method' method='$name'. ";
896             }
897              
898 0           $self->{content} =~ s/\Q$match\E/$content/gex;
  0            
899             } # while
900             }# for
901              
902 0           $self;
903             }
904             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
905             =head2 parse()
906            
907             $view->parse;
908              
909             This method normally used internally when processing the template. This method can be chained.
910              
911             =cut
912              
913             sub parse {
914 0     0 0   my ($self) = @_;
915 0           $self->parse_vars;
916 0           $self->parse_blocks;
917 0           $self;
918             }
919             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
920             =head2 process_pass()
921            
922             $view->process_pass;
923              
924             This method normally used internally when processing the template. This method can be chained.
925              
926             =cut
927              
928             sub process_pass {
929 0     0 0   my ($self) = @_;
930 0           $self->process(1);
931 0           $self->parse;
932 0           $self;
933             }
934             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
935             =head2 process()
936            
937             $view->process;
938             $view->process($passes);
939              
940             Process the template. This method can be chained.
941              
942             =cut
943              
944             sub process {
945            
946 0     0 0   my ($self, $passes) = @_;
947            
948 0           $passes += 0;
949 0   0       $passes ||= 3;
950            
951 0           for my $pass(1..$passes) {
952 0           $self->translate;
953 0           $self->process_widgets;
954 0           $self->process_blocks;
955 0           $self->process_plugins;
956 0           $self->process_perl;
957 0           $self->process_vars;
958 0 0         if ($pass < $passes) {
959 0           $self->parse;
960             }
961             }
962              
963 0           $self;
964             }
965             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
966             =head2 out()
967            
968             $view->out;
969              
970             Process the template and return the content.
971              
972             =cut
973              
974             sub out {
975 0     0 0   my ($self) = $_[0];
976 0           $self->process();
977 0           return $self->{content};
978             }
979             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
980             =head2 object()
981            
982             # get a new view object
983             #my $view1 = $view->object;
984            
985             Returns a new view object.
986              
987             =cut
988              
989             sub object {
990 0     0 0   my $self = shift;
991 0           $self->app->object(__PACKAGE__, @_);
992             }
993             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
994 0     0     sub DESTROY {
995             }
996             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
997              
998             =pod
999              
1000             =head1 Bugs
1001              
1002             This project is available on github at L<https://github.com/mewsoft/Nile>.
1003              
1004             =head1 HOMEPAGE
1005              
1006             Please visit the project's homepage at L<https://metacpan.org/release/Nile>.
1007              
1008             =head1 SOURCE
1009              
1010             Source repository is at L<https://github.com/mewsoft/Nile>.
1011              
1012             =head1 SEE ALSO
1013              
1014             See L<Nile> for details about the complete framework.
1015              
1016             =head1 AUTHOR
1017              
1018             Ahmed Amin Elsheshtawy, احمد امين الششتاوى <mewsoft@cpan.org>
1019             Website: http://www.mewsoft.com
1020              
1021             =head1 COPYRIGHT AND LICENSE
1022              
1023             Copyright (C) 2014-2015 by Dr. Ahmed Amin Elsheshtawy احمد امين الششتاوى mewsoft@cpan.org, support@mewsoft.com,
1024             L<https://github.com/mewsoft/Nile>, L<http://www.mewsoft.com>
1025              
1026             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
1027              
1028             =cut
1029              
1030             1;