File Coverage

blib/lib/XAO/DO/Web/Page.pm
Criterion Covered Total %
statement 452 508 88.9
branch 224 280 80.0
condition 61 94 64.8
subroutine 48 52 92.3
pod 22 32 68.7
total 807 966 83.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XAO::DO::Web::Page - core object of XAO::Web rendering system
4              
5             =head1 SYNOPSIS
6              
7             Outside web environment:
8              
9             my $page=XAO::Objects->new(objname => 'Page');
10             my $date=$page->expand(template => '<%Date%>');
11              
12             Inside XAO::Web template:
13              
14             <%Page path="/bits/some-path" ARG={<%SomeObject/f%>}%>
15              
16             =head1 DESCRIPTION
17              
18             As XAO::DO::Web::Page object (from now on just Page displayable
19             object) is the core object for XAO::Web web rendering engine we
20             will start with basics of how it works.
21              
22             The goal of XAO::Web rendering engine is to produce HTML data file
23             that can be understood by browser and displayed to a user. It will
24             usually use database tables, templates and various displayable objects
25             to achieve that.
26              
27             Every time a page is requested in someone's web browser a XAO::Web handler
28             gets executed, prepares site configuration, opens database connection,
29             determines what would be start object and/or start path and does a lot
30             of other useful things. If you have not read about it yet it is suggested to
31             do so -- see L and L.
32              
33             Although XAO::Web handler can call arbitrary object with arbitrary arguments
34             to produce an HTML page we will assume the simplest scenario of calling
35             Page object with just one argument -- path to an HTML file template for
36             simplicity (another way to pass some template to a Page object is
37             to pass argument named "template" with the template text as the
38             value). This is the default behavior of XAO::Web handler if you
39             do not override it in configuration.
40              
41             Let's say user asked for http://oursite.com/ and XAO::Web translated
42             that into the call to Page's display method with "path" argument set to
43             "/index.html". All template paths are treated relative to "templates"
44             directory in site directory or to system-wide "templates" directory if
45             site-specific template does not exist. Suppose templates/index.html file
46             in our site's home directory contains the following:
47              
48             Hello, World!
49              
50             As there are no special symbols in that template Page's display method
51             will return exactly that text without any changes (it will also cache
52             pre-parsed template for re-use under mod_perl, but this is irrelevant
53             for now).
54              
55             Now let's move to a more complex example -- suppose we want some kind of
56             header and footer around our text:
57              
58             <%Page path="/bits/header-template"%>
59              
60             Hello, World!
61              
62             <%Page path="/bits/footer-template"%>
63              
64             Now, Page's parser sees reference to other items in that template -
65             these things, surrounded by <% %> signs. What it does is the following.
66              
67             First it checks if there is an argument given to original Page's
68             display() method named 'Page' (case sensitive). In our case there is no
69             such argument present.
70              
71             Then, as no such static argument is found, it attempts to load an
72             object named 'Page' and pass whatever arguments given to that object's
73             display method.
74              
75             I it is recommended to name static
76             arguments in all-lowercase (for standard parameters accepted by an
77             object) or all-uppercase (for parameters that are to be included into
78             template literally) letters to distinguish them from object names where
79             only the first letter of every word is capitalized.
80              
81             In our case Page's parser will create yet another instance of Page
82             displayable object and pass argument "path" with value
83             "/bits/header-template". That will include the content of
84             templates/bits/header-template file into the output. So, if the content
85             of /bits/header-template file is:
86              
87            
88              
89             And the content of /bits/footer-template is:
90              
91            
92              
93             Then the output produced by the original Page's display would be:
94              
95            
96              
97             Hello, World!
98              
99            
100              
101             For the actual site you might opt to use specific objects for header and
102             footer (see L and L):
103              
104             <%Header title="My first XAO::Web page"%>
105              
106             Hello, World!
107              
108             <%Footer%>
109              
110             Page's parser is not limited to only these simple cases, you can embed
111             references to variables and objects almost everywhere. In the following
112             example Utility object (see L) is used to
113             build complete link to a specific page:
114              
115             /somepage.html">blah blah blah
116              
117             If current (configured or guessed) site URL is "http://demosite.com/"
118             this template would be translated into:
119              
120             blah blah blah
121              
122             Even more interesting is that you can use embedding to create arguments
123             for embedded objects:
124              
125             <%Date gmtime={<%CgiParam param="shippingtime" default="0"%>}%>
126              
127             If your page was called with "shippingtime=984695182" argument in the
128             query then this code would expand to (in PST timezone):
129              
130             Thu Mar 15 14:26:22 2001
131              
132             As you probably noticed, in the above example argument value was in
133             curly brackets instead of quotes. Here are the options for passing
134             values for objects' arguments:
135              
136             =over
137              
138             =item 1
139              
140             You can surround value with double quotes: name="value". This is
141             recommended for short strings that do not include any " characters.
142              
143             =item 2
144              
145             You can surround value with matching curly brackets. Curly brackets
146             inside are allowed and counted so that these expansions would work:
147              
148             name={Some text with " symbols}
149              
150             name={Multiple
151             Lines}
152              
153             name={something <%Foo bar={test}%> alsdj}
154              
155             The interim brackets in the last example would be left untouched by the
156             parser. Although this example won't work because of unmatched brackets:
157              
158             name={single { inside}
159              
160             See below for various ways to include special symbols inside of
161             arguments.
162              
163             =item 3
164              
165             Just like for HTML files if the value does not include any spaces or
166             special symbols quotes can be left out:
167              
168             number=123
169              
170             But it is not recommended to use that method and it is not guaranteed
171             that this will remain legal in future versions. Kept mostly for
172             compatibility with already deployed code.
173              
174             =item 4
175              
176             To pass a string literally without performing any substitutions you can
177             use single quotes. For instance:
178              
179             <%FS
180             uri="/Members/<%MEMBER_ID/f%>"
181             mode="show-hash"
182             fields="*"
183             template='<%MEMBER_AGE/f%> -- <%MEMBER_STATUS/f%>'
184             %>
185              
186             If double quotes were used in this example then the parser would try to
187             expand <%MEMBER_AGE%> and <%MEMBER_STATUS%> variables using the current
188             object arguments which is not what is intended. Using single quotes it
189             is possible to let FS object do the expansion and therefore insert
190             database values in this case.
191              
192             =item 5
193              
194             To pass multiple nested arguments literally or to include a single quote
195             into the string matching pairs of {' and '} can be used:
196              
197             <%FS
198             uri="/Members/<%MEMBER_ID/f%>"
199             mode="show-hash"
200             fields="*"
201             template={'Member's age is <%MEMBER_AGE/f%>'}
202             %>
203              
204             =back
205              
206              
207             =head2 BINARY vs UNICODE
208              
209             By default expanded templates and object arguments are bytes, not
210             Unicode characters. This does not mean that they cannot be used in
211             Unicode (or other encoding) web sites, but it does mean that objects
212             need to convert arguments and expansion results into characters where
213             and as needed.
214              
215             Starting with version 1.48 it is possible to switch the engine
216             into returning perl characters from template expansion and for
217             passing characters in object arguments. This is achieved by setting
218             /xao/page/character_mode configuration parameter to '1'.
219              
220             One exception allowing to retrieve byte data is when a template is
221             expanded with 'unparsed' qualifier. This is typically used to pass
222             binary data such as images and spreadsheets to objects such as
223             L.
224              
225             There is an important caveat for using 'unparsed' with binary data in
226             character mode. A template like the one below wou pass perl characters
227             in 'data' argument when executing Process.
228              
229             <%Process
230             data={<%Page
231             path='/binary/data.bin'
232             unparsed
233             %>}
234             %>
235              
236             There is no way to mix byte and character processing mode. Pick one
237             depending on your needs. Character mode allows a more natural processing
238             in Perl while byte mode allows more granular control over the data flow.
239              
240              
241             =head2 EMBEDDING SPECIAL CHARACTERS
242              
243             Sometimes it is necessary to include various special symbols into
244             argument values. This can be done in the same way you would embed
245             special symbols into HTML tags arguments:
246              
247             =over
248              
249             =item *
250              
251             By using &tag; construction, where tag could be "quot", "lt", "gt" and
252             "amp" for double quote, left angle bracket, right angle bracket and
253             ampersand respectfully.
254              
255             =item *
256              
257             By using &#NNN; construction where NNN is the decimal code for the
258             corresponding symbol. For example left curly bracket could be encoded as
259             { and right curly bracket as }. The above example should be
260             re-written as follows to make it legal:
261              
262             name={single { inside}
263              
264             =back
265              
266              
267             =head2 OUTPUT CONVERSION
268              
269             As the very final step in the processing of an embedded object or
270             variable the parser will check if it has any flags and convert it
271             accordingly. This can (and should) be used to safely pass special
272             characters into fields, HTML documents and so on.
273              
274             For instance, the following code might break if you do not use flags and
275             variable will contain a duoble quote character in it:
276              
277            
278              
279             Correct way to write it would be (note /f after VALUE):
280              
281            
282              
283             Generic format for specifying flags is:
284              
285             <%Object/x ...%> or <$VARIABLE/x$>
286              
287             Where 'x' could be one of:
288              
289             =over
290              
291             =item f
292              
293             Converts text for safe use in HTML elements attributes. Mnemonic for
294             remembering - (f)ield.
295              
296             Will convert '123"234' into '123"234'.
297              
298             =item h
299              
300             Converts text for safe use in HTML text. Mnemonic - (H)TML.
301              
302             Will convert '123
234' into '123<BR>234'.
303              
304             =item q
305              
306             Converts text for safe use in HTML query parameters. Mnemonic - (q)uery.
307              
308             Will convert '123 234' into '123%20234'.
309              
310             Example: Test '<$VAR/h$>'
311              
312             =item s
313              
314             The same as 'h' excepts that it translates empty string into
315             ' '. Suitable for inserting pieces of text into table cells.
316              
317             =item u
318              
319             The same as 'q'. Mnemonic - (U)RL, as it can be used to convert text for
320             inclusion into URLs.
321              
322             =back
323              
324             It is a very good habit to use flags as much as possible and always
325             specify a correct conversion. Leaving output untranslated may lead to
326             anything from broken HTML to security violations.
327              
328             =head2 LEVELS OF PARSING
329              
330             Arguments can include as many level of embedding as you like, but you
331             must remember:
332              
333             =over
334              
335             =item 1
336              
337             That all embedded arguments are expanded from the deepest
338             level up to the top before executing main object.
339              
340             =item 2
341              
342             That undefined references to either non-existing object or non-existing
343             variable produce a run-time error and the page is not shown.
344              
345             =item 3
346              
347             All embedded arguments are processed in the same arguments space that
348             the template one level up from them.
349              
350             =back
351              
352             As a test of how you understood everything above please attempt to
353             predict what would be printed by the following example (after reading
354             L or guessing its meaning). The answer is
355             about one page down, at the end of this chapter.
356              
357             <%SetArg name="V1" value="{}"%>
358             <%SetArg name="V2" value={""}%>
359             <%Page template={<%V1%><%V2%>
360             <%Page template={<%SetArg name="V2" value="[]" override%><%V2%>}%>
361             <%V2%><%V1%>}
362             %>
363              
364             In most cases it is not recommended to make complex inline templates
365             though. It is usually better to move a sub-template into a separate file
366             and include it by passing a 'path' parameter into Page. Usually it is
367             also more efficient because templates with known paths are cached in
368             parsed state the first time they are used while inlined templates are
369             parsed every time.
370              
371             It is usually a good idea to make templates as simple as possible and move
372             most of the logic inside of objects. To comment what you're doing in
373             various parts of template you can use normal HTML-style comments. They
374             are removed from the output completely, so you can include any amount
375             of text inside of comments -- it won't impact the size of final HTML
376             file. Here is an example:
377              
378            
379             <%Header title="demosite.com"%>
380             <%Page path="/bits/menu"%>
381              
382            
383             <%Page path="/bits/body"%>
384              
385            
386             <%Footer%>
387              
388             One exception is JavaScript code which is usually put into comments. The
389             parser will NOT remove comments if the opening comment is
397            
398              
399              
400             =head2 CACHING
401              
402             Parsed templates are always cached either locally or using a configured
403             cache. The cache is keyed on 'path' or 'template' parameters value (two
404             identical 'template's will only parse once). Parse cache can be disabled
405             by giving a "xao.uncached" parameter. See parse() method description
406             for details.
407              
408             The fully rendered content can also be cached if a couple of conditions
409             are met:
410              
411             =over
412              
413             =item *
414              
415             /xao/page/render_cache_name in the config -- this should contain a name of
416             the cache to be used for rendered page components.
417              
418             =item *
419              
420             The page is configured to be cacheable with either an entry in
421             the configuration under '/xao/page/render_cache_allow' or with a
422             'xao.cacheable' parameter given (e.g. something like <%Page ...
423             xao.cacheable%>).
424              
425             =item *
426              
427             There is no "/xao/page/render_cache_update" in the clipboard. This can be used
428             to force cache reload by checking some environmental variable early in
429             the flow and setting the clipboard to disable all render caches for that
430             one render. Cached content is not used, but is updated -- so subsequent
431             cached calls with the same parameters will return new content.
432              
433             =item *
434              
435             There is no "/xao/page/render_cache_skip" in the clipboard. This can be used to
436             skip cache altogether if it is known that pages rendered in this session
437             are different from cached and the cache does not want to be contaminated
438             with them.
439              
440             =back
441              
442             Properly used render cache can speed up pages significantly, but if
443             used incorrectly it can also introduce very hard to find issues in the
444             rendered content.
445              
446             Carefully consider what pages to tag with "cacheable" tag. Benchmarking
447             reports can be of great help for that.
448              
449             Entries in the config /xao/page/render_cache_allow may include additional
450             specifications for what parameters are checked when rendered content is
451             cached. By default, if the value is '1' or 'on' all of Page template
452             parameters are checked, but none of CGI or cookies. Values for
453             parameters 'path' and 'template' are always checked, regardless of the
454             configuration.
455              
456             The configuration can look like this:
457              
458             xao => {
459             page => {
460             render_cache_name => 'xao_page_render',
461             render_cache_allow => {
462             'p:/bits/complex-template' => 1,
463             'p:/bits/complex-cgi' => {
464             param => [ '*' ],
465             cgi => [ 'cf*' ],
466             },
467             'p:/bits/complex-cookie' => {
468             param => [ '*', '!session*' ],
469             cookie => [ 'session' ],
470             },
471             },
472             },
473             }
474              
475              
476             =head2 BENCHMARKING
477              
478             Benchmarking can be started and stopped by using benchmark_start()
479             and benchmark_stop() calls. The hash with current benchmarking data can
480             be retrieved with benchmark_stats() call.
481              
482             When benchmarking is started all rendered paths (and optionally all
483             templates) are timed and are also analyzed for potential cacheability --
484             if rendered content is repeatedly the same for some set of parameters.
485              
486             Custom execution paths spanning multiple templates can be tracked by
487             using benchmark_enter($tag) and benchmark_leave($tag) calls.
488              
489             The data is "static", not specific to a particular Page object.
490              
491             Benchmarking slows down processing. Do not use it in production.
492              
493             For an easy way to control benchmarking from templates use <%Benchmark%>
494             object.
495              
496              
497             =head2 NOTE FOR HARD-BOILED HACKERS
498              
499             If you do not like something in the parser behavior you can define
500             site-specific Page object and refine or replace any methods of system
501             Page object. Your new object would then be used by all system and
502             site-specific objects B and won't impact any other sites
503             installed on the same host. But this is mentioned here merely as a
504             theoretical possibility, not as a good thing to do.
505              
506             =head2 TEST OUTPUT
507              
508             The output of the test above would be:
509              
510             {}""
511             []
512             ""{}
513              
514             In fact first two SetArg's would add two empty lines in front because
515             they have carriage returns after them, but this is only significant if
516             your HTML code is space-sensitive.
517              
518             =head1 METHODS
519              
520             Publicly accessible methods of Page (and therefor of all objects derived
521             from Page unless overwritten) are:
522              
523             =over
524              
525             =cut
526              
527             ###############################################################################
528             package XAO::DO::Web::Page;
529 17     17   22438 use warnings;
  17         50  
  17         738  
530 17     17   132 use strict;
  17         51  
  17         396  
531 17     17   9268 use utf8;
  17         243  
  17         136  
532 17     17   10086 use Digest::SHA qw(sha1_hex);
  17         58538  
  17         1458  
533 17     17   144 use Encode;
  17         44  
  17         1526  
534 17     17   10297 use Time::HiRes qw(gettimeofday tv_interval);
  17         25122  
  17         83  
535 17     17   16036 use JSON qw(to_json);
  17         131919  
  17         135  
536 17     17   2237 use XAO::Cache;
  17         37  
  17         392  
537 17     17   88 use XAO::Objects;
  17         64  
  17         417  
538 17     17   91 use XAO::PageSupport;
  17         47  
  17         588  
539 17     17   103 use XAO::Projects qw(:all);
  17         47  
  17         2951  
540 17     17   133 use XAO::Templates;
  17         43  
  17         406  
541 17     17   84 use XAO::Utils;
  17         35  
  17         1010  
542 17     17   137 use Error qw(:try);
  17         40  
  17         144  
543              
544 17     17   2984 use base XAO::Objects->load(objname => 'Atom');
  17         50  
  17         110  
545              
546             # Prototypes
547             #
548             sub cache ($%);
549             sub cgi ($);
550             sub check_db ($);
551             sub dbh ($);
552             sub display ($%);
553             sub expand ($%);
554             sub finaltextout ($%);
555             sub object ($%);
556             sub odb ($);
557             sub parse ($%);
558             sub siteconfig ($);
559             sub textout ($%);
560             sub benchmark_enabled ($);
561             sub benchmark_enter ($$;$$$);
562             sub benchmark_leave ($$;$$);
563             sub benchmark_reset ($);
564             sub benchmark_start ($;$);
565             sub benchmark_stats ($;$);
566             sub benchmark_stop ($);
567             sub page_clipboard ($);
568              
569             sub _do_pass_args ($$$);
570              
571             ###############################################################################
572              
573             sub params_digest ($$;$) {
574 115     115 0 932 my ($self,$args,$spec)=@_;
575              
576             # Dropping non-scalar values from params. They get in by calling
577             # ::Action::data_... methods for example, and in other scenarios
578             # too.
579             #
580 115 100       325 my $params={ map { ref $args->{$_} ? () : ($_ => $args->{$_}) } keys %$args };
  396         1144  
581              
582             # Template and path are always passed along
583             #
584 115         313 my $path=delete $params->{'path'};
585 115         186 my $template=delete $params->{'template'};
586              
587             # Checking what is considered important for the digest, getting a
588             # specification. It may come from outside in testing.
589             #
590 115 100       240 if(!$spec) {
591 101         163 $spec=$args->{'xao.cacheable'};
592             }
593              
594 115 100 66     611 if(!$spec && !defined $args->{'template'} && (my $path=$args->{'path'})) {
      66        
595 89         146 my $cache_allow=$self->{'cache_allow'};
596 89 50       170 if($cache_allow) {
597 89         228 $spec=$cache_allow->{'p:'.$path};
598             }
599             }
600              
601             # It may be a hash of instructions about what to keep and what to
602             # drop for the key:
603             #
604             # param => [ 'FOO*', '!FOO.BAR*' ],
605             # cgi => [ 'fn', 'fv' ],
606             # cookie => [ 'customer_id' ],
607             #
608             # Default is to ignore cookies and CGI and hash all scalar
609             # parameters.
610             #
611 115         274 my $cgis;
612             my $cookies;
613 115         0 my $protocol;
614 115 100 100     330 if($spec && ref($spec)) {
615 13         44 while(my ($spec_key,$spec_list)=each %$spec) {
616 31         51 my $hash;
617             my $target;
618              
619 31 100 66     104 if($spec_key eq 'param') {
    100 33        
    100          
    50          
620 13         21 $hash=$params;
621 13         34 $target=\$params;
622             }
623             elsif($spec_key eq 'cgi') {
624 8         18 my $cgi=$self->cgi;
625 8         22 $hash={ map { $_ => [ $cgi->param($_) ] } $cgi->param };
  16         434  
626 8         228 $target=\$cgis;
627             }
628             elsif($spec_key eq 'cookie' || $spec_key eq 'cookies') {
629 8         33 my $config=$self->siteconfig;
630 8         31 $hash={ map { $_ => $config->get_cookie($_,1) } $self->cgi->cookie() };
  14         3646  
631 8         22 $target=\$cookies;
632             }
633             elsif($spec_key eq 'proto' && $spec_list) {
634 2 50       8 $protocol=$self->is_secure ? 'https' : 'http';
635 2         66 next;
636             }
637             else {
638 0         0 throw $self "- unsupported source '$spec_key' for '$args->{'path'}'";
639             }
640              
641 29         74 $$target=$self->_do_pass_args($hash,$spec_list);
642             }
643             }
644              
645             # Converting to a canonical scalar and calculating a unique digest.
646             #
647 115         640 my $params_json=to_json([$path,$template,$params,$cgis,$cookies,$protocol],{ utf8 => 1, canonical => 1 });
648              
649 115         3833 my $params_digest=sha1_hex($params_json);
650              
651 115 100       592 return wantarray ? ($params_digest,$params_json) : $params_digest;
652             }
653              
654             ###############################################################################
655              
656             sub _do_display ($@) {
657 1130     1130   1916 my $self=shift;
658 1130         2819 my $cache_args=get_args(\@_);
659              
660             # We need to operate on this specific hash because it can get
661             # modified during template processing.
662             #
663 1130   33     8499 my $args=$self->{'args'} || throw $self "- no 'args' in self";
664              
665             # Preparing to benchmark if requested
666             #
667 1130         2575 my $benchmark=$self->benchmark_enabled();
668              
669             # We need to bookmark buffer position to analyze content data
670             # for cacheability later.
671             #
672 1130 100       35712 my $bookmark=$benchmark ? XAO::PageSupport::bookmark() : 0;
673              
674             # When called from a cache retrieve we have a cache_key parameter.
675             #
676 1130         1780 my $from_cache_retrieve=$cache_args->{'cache_key'};
677 1130 100       2084 if($from_cache_retrieve) {
678 4         13 XAO::PageSupport::push();
679              
680 4 50       9 if($self->debug_check('render-cache-add')) {
681 0         0 my ($args_digest,$args_json)=$self->params_digest($args);
682 0         0 dprint "RENDER_CACHE_ADD: $args_digest / $args_json";
683             }
684             }
685              
686             # Parsing template or getting already pre-parsed template when it is
687             # available.
688             #
689             # Also defining the tag for benchmarking. Normally it is only
690             # defined for paths, but can also be defined for templates.
691             #
692 1130         3068 my $benchmark_tag;
693             my $args_digest;
694 1130         0 my $args_json;
695 1130         0 my $parsed;
696 1130 100       1933 if($benchmark) {
697 447         1237 $parsed=$self->parse($args,{ cache_key_ref => \$benchmark_tag });
698              
699 447 100 66     2448 if($benchmark<2 && $benchmark_tag && substr($benchmark_tag,0,2) ne 'p:') {
      100        
700 208         338 $benchmark_tag=undef;
701             }
702             }
703             else {
704 683         1439 $parsed=$self->parse($args);
705             }
706              
707             # Starting the stopwatch if needed. We may not get a tag if this is
708             # an inner pre-parsed template.
709             #
710             # Calculating a 'run' key that uniquely identifies a specific set of
711             # parameters. Used for two purposes: identifying cacheable pages
712             # and benchmarking self-referencing recurrent templates.
713             #
714 1130 100       2215 if($benchmark_tag) {
715 89         209 ($args_digest,$args_json)=$self->params_digest($args);
716 89 100       271 $self->benchmark_enter($benchmark_tag,$args_digest,$args_json,$self->can_cache_render($args) ? 1 : 0);
717             }
718              
719             # Template processing itself. Pretty simple, huh? :)
720             #
721 1130         2489 foreach my $item (@$parsed) {
722 7035         13566 my $stop_after;
723             my $itemflag;
724 7035         0 my $text;
725              
726 7035 100       14805 if(exists $item->{'text'}) {
    100          
    50          
727 3340         5147 $text=$item->{'text'};
728             }
729              
730             elsif(exists $item->{'varname'}) {
731 2255         3318 my $varname=$item->{'varname'};
732 2255         3322 $text=$args->{$varname};
733 2255 50       4180 defined $text ||
734             throw $self "- undefined argument '$varname'";
735 2255         3174 $itemflag=$item->{'flag'};
736             }
737              
738             elsif(exists $item->{'objname'}) {
739 1440         2216 my $objname=$item->{'objname'};
740              
741 1440         2073 $itemflag=$item->{'flag'};
742              
743             # First we're trying to substitute from arguments for old
744             # style <%FUBAR%>
745             #
746 1440         2021 $text=$args->{$objname};
747              
748             # Executing object if not.
749             #
750 1440 100       2786 if(!defined $text) {
751 1384         2981 my $obj=$self->object(objname => $objname);
752              
753             # Preparing arguments. If argument includes object references -
754             # they are expanded first.
755             #
756 1384         101699 my %objargs;
757 1384         2190 my $ia=$item->{'args'};
758 1384         2176 my $args_copy;
759             my $page_obj;
760 1384         3175 foreach my $a (keys %$ia) {
761 2971         4510 my $v=$ia->{$a};
762 2971 100       5264 if(ref($v)) {
763 787 100 100     2831 if(@$v==1 && exists($v->[0]->{'text'})) {
764 487         973 $v=$v->[0]->{'text'};
765             }
766             else {
767 300 100       964 if(!$args_copy) {
768 287         593 $args_copy=merge_refs($args);
769 287         3099 delete $args_copy->{'path'};
770             }
771 300 100       598 if(!$page_obj) {
772 287         570 $page_obj=$self->object(objname => 'Page');
773             }
774 300         18127 $args_copy->{'template'}=$v;
775 300         700 $v=$page_obj->expand($args_copy);
776             }
777             }
778              
779             # Decoding entities from arguments. Lt, gt, amp,
780             # quot and &#DEC; are supported.
781             #
782 2971         4942 $v=~s/</
783 2971         4015 $v=~s/>/>/sg;
784 2971         3833 $v=~s/"/"/sg;
785 2971         3934 $v=~s/&#(\d+);/chr($1)/sge;
  26         107  
786 2971         3872 $v=~s/&/&/sg;
787              
788 2971         5950 $objargs{$a}=$v;
789             }
790              
791             # Executing object. For speed optimisation we call object's
792             # display method directly if we're not going to do anything
793             # with the text anyway. This way we avoid push/pop and at
794             # least two extra memcpy's.
795             #
796 1384 100 66     3584 if($itemflag && $itemflag ne 't') {
797 160         408 $text=$obj->expand(\%objargs);
798             }
799             else {
800 1224         3376 $obj->display(\%objargs);
801             }
802              
803             # Indicator that we do not need to parse or display anything
804             # after that point.
805             #
806 1382         3962 $stop_after=$self->clipboard->get('_no_more_output');
807              
808             # Was it something like SetArg object? Merging changes in then.
809             #
810 1382 100       38189 if($self->{'merge_args'}) {
811 181         264 @{$args}{keys %{$self->{'merge_args'}}}=values %{$self->{'merge_args'}};
  181         341  
  181         359  
  181         492  
812 181         890 delete $self->{'merge_args'};
813             }
814             }
815             }
816              
817 7033 100       11909 if(defined $text) {
818              
819             # When the text is from an external argument like \xe9
820             # it might be stored in a platform encoding and not in
821             # Unicode. Upgrading it.
822             #
823 5811 100 100     9444 utf8::upgrade($text) if $self->_character_mode && !$item->{'binary'};
824              
825             # Safety conversion - q for query, h - for html, s - for
826             # nbsp'ced html, f - for tag fields, u - for URLs, t - for text
827             # as is (default).
828             #
829 5811 100 66     12507 if($itemflag && $itemflag ne 't') {
830 1637 100       3666 if($itemflag eq 'h') {
    50          
    100          
    100          
    100          
    50          
831 453         783 $text=XAO::Utils::t2ht($text);
832             }
833             elsif($itemflag eq 's') {
834 0 0 0     0 $text=(defined $text && length($text)) ? XAO::Utils::t2ht($text) : " ";
835             }
836             elsif($itemflag eq 'q') {
837 451         913 $text=XAO::Utils::t2hq($text);
838             }
839             elsif($itemflag eq 'f') {
840 731         1523 $text=XAO::Utils::t2hf($text);
841             }
842             elsif($itemflag eq 'u') {
843 1         15 $text=XAO::Utils::t2hq($text);
844             }
845             elsif($itemflag eq 'j') {
846 1         9 $text=XAO::Utils::t2hj($text);
847             }
848             else {
849 0         0 eprint "Unsupported translation flag '$itemflag', objname=",$item->{'objname'};
850             }
851             }
852              
853             # Sending out the text
854             #
855 5811         26333 $self->textout($text);
856             }
857              
858             # Checking if this object required to stop processing
859             #
860 7033 100       13850 last if $stop_after;
861             }
862              
863             # We need to return the actual rendered content if this is called
864             # from cache render.
865             #
866 1128         1596 my $content=undef;
867 1128 100       2427 if($from_cache_retrieve) {
    100          
868 4   33     8 $content=XAO::PageSupport::pop($self->_character_mode && !$args->{'unparsed'});
869             }
870             elsif($benchmark_tag) {
871 89   33     167 $content=XAO::PageSupport::peek($bookmark,$self->_character_mode && !$args->{'unparsed'});
872             }
873              
874             # When benchmarking we stop the timer and we also remember the
875             # content for cacheability analysis.
876             #
877 1128 100       1986 if($benchmark_tag) {
878 89         749 my $content_digest=sha1_hex($content);
879 89         214 $self->benchmark_leave($benchmark_tag,$args_digest,$content_digest);
880             }
881              
882             # This will be an undef if the call is not from cache. That is fine.
883             #
884 1128         3539 return $content;
885             }
886              
887             ###############################################################################
888              
889             sub _character_mode ($) {
890 7950     7950   10125 my $self=shift;
891              
892 7950 100       20546 return $self->{'character_mode'} if exists $self->{'character_mode'};
893              
894 1028 100       1969 my $character_mode=$self->siteconfig->get('/xao/page/character_mode') ? 1 : 0;
895              
896 1028         47386 $self->{'character_mode'}=$character_mode;
897              
898 1028         2780 return $character_mode;
899             }
900              
901             ###############################################################################
902              
903             sub _render_cache ($) {
904 21     21   41 my $self=$_[0];
905              
906 21 100       96 return $self->{'render_cache_obj'} if exists $self->{'render_cache_obj'};
907              
908 3   100     18 my $cache_name=$self->siteconfig->get('/xao/page/render_cache_name') || '';
909              
910 3         207 my $cache_obj;
911              
912 3 100       17 if($cache_name) {
913 2         26 dprint "Using a cache '$cache_name' for rendered templates";
914              
915 2         23 $cache_obj=$self->cache(
916             name => $cache_name,
917             coords => [ 'cache_key' ],
918             retrieve => \&_do_display,
919             );
920             }
921              
922 3         623 $self->{'render_cache_obj'}=$cache_obj;
923              
924 3         18 return $cache_obj;
925             }
926              
927             ###############################################################################
928              
929             # In case of memcached this clears ALL caches, not just render!
930              
931             sub render_cache_clear ($) {
932 1     1 0 74 my $self=$_[0];
933              
934 1         5 my $cache=$self->_render_cache;
935              
936 1 50       32 $cache->drop_all if $cache;
937             }
938              
939             ###############################################################################
940              
941             sub can_cache_render ($$) {
942 1225     1225 0 2229 my ($self,$args)=@_;
943              
944 1225 50       2643 return 0 if $self->page_clipboard->{'render_cache_skip'};
945              
946 1225 100       2676 return 1 if $args->{'xao.cacheable'};
947              
948 1205   66     3260 my $path=!defined $args->{'template'} && $args->{'path'};
949              
950 1205 100       3273 return 0 unless $path;
951              
952 285         591 my $cache_key='p:' . $path;
953              
954 285         479 my $cache_allow=$self->{'cache_allow'};
955 285 100       592 if(!$cache_allow) {
956 132         307 $cache_allow=$self->siteconfig->get('/xao/page/render_cache_allow');
957 132 100       9830 if($cache_allow) {
958 121         306 $self->{'cache_allow'}=$cache_allow;
959             }
960             else {
961 11         63 $cache_allow=$self->{'cache_allow'}={ };
962 11         40 $self->siteconfig->put('/xao/page/render_cache_allow' => $cache_allow);
963             }
964             }
965              
966 285         1351 return $cache_allow->{$cache_key};
967             }
968              
969             ###############################################################################
970              
971             =item display (%)
972              
973             Displays given template to the current output buffer. The system uses
974             buffers to collect all text displayed by various objects in a rather
975             optimal way using XAO::PageSupport (see L)
976             module. In XAO::Web handler the global buffer is initialized and after all
977             displayable objects have worked their way it retrieves whatever was
978             accumulated in that buffer and displays it.
979              
980             This way you do not have to think about where your output goes as long
981             as you do not "print" anything by yourself - you should always call
982             either display() or textout() to print any piece of text.
983              
984             Display() accepts the following arguments:
985              
986             =over
987              
988             =item pass
989              
990             Passes arguments from calling context into the template.
991              
992             The syntax allows to map parent arguments into new names,
993             and/or to limit what is passed. Multiple semi-colon separated rules are
994             allowed. Rules are processed from left to right.
995              
996             NEWNAME=OLDNAME - pass the value of OLDNAME as NEWNAME
997             NEW*=OLD* - pass all old values starting with OLD as NEW*
998             VAR;VAR.* - pass VAR and VAR.* under their own names
999             *;!VAR* - pass everything except VAR*
1000              
1001             The default, when the value of 'pass' is 'on' or '1', is the same as
1002             passing '*' -- meaning that all parent arguments are passed literally
1003             under their own names.
1004              
1005             There are exceptions, that are never passed from parent arguments:
1006             'pass', 'objname', 'path', and 'template'.
1007              
1008             Arguments given to display() override those inherited from the caller
1009             using 'pass'.
1010              
1011             =item path => 'path/to/the/template'
1012              
1013             Gives Page a path to the template that should be processed and
1014             displayed.
1015              
1016             =item template => 'template text'
1017              
1018             Provides Page with the actual template text.
1019              
1020             =item unparsed => 1
1021              
1022             If set it does not parse template, just displays it literally.
1023              
1024             =back
1025              
1026             Any other argument given is passed into template unmodified as a
1027             variable. Remember that it is recommended to pass variables using
1028             all-capital names for better visual recognition.
1029              
1030             Example:
1031              
1032             $obj->display(path => "/bits/left-menu", ITEM => "main");
1033              
1034             For security reasons it is also recommended to put all sub-templates
1035             into /bits/ directory under templates tree or into "bits" subdirectory
1036             of some tree inside of templates (like /admin/bits/admin-menu). Such
1037             templates cannot be displayed from XAO::Web handler by passing their
1038             path in URL.
1039              
1040             =cut
1041              
1042             sub display ($%) {
1043 1136     1136 1 4778 my $self=shift;
1044 1136         2527 my $args=$self->{'args'}=get_args(\@_);
1045              
1046             # Merging parent's args in if requested.
1047             #
1048 1136 100       12006 if($args->{'pass'}) {
1049 28         77 $args=$self->{'args'}=$self->pass_args($args->{'pass'},$args);
1050             }
1051              
1052             # Is this page cacheable? There is a distinction between page not
1053             # being cached with '/xao/page/render_cache_skip' and page being flushed in
1054             # cache with '/xao/page/render_cache_update'.
1055             #
1056 1136 100       2828 if($self->can_cache_render($args)) {
1057 20 100       66 if(my $cache=$self->_render_cache()) {
1058              
1059             # The key depends on all arguments.
1060             #
1061 10         34 my ($cache_key,$params_json)=$self->params_digest($args);
1062              
1063 10 50       43 if($self->debug_check('render-cache-get')) {
1064 0         0 dprint "RENDER_CACHE_GET: $cache_key / $params_json";
1065             }
1066              
1067             # Building the content. Real arguments for displaying are in
1068             # $self->{'args'}.
1069             #
1070             my $content=$cache->get($self,{
1071             cache_key => $cache_key,
1072 10   33     34 force_update => ($self->page_clipboard->{'render_cache_update'} || $args->{'xao.uncached'}),
1073             });
1074              
1075 10         526 $self->textout($content);
1076              
1077 10         24 return;
1078             }
1079             }
1080              
1081             # We get here if the page cannot be cached
1082             #
1083 1126         3074 $self->_do_display();
1084             }
1085              
1086             ###############################################################################
1087              
1088             =item expand (%)
1089              
1090             Returns a string corresponding to the expanded template. Accepts exactly
1091             the same arguments as display(). Here is an example:
1092              
1093             my $str=$obj->expand(template => '<%Date%>');
1094              
1095             =cut
1096              
1097             sub expand ($%) {
1098 949     949 1 42535 my $self=shift;
1099 949         2302 my $args=get_args(\@_);
1100              
1101             # First it prepares a place in stack for new text (push) and after
1102             # display it calls pop to get back whatever was written. The sole
1103             # reason for all this is speed optimization - XAO::PageSupport is
1104             # implemented in C in quite optimal way.
1105             #
1106 949         11194 XAO::PageSupport::push();
1107              
1108             # Not using Error's try{} -- it is too slow. Benchmarking showed
1109             # about 7% slowdown.
1110             #
1111             ### my $args=get_args(\@_);
1112             ### try {
1113             ### $self->display($args);
1114             ### }
1115             ### otherwise {
1116             ### my $e=shift;
1117             ###
1118             ### # Popping out the potential output of the failed
1119             ### # template. Otherwise we are going to break the stack order.
1120             ### #
1121             ### XAO::PageSupport::pop();
1122             ###
1123             ### $e->throw();
1124             ### };
1125              
1126             # Eval is faster, almost indistinguishable from the bare call on
1127             # benchmark results.
1128             #
1129 949         1688 eval {
1130 949         2480 $self->display($args);
1131             };
1132              
1133 949 100       105477 if($@) {
1134 2         32 XAO::PageSupport::pop(0);
1135              
1136 2 50       35 if($@->can('throw')) {
1137 2         53 throw $@;
1138             }
1139             else {
1140 0         0 throw $self "- $@";
1141             }
1142             }
1143              
1144             # Text pages are converted into perl characters, otherwise returning
1145             # bytes.
1146             #
1147             my $chmode=$self->_character_mode &&
1148 947   100     2149 !$args->{'unparsed'} &&
1149             !$self->siteconfig->force_byte_output;
1150              
1151 947 100       4356 return XAO::PageSupport::pop($chmode ? 1 : 0);
1152             }
1153              
1154             ###############################################################################
1155              
1156             =item parse ($%)
1157              
1158             Takes template from either 'path' or 'template' and parses it. If given
1159             the following template:
1160              
1161             Text <%Object a=A b="B" c={X<%C/f ca={CA}%>} d='D' e={'<$E$>'}%>
1162              
1163             It will return a reference to an array of the following structure:
1164              
1165             [ { text => 'Text ',
1166             },
1167             { objname => 'Object',
1168             args => {
1169             a => [
1170             { text => 'A',
1171             },
1172             ],
1173             b => [
1174             { text => 'B',
1175             },
1176             ],
1177             c => [
1178             { text => 'X',
1179             },
1180             { objname => 'C',
1181             flag => 'f',
1182             args => {
1183             ca => [
1184             { text => 'CA',
1185             },
1186             ],
1187             },
1188             },
1189             ],
1190             d => 'D',
1191             e => '<$E$>',
1192             },
1193             },
1194             ]
1195              
1196             With "unparsed" parameter the content of the template is not analyzed
1197             and is returned as a single 'text' node.
1198              
1199             Templates are only parsed once, unless an "xao.uncached" parameter is
1200             set to true.
1201              
1202             Normally the parsed templates cache uses a local perl hash. If
1203             desired a XAO::Cache based implementation can be used by setting
1204             /xao/page/parse_cache_name parameter in the site configuration to the desired
1205             cache name (e.g. "xao_parse_cache").
1206              
1207             Statistics of various ways of calling:
1208              
1209             memcached-cache-path 1866/s
1210             memcached-cache-template 2407/s
1211             no-cache-path 5229/s
1212             no-cache-template 5572/s
1213             memory-cache-template 26699/s
1214             memory-cache-path 45253/s
1215             local-cache-template 49681/s
1216             local-cache-path 149806/s
1217              
1218             Unless the site has a huge number of templates there is really no
1219             compelling reason to use anything but the default local cache. The
1220             performance of memcached is worse than no caching at all for example.
1221              
1222             The method always returns with a correct array or throws an error.
1223              
1224             =cut
1225              
1226             sub parse_retrieve ($@);
1227              
1228             my %parsed_cache;
1229              
1230             sub parse ($%) {
1231 1130     1130 1 1633 my $self=shift;
1232 1130         2584 my $args=get_args(\@_);
1233              
1234 1130         19199 my $unparsed=$args->{'unparsed'};
1235              
1236 1130         1718 my $uncached=$args->{'xao.uncached'};
1237              
1238             # Preparing a short key that uniquely identifies the template given
1239             # (by either a path or an inline text). Uniqueness is only needed
1240             # within the site context. Global scope uniqueness is dealt with by
1241             # cache implementations below.
1242             #
1243 1130         1668 my $path;
1244             my $cache_key;
1245 1130 100       2143 if(defined($args->{'template'})) {
1246 920         1343 my $template=$args->{'template'};
1247              
1248 920 100       1877 if(ref($template)) {
1249 300         799 return $template; # Pre-parsed as an argument of some upper class
1250             }
1251              
1252 620 100       1865 my $tbytes=Encode::is_utf8($template) ? Encode::encode_utf8($template) : $template;
1253              
1254 620 100       1299 if(length $tbytes < 80) {
1255 565 100       1674 $cache_key=($unparsed ? 'T' : 't').':'.$tbytes;
1256             }
1257             else {
1258 55 50       472 $cache_key=($unparsed ? 'H' : 'h').':'.sha1_hex($tbytes);
1259             }
1260             }
1261             else {
1262 210   33     513 $path=$args->{'path'} ||
1263             throw $self "- no 'path' and no 'template' given to a Page object";
1264              
1265 210 100       606 $cache_key=($unparsed ? 'P' : 'p').':'.$path;
1266             }
1267              
1268             # Remembering the key if needed. It is used for benchmark cache.
1269             #
1270 830         1332 my $cache_key_ref=$args->{'cache_key_ref'};
1271 830 100       1640 $$cache_key_ref=$cache_key if $cache_key_ref;
1272              
1273             # Encoding also matters
1274             #
1275 830         1707 $cache_key.=':'.$self->_character_mode;
1276              
1277             # With uncached we don't even try to use any caches.
1278             #
1279 830         1376 my $parsed;
1280 830 50       1518 if($uncached) {
1281 0         0 $parsed=$self->parse_retrieve($args);
1282             }
1283              
1284             # Caching either locally, or in a standard cache
1285             #
1286             else {
1287              
1288             # Setup, only executed once.
1289             #
1290 830         1260 my $cache_name=$self->{'parse_cache_name'};
1291 830 100       1698 if(!defined $cache_name) {
1292 587   50     1134 $cache_name=$self->{'parse_cache_name'}=$self->siteconfig->get('/xao/page/parse_cache_name') || '';
1293             }
1294              
1295             # A fast totally local implementation.
1296             #
1297             # About two times faster than a memcached, but grows a template
1298             # cache per-process.
1299             #
1300 830 50       25717 if(!$cache_name) {
1301              
1302             # Making it unique per site
1303             #
1304 830   50     2799 my $sitename=$self->{'sitename'} || get_current_project_name() || '';
1305 830         4381 $cache_key=$sitename . ':' . $cache_key;
1306              
1307             # Checking if we have parsed and cached this before
1308             #
1309 830         1494 $parsed=$parsed_cache{$cache_key};
1310              
1311 830 100       2393 return $parsed if defined $parsed;
1312              
1313             # Reading and parsing.
1314             #
1315 276         634 $parsed=$self->parse_retrieve($args);
1316              
1317             # Caching the parsed template.
1318             #
1319 276         802 $parsed_cache{$cache_key}=$parsed;
1320              
1321             # Logging the size
1322             #
1323 276 50       575 if($self->debug_check('page-cache-size')) {
1324 0         0 $self->cache_show_size($cache_key);
1325             }
1326             }
1327              
1328             # More generic implementation that can be switched from local to
1329             # memcached to anything else
1330             #
1331             else {
1332 0         0 my $cache=$self->{'parse_cache_obj'};
1333 0 0       0 if(!$cache) {
1334 0         0 dprint "Using a named cache '$cache_name' for parsed templates";
1335              
1336 0         0 $cache=$self->{'parse_cache_obj'}=$self->siteconfig->cache(
1337             name => $cache_name,
1338             coords => [ 'cache_key' ],
1339             retrieve => \&parse_retrieve,
1340             );
1341             }
1342              
1343 0         0 $parsed=$cache->get($self,$args,{
1344             cache_key => $cache_key,
1345             force_update => $uncached,
1346             });
1347             }
1348             }
1349              
1350 276         581 return $parsed;
1351             }
1352              
1353             ###############################################################################
1354              
1355             sub parse_retrieve ($@) {
1356 276     276 0 422 my $self=shift;
1357 276         711 my $args=get_args(\@_);
1358              
1359 276         2535 my $path=$args->{'path'};
1360 276         432 my $template=$args->{'template'};
1361              
1362             # Reading and parsing.
1363             #
1364 276 100 66     749 if($path && !defined $template) {
1365 38 50       142 if($self->debug_check('show-read')) {
1366 0         0 dprint $self->objname."- read path='$path'";
1367             }
1368              
1369 38         225 $template=XAO::Templates::get(path => $path);
1370              
1371 38 50       118 defined($template) ||
1372             throw $self "- no template found (path=$path)";
1373             }
1374              
1375             # An unparsed template is very simple. But it might include binary
1376             # data. We don't encode/decode it regardless of encoding settings.
1377             #
1378 276 100       584 if($args->{'unparsed'}) {
1379 7         59 return [ { text => $template, binary => 1 } ];
1380             }
1381              
1382             # Logging the template or path if requested.
1383             #
1384 269 50       721 if($self->debug_check('show-parse')) {
1385 0 0       0 if($path) {
1386 0         0 dprint $self->objname."- parsing path='$path'"
1387             }
1388             else {
1389 0         0 my $te=substr($template,0,20);
1390 0         0 $te=~s/\r/\\r/sg;
1391 0         0 $te=~s/\n/\\n/sg;
1392 0         0 $te=~s/\t/\\t/sg;
1393 0 0       0 $te.='...' if length($template)>20;
1394 0         0 dprint $self->objname."- parsing template='$te'";
1395             }
1396             }
1397              
1398             # Parsing.
1399             #
1400 269         452 my $parsed;
1401 269 100       536 if($self->_character_mode) {
1402              
1403             # We might get a latin1 string like \xe9 that is meant to
1404             # be a Unicode, but is not. Unless all code is switched to
1405             # use 'unicode_strings' feature this can easily happen.
1406             #
1407             # BUT! We can also get an already UTF-8 encoded byte string,
1408             # in which case upgrade would break it.
1409             #
1410             # Using shameful black magic :(
1411             #
1412 49 100       162 if(!Encode::is_utf8($template)) {
1413 31         92 Encode::_utf8_on($template);
1414              
1415             # UTF-8 encoded bytes or plain ASCII
1416             #
1417 31 50       89 if(Encode::is_utf8($template,1)) {
1418             # No-op
1419             }
1420             else {
1421 0         0 Encode::_utf8_off($template);
1422 0         0 utf8::upgrade($template);
1423             }
1424             }
1425              
1426 49         475 $parsed=XAO::PageSupport::parse($template,1);
1427             }
1428             else {
1429 220         2457 $parsed=XAO::PageSupport::parse($template,0);
1430             }
1431              
1432             # If a scalar is returned it is an indicator of an error.
1433             #
1434 269 50       799 ref $parsed ||
1435             throw $self "- $parsed";
1436              
1437 269         530 return $parsed;
1438             }
1439              
1440             ###############################################################################
1441              
1442             =item object (%)
1443              
1444             Creates a new displayable object correctly tied to the current one. You
1445             should always get a reference to a displayable object by calling this
1446             method, not by using XAO::Objects' new() method. Currently most
1447             of the objects would work fine even if you do not, but this is not
1448             guaranteed.
1449              
1450             Possible arguments are (the same as for XAO::Objects' new method):
1451              
1452             =over
1453              
1454             =item objname => 'ObjectName'
1455              
1456             The name of an object you want to have an instance of. Default is
1457             'Page'. All objects are assumed to be in XAO::DO::Web namespace,
1458             prepending them with 'Web::' is optional.
1459              
1460             =item baseobj => 1
1461              
1462             If present then site specific object is ignored and system object is
1463             loaded.
1464              
1465             =back
1466              
1467             Example of getting Page object:
1468              
1469             sub display ($%) {
1470             my $self=shift;
1471             my $obj=$self->object;
1472             $obj->display(template => '<%Date%>');
1473             }
1474              
1475             Or even:
1476              
1477             $self->object->display(template => '<%Date%>');
1478              
1479             Getting FilloutForm object:
1480              
1481             sub display ($%) {
1482             my $self=shift;
1483             my $ff=$self->object(objname => 'FilloutForm');
1484             $ff->setup(...);
1485             ...
1486             }
1487              
1488             Object() method always returns object reference or throws an exception
1489             - meaning that under normal circumstances you do not need to worry
1490             about returned object correctness. If you get past the call to object()
1491             method then you have valid object reference on hands.
1492              
1493             =cut
1494              
1495             sub object ($%) {
1496 1953     1953 1 2780 my $self=shift;
1497 1953         4447 my $args=get_args(@_);
1498              
1499 1953   100     22617 my $objname=$args->{objname} || 'Page';
1500 1953 100       6328 $objname='Web::' . $objname unless substr($objname,0,5) eq 'Web::';
1501              
1502 1953         6252 XAO::Objects->new(
1503             objname => $objname,
1504             parent => $self,
1505             );
1506             }
1507              
1508             ###############################################################################
1509              
1510             =item textout ($)
1511              
1512             Displays a piece of text literally, without any changes.
1513              
1514             It used to be called as textout(text => "text") which is still
1515             supported for compatibility, but is not recommended any more. Call it
1516             with single argument -- text to be displayed.
1517              
1518             Example:
1519              
1520             $obj->textout("Text to be displayed");
1521              
1522             This method is the only place where text is actually gets displayed. You
1523             can override it if you really need some other output strategy for you
1524             object. Although it is not recommended to do so.
1525              
1526             =cut
1527              
1528             sub textout ($%) {
1529 6597     6597 1 13517 my $self=shift;
1530              
1531 6597 50       11694 return unless @_;
1532              
1533 6597         7902 my $text;
1534 6597 50       10238 if(@_ == 1) {
1535 6597         8950 $text=$_[0];
1536             }
1537             else {
1538 0         0 my %args=@_;
1539 0   0     0 $text=$args{'text'} // '';
1540             }
1541              
1542 6597 100       12232 if(Encode::is_utf8($text)) {
1543 74         307 XAO::PageSupport::addtext(Encode::encode_utf8($text));
1544             }
1545             else {
1546 6523         11971 XAO::PageSupport::addtext($text);
1547             }
1548             }
1549              
1550             ###############################################################################
1551              
1552             =item finaltextout ($)
1553              
1554             Displays some text and stops processing templates on all levels. No more
1555             objects should be called in this session and no more text should be
1556             printed.
1557              
1558             Used in Redirect object to break execution immediately for example.
1559              
1560             Accepts the same arguments as textout() method.
1561              
1562             =cut
1563              
1564             sub finaltextout ($%) {
1565 8     8 1 16 my $self=shift;
1566 8         29 $self->textout(@_);
1567 8         23 $self->clipboard->put(_no_more_output => 1);
1568             }
1569              
1570             ###############################################################################
1571              
1572             =item dbh ()
1573              
1574             Returns current database handler or throws an error if it is not
1575             available.
1576              
1577             Example:
1578              
1579             sub display ($%)
1580             my $self=shift;
1581             my $dbh=$self->dbh;
1582              
1583             # if you got this far - you have valid DB handler on hands
1584             }
1585              
1586             =cut
1587              
1588             sub dbh ($) {
1589 0     0 1 0 my $self=shift;
1590 0 0       0 return $self->{dbh} if $self->{'dbh'};
1591 0         0 $self->{dbh}=$self->siteconfig->dbh;
1592 0 0       0 return $self->{dbh} if $self->{dbh};
1593 0         0 throw $self "- no database connection";
1594             }
1595              
1596             ###############################################################################
1597              
1598             =item odb ()
1599              
1600             Returns current object database handler or throws an error if it is not
1601             available.
1602              
1603             Example:
1604              
1605             sub display ($%) {
1606             my $self=shift;
1607             my $odb=$self->odb;
1608              
1609             # ... if you got this far - you have valid DB handler on hands
1610             }
1611              
1612             =cut
1613              
1614             sub odb ($) {
1615 0     0 1 0 my $self=shift;
1616 0 0       0 return $self->{odb} if $self->{odb};
1617              
1618 0         0 $self->{odb}=$self->siteconfig->odb;
1619 0 0       0 return $self->{odb} if $self->{odb};
1620              
1621 0         0 throw $self "- requires object database connection";
1622             }
1623              
1624             ###############################################################################
1625              
1626             =item cache (%)
1627              
1628             A shortcut that actually calls $self->siteconfig->cache. See the
1629             description of cache() in L for more details.
1630              
1631             =cut
1632              
1633             sub cache ($%) {
1634 4     4 1 6424 my $self=shift;
1635 4         20 my $args=get_args(\@_);
1636 4         75 return $self->siteconfig->cache($args);
1637             }
1638              
1639             ###############################################################################
1640              
1641             =item cgi ()
1642              
1643             Returns CGI object reference (see L) or throws an error if it is
1644             not available.
1645              
1646             =cut
1647              
1648             sub cgi ($) {
1649 142     142 1 741 my $self=shift;
1650 142         278 $self->siteconfig->cgi;
1651             }
1652              
1653             ###############################################################################
1654              
1655             =item clipboard ()
1656              
1657             Returns clipboard object, which inherets XAO::SimpleHash methods. Use
1658             this object to pass data between various objects that work together to
1659             produce a page. Clipboard is cleaned before starting every new session.
1660              
1661             =cut
1662              
1663             sub clipboard ($) {
1664 3959     3959 1 6318 my $self=shift;
1665 3959         5649 my $clipboard=$self->{'clipboard'};
1666 3959 100       7395 if(!$clipboard) {
1667 997         1960 $clipboard=$self->{'clipboard'}=$self->siteconfig->clipboard;
1668             }
1669 3959         10024 return $clipboard;
1670             }
1671              
1672             ###############################################################################
1673              
1674             =item siteconfig ()
1675              
1676             Returns site configuration reference. Be careful with your changes to
1677             configuration, try not to change configuration -- use clipboard to pass
1678             data between objects. See L for more details.
1679              
1680             =cut
1681              
1682             sub siteconfig ($) {
1683 3370     3370 1 5621 my $self=shift;
1684 3370         4744 my $siteconfig=$self->{'siteconfig'};
1685 3370 100       6332 if(!$siteconfig) {
1686             $siteconfig=$self->{'siteconfig'}=
1687 1282 50       3360 $self->{'sitename'} ? get_project($self->{'sitename'})
1688             : get_current_project();
1689             }
1690 3370         78732 return $siteconfig;
1691             }
1692              
1693             ###############################################################################
1694              
1695             =item base_url (%)
1696              
1697             Returns base_url for secure or normal connection. Depends on parameter
1698             "secure" if it is set, or current state if it is not.
1699              
1700             If 'active' parameter is set then will return active URL, not the base
1701             one. In most practical cases active URL is the same as base URL except
1702             when your server is set up to answer for many domains. Base will stay
1703             at what is set in the site configuration and active will be the one
1704             taken from the Host: header.
1705              
1706             Examples:
1707              
1708             # Returns secure url in secure mode and normal
1709             # url in normal mode.
1710             #
1711             my $url=$self->base_url;
1712              
1713             # Return secure url no matter what
1714             #
1715             my $url=$self->base_url(secure => 1);
1716              
1717             # Return normal url no matter what
1718             #
1719             my $url=$self->base_url(secure => 0);
1720              
1721             # Return secure equivalent of the current active URL
1722             #
1723             my $url=$self->base_url(secure => 1, active => 1);
1724              
1725             =cut
1726              
1727             sub base_url ($;%) {
1728 48     48 1 72 my $self=shift;
1729 48         111 my $args=get_args(\@_);
1730              
1731 48         524 my $secure=$args->{secure};
1732 48 50       98 $secure=$self->is_secure unless defined $secure;
1733              
1734 48         66 my $active=$args->{active};
1735              
1736 48         58 my $url;
1737 48 100       84 if($secure) {
1738 26 100       69 $url=$active ? $self->clipboard->get('active_url_secure')
1739             : $self->siteconfig->get('base_url_secure');
1740             } else {
1741 22 100       58 $url=$active ? $self->clipboard->get('active_url')
1742             : $self->siteconfig->get('base_url');
1743             }
1744              
1745 48         1995 return $url;
1746             }
1747              
1748             ###############################################################################
1749              
1750             =item is_secure ()
1751              
1752             Returns 1 if the current the current connection is a secure one or
1753             0 otherwise. If there is a defined cgi() value then the result is
1754             defined by its https() method; otherwise the default is taken from
1755             /xao/page/default_https configuration variable. The later is useful for
1756             scripts that don't have a CGI environment.
1757              
1758             =cut
1759              
1760             sub is_secure ($) {
1761 31     31 1 52 my $self=shift;
1762 31         65 my $cgi=$self->cgi;
1763 31 50       70 if($cgi) {
1764 31 100       194 return $cgi->https() ? 1 : 0;
1765             }
1766             else {
1767 0 0       0 return $self->siteconfig->get('/xao/page/default_https') ? 1 : 0;
1768             }
1769             }
1770              
1771             ###############################################################################
1772              
1773             =item pageurl (%)
1774              
1775             Returns full URL of current page without parameters. Accepts the same
1776             arguments as base_url() method.
1777              
1778             =cut
1779              
1780             sub pageurl ($;%) {
1781 33     33 1 62 my $self=shift;
1782              
1783 33   33     54 my $pagedesc=$self->clipboard->get('pagedesc') ||
1784             throw $self "- no Web context, needs clipboard->'pagedesc'";
1785              
1786 33         1310 my $url=$self->base_url(@_);
1787              
1788             # This works in both CGI and PSGI environments, but simply
1789             # requesting $cgi->url(-absolute => 1) does not work for PSGI
1790             # because it sets PATH_INFO and REQUEST_URI to the same value,
1791             # making them cancel each other.
1792             #
1793 33         75 my $uri=$self->cgi->request_uri();
1794 33         286 $uri =~ s/\?.*$//s;
1795 33         85 $uri = $self->cgi->unescape($uri);
1796              
1797 33         555 return $url.$uri;
1798             }
1799              
1800             ###############################################################################
1801              
1802             sub _do_pass_args ($$$) {
1803 179     179   375 my ($self,$pargs,$spec)=@_;
1804              
1805 179         296 my $hash={ };
1806              
1807 179         347 foreach my $rule (@$spec) {
1808 212         1497 $rule=~s/^\s*(.*?)\s*$/$1/;
1809              
1810             ### dprint "...rule='$rule'";
1811              
1812 212 100       812 if($rule eq '*') {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    50          
1813 163         348 $hash=merge_refs($pargs,$hash);
1814             }
1815             elsif($rule =~ /^([\w\.]+)\s*=\s*([\w\.]+)$/) { # VAR=FOO
1816 8         29 $hash->{$1}=$pargs->{$2};
1817             }
1818             elsif($rule =~ /^([\w\.]*)\*([\w\.]*)\s*=\s*([\w\.]*)\*([\w\.]*)$/) {# VAR*=FOO* or *VAR=*FOO or V*R=T*Z or *=X*Z
1819 9         42 my ($prnew,$sufnew,$prold,$sufold)=($1,$2,$3,$4);
1820 9         184 my $re=qr/^\Q$prold\E(.*)\Q$sufold\E$/;
1821 9         48 foreach my $k (keys %$pargs) {
1822 63 100       226 next unless $k =~ $re;
1823 20         76 $hash->{$prnew.$1.$sufnew}=$pargs->{$k};
1824             }
1825             }
1826             elsif($rule =~ /^([\w\.]+)$/) { # VAR
1827 6         21 $hash->{$1}=$pargs->{$1};
1828             }
1829             elsif($rule =~ /^([\w\.]*)\*([\w\.]*)$/) { # VAR* or *VAR or VAR*FOO
1830 2         7 my ($pr,$suf)=($1,$2);
1831 2         27 my $re=qr/^\Q$pr\E(.*)\Q$suf\E$/;
1832 2         10 foreach my $k (keys %$pargs) {
1833 4 100       27 next unless $k =~ $re;
1834 2         11 $hash->{$k}=$pargs->{$k};
1835             }
1836             }
1837             elsif($rule =~ /^!([\w\.]+)$/) { # !VAR
1838 10         37 delete $hash->{$1};
1839             }
1840             elsif($rule =~ /^!([\w\.]*)\*([\w\.]*)$/) { # !VAR* or !*VAR or !VAR*FOO
1841 13         60 my ($pr,$suf)=($1,$2);
1842 13         206 my $re=qr/^\Q$pr\E(.*)\Q$suf\E$/;
1843 13         26 my @todel;
1844 13         37 foreach my $k (keys %$hash) {
1845 38 100       182 next unless $k =~ $re;
1846 18         43 push(@todel,$k);
1847             }
1848 13         22 delete @{$hash}{@todel};
  13         47  
1849             }
1850             elsif($rule eq '!*') {
1851 0         0 $hash={};
1852             }
1853             elsif($rule eq '') {
1854             # no-op
1855             }
1856             else {
1857 0         0 throw $self "- don't know how to pass for '$rule'";
1858             }
1859             }
1860              
1861 179         2394 return $hash;
1862             }
1863              
1864             ###############################################################################
1865              
1866             =item pass_args ($) {
1867              
1868             Helper method for supporting "pass" argument in web objects. Synopsis:
1869              
1870             $page->display($page->pass_args($args->{'pass'},$args),{
1871             path => $args->{'blah.path'},
1872             template => $args->{'blah.template'},
1873             FOO => 'bar',
1874             });
1875              
1876             If "pass" argument is not defined it will just return the original args,
1877             otherwise the following rules are supported:
1878              
1879             "on" or "1" - pass all arguments from parent object
1880             "VAR=FOO" - pass FOO from parent as VAR
1881             "VAR*=FOO*" - pass FOO* from parent renaming as VAR*
1882             "*=FOO*" - pass FOO* from parent stripping FOO
1883             "VAR" - pass only VAR from parent
1884             "VAR*" - pass only VAR* from parent
1885              
1886             Multiple pass specifications can be given with semi-colon delimiter.
1887              
1888             Several special tags are deleted from parent arguments: pass, path,
1889             template, and objname.
1890              
1891             =cut
1892              
1893             sub pass_args ($$;$) {
1894 170     170 1 450 my ($self,$pass,$args)=@_;
1895              
1896 170   100     473 $args||={ };
1897              
1898             # The first argument is the content of 'pass', if it's not defined
1899             # we return unadulteraded arguments.
1900             #
1901 170 100       404 return $args unless $pass;
1902              
1903             # If we don't have parent arguments then there is nothing to do.
1904             #
1905 150         227 my $pargs;
1906 150 50 33     592 if(!$self->{'parent'} || !($pargs=$self->{'parent'}->{'args'})) {
1907 0         0 return $args;
1908             }
1909              
1910             # Simplified (old) way of calling with just <%Page pass
1911             # template='xxx'%> would result in pass being 'on'.
1912             #
1913 150 100 66     379 if($pass eq 'on' || $pass eq '1') {
1914 135         218 $pass='*';
1915             }
1916              
1917             # Building inherited hash.
1918             #
1919 150         601 my $hash=$self->_do_pass_args($pargs,[split(/;/,$pass)]);
1920              
1921             # Always deleting pass, path and template
1922             #
1923 150         319 delete @{$hash}{'pass','objname','path','template'};
  150         383  
1924              
1925             # This is it, merging with the arguments given to us and returning
1926             #
1927 150         348 return merge_refs($hash,$args);
1928             }
1929              
1930             ###############################################################################
1931              
1932             sub benchmark_enabled ($) {
1933 1133     1133 0 1703 my $self=shift;
1934 1133 100       2036 $self->clipboard->get('_page_benchmark_enabled') || 0;
1935             }
1936              
1937             ###############################################################################
1938              
1939             sub _benchmark_hash ($) {
1940 195     195   259 my $self=shift;
1941              
1942 195         314 my $stats=$self->{'benchmark_stats'};
1943              
1944 195 100       372 if(!$stats) {
1945 18         52 $stats=$self->siteconfig->get('_page_benchmark_stats');
1946 18 100       810 if($stats) {
1947 17         53 $self->{'benchmark_stats'}=$stats;
1948             }
1949             else {
1950 1         3 $stats=$self->{'benchmark_stats'}={ };
1951 1         2 $self->siteconfig->put('_page_benchmark_stats' => $stats);
1952             }
1953             }
1954              
1955 195         357 return $stats;
1956             }
1957              
1958             ###############################################################################
1959              
1960             sub benchmark_tag_data ($$) {
1961 180     180 0 307 my ($self,$tag,$key)=@_;
1962              
1963 180 50       305 $tag || throw $self "- no 'tag'";
1964              
1965 180   100     342 $key||='-';
1966              
1967 180 50       312 ref $tag && throw $self "- tag '$tag' is not a scalar";
1968              
1969 180         315 my $stats=$self->_benchmark_hash();
1970              
1971 180         318 my $tagdata=$stats->{$tag};
1972              
1973 180 100       313 if(!$tagdata) {
1974 6         37 $tagdata=$stats->{$tag}={
1975             count => 0,
1976             total => 0,
1977             last => [ ],
1978             runs => { },
1979             };
1980             }
1981              
1982 180         319 my $rundata=$tagdata->{'runs'};
1983 180   100     492 $rundata->{$key}||={ };
1984 180         277 $rundata=$rundata->{$key};
1985              
1986 180 50       562 return wantarray ? ($tagdata,$rundata,$key) : $tagdata;
1987             }
1988              
1989             ###############################################################################
1990              
1991             =item benchmark_enter($;$$$)
1992              
1993             Start tracking the given tag execution time until benchmark_leave() is
1994             called on the same tag.
1995              
1996             An optional second argument can contain a unique key that identifies a
1997             specific run for the tag (in case of recurrent tag execution). The third
1998             optional argument is a description of this run.
1999              
2000             =cut
2001              
2002             sub benchmark_enter ($$;$$$) {
2003 90     90 1 252 my ($self,$tag,$key,$description,$cache_flag)=@_;
2004              
2005 90         124 my ($tagdata,$rundata);
2006 90         181 ($tagdata,$rundata,$key)=$self->benchmark_tag_data($tag,$key);
2007              
2008 90 50       258 if($rundata->{'started'}) {
2009 0         0 eprint "Benchmark for '$tag' (key '$key') not finished, discarding";
2010             }
2011              
2012 90   100     184 $description||='';
2013 90 100       236 $rundata->{'description'}=length $description > 100 ? substr($description,0,100) : $description;
2014              
2015 90 100       183 $rundata->{'cache_flag'}=$cache_flag ? 1 : 0;
2016              
2017 90         320 $rundata->{'started'}=[ gettimeofday ];
2018             }
2019              
2020             ###############################################################################
2021              
2022             =item benchmark_leave ($)
2023              
2024             Stop time tracking for the given tag and record tracking results in the
2025             history.
2026              
2027             =cut
2028              
2029             sub benchmark_leave ($$;$$) {
2030 90     90 1 209 my ($self,$tag,$key,$content_digest)=@_;
2031              
2032 90         120 my ($tagdata,$rundata);
2033 90         216 ($tagdata,$rundata,$key)=$self->benchmark_tag_data($tag,$key);
2034              
2035             ### dprint to_json($tagdata);
2036              
2037 90         160 my $started=$rundata->{'started'};
2038 90 50       173 if(!$started) {
2039 0         0 eprint "Benchmark for '$tag' (key '$key') was not started";
2040 0         0 return;
2041             }
2042              
2043 90         253 my $taken=tv_interval($started);
2044              
2045             # For median calculation
2046             #
2047 90         1355 my $last=$tagdata->{'last'};
2048 90         210 push(@$last,$taken);
2049 90 50       190 shift(@$last) if scalar(@$last) > 50;
2050              
2051 90         138 ++$tagdata->{'count'};
2052 90         137 ++$rundata->{'count'};
2053              
2054 90         149 $tagdata->{'total'}+=$taken;
2055 90         147 $rundata->{'total'}+=$taken;
2056              
2057             # Remembering the content for cacheability analysis.
2058             #
2059 90   100     165 $content_digest||='-';
2060 90         211 ++$rundata->{'content'}->{$content_digest};
2061              
2062             # Resetting for the next run
2063             #
2064 90         211 $rundata->{'started'}=undef;
2065             }
2066              
2067             ###############################################################################
2068              
2069             =item benchmark_reset()
2070              
2071             Clear all benchmarking statistics accumulated so far.
2072              
2073             =cut
2074              
2075             sub benchmark_reset ($) {
2076 0     0 1 0 my $self=shift;
2077 0         0 %{$self->_benchmark_hash()}=();
  0         0  
2078             }
2079              
2080             ###############################################################################
2081              
2082             =item benchmark_start(;$)
2083              
2084             Start automatic system-wide page rendering benchmarking.
2085              
2086             By default only 'path' based rendering is benchmarked. If an optional
2087             single argument is set to '2' then templates are also benchmarked (this
2088             may demand a lot of extra memory!).
2089              
2090             =cut
2091              
2092             sub benchmark_start ($;$) {
2093 2     2 1 4 my ($self,$level)=@_;
2094 2   50     7 $self->clipboard->put('_page_benchmark_enabled' => ($level || 1));
2095             }
2096              
2097             ###############################################################################
2098              
2099             =item benchmark_stop()
2100              
2101             Stop automatic system-wide rendering benchmarking.
2102              
2103             =cut
2104              
2105             sub benchmark_stop ($) {
2106 1     1 1 2 my $self=shift;
2107 1         2 $self->clipboard->put('_page_benchmark_enabled' => 0);
2108             }
2109              
2110             ###############################################################################
2111              
2112             =item benchmark_stats
2113              
2114             Return a hash with accumulated benchmark statistics.
2115              
2116             =cut
2117              
2118             sub benchmark_stats ($;$) {
2119 15     15 1 1637 my ($self,$desired_tag)=@_;
2120              
2121 15         36 my $stats=$self->_benchmark_hash();
2122              
2123 15         24 my %analyzed;
2124              
2125 15         38 foreach my $tag (keys %$stats) {
2126 79         105 my $d=$stats->{$tag};
2127 79 50       212 next unless $d->{'count'};
2128 79 100 100     173 next if $desired_tag && $tag ne $desired_tag;
2129              
2130 55         96 $d->{'average'}=$d->{'total'} / $d->{'count'};
2131 55         67 $d->{'median'}=$d->{'last'}->[scalar(@{$d->{'last'}})/2];
  55         115  
2132              
2133             # The page is cacheable if the content only depends on
2134             # parameters and not on clipboard, cookies, CGI, time, or other
2135             # environment.
2136             #
2137             $d->{'cacheable'}=scalar(grep {
2138 248         290 scalar(keys %{$d->{'runs'}->{$_}->{'content'}}) != 1
  248         605  
2139 55 100       69 } keys %{$d->{'runs'}}) ? 0 : 1;
  55         179  
2140              
2141             # Current cacheable flag, if it's shared across all runs
2142             #
2143             $d->{'cache_flag'}=scalar(grep {
2144 248         448 ! $d->{'runs'}->{$_}->{'cache_flag'}
2145 55 100       81 } keys %{$d->{'runs'}}) ? 0 : 1;
  55         105  
2146              
2147 55         133 $analyzed{$tag}=$d;
2148             }
2149              
2150             ### dprint to_json(\%analyzed,{ utf8 => 1, canonical => 1, pretty => 1 });
2151              
2152 15         61 return \%analyzed;
2153             }
2154              
2155             ###############################################################################
2156              
2157             sub cache_show_size ($$) {
2158 0     0 0 0 my ($self,$path)=@_;
2159              
2160 0         0 eval {
2161 0         0 require Devel::Size;
2162             };
2163              
2164 0 0       0 if($@) {
2165 0         0 eprint "Devel::Size not available, disabling debug 'page-cache-size'";
2166 0         0 $self->debug_set('cache-size' => 0);
2167 0         0 return;
2168             }
2169              
2170 0         0 my $size=Devel::Size::total_size(\%parsed_cache);
2171              
2172 0         0 eprint "Web::Page cache size ".sprintf('%.3f',$size/1024.0)." KB - ",$path;
2173             }
2174              
2175             ###############################################################################
2176              
2177             sub debug_check ($$) {
2178 602     602 0 1280 my ($self,$type)=@_;
2179              
2180             # This is a speed up (makes the parsing more than twice faster when a
2181             # local parsing cache is also used).
2182             #
2183             # 8 wallclock secs ( 8.78 usr + 0.01 sys = 8.79 CPU) @ 113765.64/s (n=1000000)
2184             # 19 wallclock secs (18.97 usr + 0.00 sys = 18.97 CPU) @ 52714.81/s (n=1000000)
2185             #
2186             ### return $self->clipboard->get("debug/Web/Page/$type");
2187              
2188 602         1038 my $debug_hash=$self->{'debug_hash'};
2189              
2190 602 100       1093 if(!$debug_hash) {
2191 171         348 $debug_hash=$self->clipboard->get('/debug/Web/Page');
2192 171 100       8307 if($debug_hash) {
2193 78         187 $self->{'debug_hash'}=$debug_hash;
2194             }
2195             else {
2196 93         315 $self->{'debug_hash'}=$debug_hash={ };
2197 93         219 $self->clipboard->put('/debug/Web/Page' => $debug_hash);
2198             }
2199             }
2200              
2201 602         5331 return $debug_hash->{$type};
2202             }
2203              
2204             ###############################################################################
2205              
2206             sub debug_set ($%) {
2207 3     3 0 4 my $self=shift;
2208 3         16 my $args=get_args(\@_);
2209 3         30 foreach my $type (keys %$args) {
2210 4 100       56 $self->clipboard->put("/debug/Web/Page/$type",$args->{$type} ? 1 : 0);
2211             }
2212             }
2213              
2214             ###############################################################################
2215              
2216             sub page_clipboard ($) {
2217 1235     1235 0 1894 my $self=shift;
2218              
2219 1235         1966 my $cb_hash=$self->{'page_clipboard'};
2220              
2221 1235 100       2467 if(!$cb_hash) {
2222 874         1790 $cb_hash=$self->clipboard->get('/xao/page');
2223 874 100       50282 if($cb_hash) {
2224 732         1490 $self->{'page_clipboard'}=$cb_hash;
2225             }
2226             else {
2227 142         396 $self->{'page_clipboard'}=$cb_hash={ };
2228 142         306 $self->clipboard->put('/xao/page' => $cb_hash);
2229             }
2230             }
2231              
2232 1235         7726 return $cb_hash;
2233             }
2234              
2235             ###############################################################################
2236             1;
2237             __END__