The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

XAO::DO::Web::Page - core object of XAO::Web rendering system

SYNOPSIS

Outside web environment:

 my $page=XAO::Objects->new(objname => 'Page');
 my $date=$page->expand(template => '<%Date%>');

Inside XAO::Web template:

 <%Page path="/bits/some-path" ARG={<%SomeObject/f%>}%>

DESCRIPTION

As XAO::DO::Web::Page object (from now on just Page displayable object) is the core object for XAO::Web web rendering engine we will start with basics of how it works.

The goal of XAO::Web rendering engine is to produce HTML data file that can be understood by browser and displayed to a user. It will usually use database tables, templates and various displayable objects to achieve that.

Every time a page is requested in someone's web browser a XAO::Web handler gets executed, prepares site configuration, opens database connection, determines what would be start object and/or start path and does a lot of other useful things. If you have not read about it yet it is suggested to do so -- see XAO::Web::Intro and XAO::Web.

Although XAO::Web handler can call arbitrary object with arbitrary arguments to produce an HTML page we will assume the simplest scenario of calling Page object with just one argument -- path to an HTML file template for simplicity (another way to pass some template to a Page object is to pass argument named "template" with the template text as the value). This is the default behavior of XAO::Web handler if you do not override it in configuration.

Let's say user asked for http://oursite.com/ and XAO::Web translated that into the call to Page's display method with "path" argument set to "/index.html". All template paths are treated relative to "templates" directory in site directory or to system-wide "templates" directory if site-specific template does not exist. Suppose templates/index.html file in our site's home directory contains the following:

  Hello, World!

As there are no special symbols in that template Page's display method will return exactly that text without any changes (it will also cache pre-parsed template for re-use under mod_perl, but this is irrelevant for now).

Now let's move to a more complex example -- suppose we want some kind of header and footer around our text:

  <%Page path="/bits/header-template"%>

  Hello, World!

  <%Page path="/bits/footer-template"%>

Now, Page's parser sees reference to other items in that template - these things, surrounded by <% %> signs. What it does is the following.

First it checks if there is an argument given to original Page's display() method named 'Page' (case sensitive). In our case there is no such argument present.

Then, as no such static argument is found, it attempts to load an object named 'Page' and pass whatever arguments given to that object's display method.

NOTE: it is recommended to name static arguments in all-lowercase (for standard parameters accepted by an object) or all-uppercase (for parameters that are to be included into template literally) letters to distinguish them from object names where only the first letter of every word is capitalized.

In our case Page's parser will create yet another instance of Page displayable object and pass argument "path" with value "/bits/header-template". That will include the content of templates/bits/header-template file into the output. So, if the content of /bits/header-template file is:

  <HTML><BODY BGCOLOR="#FFFFFF">

And the content of /bits/footer-template is:

  </BODY></HTML>

Then the output produced by the original Page's display would be:

  <HTML><BODY BGCOLOR="#FFFFFF">

  Hello, World!

  </BODY></HTML>

For the actual site you might opt to use specific objects for header and footer (see XAO::DO::Web::Header and XAO::DO::Web::Footer):

  <%Header title="My first XAO::Web page"%>

  Hello, World!

  <%Footer%>

Page's parser is not limited to only these simple cases, you can embed references to variables and objects almost everywhere. In the following example Utility object (see XAO::DO::Web::Utility) is used to build complete link to a specific page:

  <A HREF="<%Utility mode="base-url"%>/somepage.html">blah blah blah</A>

If current (configured or guessed) site URL is "http://demosite.com/" this template would be translated into:

  <A HREF="http://demosite.com/somepage.html">blah blah blah</A>

Even more interesting is that you can use embedding to create arguments for embedded objects:

  <%Date gmtime={<%CgiParam param="shippingtime" default="0"%>}%>

If your page was called with "shippingtime=984695182" argument in the query then this code would expand to (in PST timezone):

  Thu Mar 15 14:26:22 2001

As you probably noticed, in the above example argument value was in curly brackets instead of quotes. Here are the options for passing values for objects' arguments:

  1. You can surround value with double quotes: name="value". This is recommended for short strings that do not include any " characters.

  2. You can surround value with matching curly brackets. Curly brackets inside are allowed and counted so that these expansions would work:

     name={Some text with " symbols}
    
     name={Multiple
           Lines}
    
     name={something <%Foo bar={test}%> alsdj}

    The interim brackets in the last example would be left untouched by the parser. Although this example won't work because of unmatched brackets:

     name={single { inside}

    See below for various ways to include special symbols inside of arguments.

  3. Just like for HTML files if the value does not include any spaces or special symbols quotes can be left out:

     number=123

    But it is not recommended to use that method and it is not guaranteed that this will remain legal in future versions. Kept mostly for compatibility with already deployed code.

  4. To pass a string literally without performing any substitutions you can use single quotes. For instance:

     <%FS
       uri="/Members/<%MEMBER_ID/f%>"
       mode="show-hash"
       fields="*"
       template='<%MEMBER_AGE/f%> -- <%MEMBER_STATUS/f%>'
     %>

    If double quotes were used in this example then the parser would try to expand <%MEMBER_AGE%> and <%MEMBER_STATUS%> variables using the current object arguments which is not what is intended. Using single quotes it is possible to let FS object do the expansion and therefore insert database values in this case.

  5. To pass multiple nested arguments literally or to include a single quote into the string matching pairs of {' and '} can be used:

     <%FS
       uri="/Members/<%MEMBER_ID/f%>"
       mode="show-hash"
       fields="*"
       template={'Member's age is <%MEMBER_AGE/f%>'}
     %>

BINARY vs UNICODE

By default expanded templates and object arguments are bytes, not Unicode characters. This does not mean that they cannot be used in Unicode (or other encoding) web sites, but it does mean that objects need to convert arguments and expansion results into characters where and as needed.

Starting with version 1.48 it is possible to switch the engine into returning perl characters from template expansion and for passing characters in object arguments. This is achieved by setting /xao/page/character_mode configuration parameter to '1'.

One exception allowing to retrieve byte data is when a template is expanded with 'unparsed' qualifier. This is typically used to pass binary data such as images and spreadsheets to objects such as XAO::DO::Web::Mailer.

There is an important caveat for using 'unparsed' with binary data in character mode. A template like the one below wou pass perl characters in 'data' argument when executing Process.

  <%Process
    data={<%Page
            path='/binary/data.bin'
            unparsed
          %>}
  %>

There is no way to mix byte and character processing mode. Pick one depending on your needs. Character mode allows a more natural processing in Perl while byte mode allows more granular control over the data flow.

EMBEDDING SPECIAL CHARACTERS

Sometimes it is necessary to include various special symbols into argument values. This can be done in the same way you would embed special symbols into HTML tags arguments:

  • By using &tag; construction, where tag could be "quot", "lt", "gt" and "amp" for double quote, left angle bracket, right angle bracket and ampersand respectfully.

  • By using &#NNN; construction where NNN is the decimal code for the corresponding symbol. For example left curly bracket could be encoded as &#123; and right curly bracket as &#125;. The above example should be re-written as follows to make it legal:

     name={single &#123; inside}

OUTPUT CONVERSION

As the very final step in the processing of an embedded object or variable the parser will check if it has any flags and convert it accordingly. This can (and should) be used to safely pass special characters into fields, HTML documents and so on.

For instance, the following code might break if you do not use flags and variable will contain a duoble quote character in it:

 <INPUT TYPE="TEXT" VALUE="<$VALUE$>">

Correct way to write it would be (note /f after VALUE):

 <INPUT TYPE="TEXT" VALUE="<$VALUE/f$>">

Generic format for specifying flags is:

 <%Object/x ...%> or <$VARIABLE/x$>

Where 'x' could be one of:

f

Converts text for safe use in HTML elements attributes. Mnemonic for remembering - (f)ield.

Will convert '123"234' into '123&quot;234'.

h

Converts text for safe use in HTML text. Mnemonic - (H)TML.

Will convert '123<BR>234' into '123&lt;BR&gt;234'.

q

Converts text for safe use in HTML query parameters. Mnemonic - (q)uery.

Will convert '123 234' into '123%20234'.

Example: <A HREF="test.html?name=<$VAR/q$>">Test '<$VAR/h$>'</A>

s

The same as 'h' excepts that it translates empty string into '&nbsp;'. Suitable for inserting pieces of text into table cells.

u

The same as 'q'. Mnemonic - (U)RL, as it can be used to convert text for inclusion into URLs.

It is a very good habit to use flags as much as possible and always specify a correct conversion. Leaving output untranslated may lead to anything from broken HTML to security violations.

LEVELS OF PARSING

Arguments can include as many level of embedding as you like, but you must remember:

  1. That all embedded arguments are expanded from the deepest level up to the top before executing main object.

  2. That undefined references to either non-existing object or non-existing variable produce a run-time error and the page is not shown.

  3. All embedded arguments are processed in the same arguments space that the template one level up from them.

As a test of how you understood everything above please attempt to predict what would be printed by the following example (after reading XAO::DO::Web::SetArg or guessing its meaning). The answer is about one page down, at the end of this chapter.

 <%SetArg name="V1" value="{}"%>
 <%SetArg name="V2" value={""}%>
 <%Page template={<%V1%><%V2%>
 <%Page template={<%SetArg name="V2" value="[]" override%><%V2%>}%>
 <%V2%><%V1%>}
 %>

In most cases it is not recommended to make complex inline templates though. It is usually better to move a sub-template into a separate file and include it by passing a 'path' parameter into Page. Usually it is also more efficient because templates with known paths are cached in parsed state the first time they are used while inlined templates are parsed every time.

It is usually a good idea to make templates as simple as possible and move most of the logic inside of objects. To comment what you're doing in various parts of template you can use normal HTML-style comments. They are removed from the output completely, so you can include any amount of text inside of comments -- it won't impact the size of final HTML file. Here is an example:

 <!-- Header section -->
 <%Header title="demosite.com"%>
 <%Page path="/bits/menu"%>

 <!-- Main part -->
 <%Page path="/bits/body"%>

 <!-- Footer -->
 <%Footer%>

One exception is JavaScript code which is usually put into comments. The parser will NOT remove comments if the opening comment is <!--//. Here is an example of JavaScript code:

 <script type="text/javascript"><!--//
 function foo ()
 { alert("bar");
 }
 //-->
 </script>

CACHING

Parsed templates are always cached either locally or using a configured cache. The cache is keyed on 'path' or 'template' parameters value (two identical 'template's will only parse once). Parse cache can be disabled by giving a "xao.uncached" parameter. See parse() method description for details.

The fully rendered content can also be cached if a couple of conditions are met:

  • /xao/page/render_cache_name in the config -- this should contain a name of the cache to be used for rendered page components.

  • The page is configured to be cacheable with either an entry in the configuration under '/xao/page/render_cache_allow' or with a 'xao.cacheable' parameter given (e.g. something like <%Page ... xao.cacheable%>).

  • There is no "/xao/page/render_cache_update" in the clipboard. This can be used to force cache reload by checking some environmental variable early in the flow and setting the clipboard to disable all render caches for that one render. Cached content is not used, but is updated -- so subsequent cached calls with the same parameters will return new content.

  • There is no "/xao/page/render_cache_skip" in the clipboard. This can be used to skip cache altogether if it is known that pages rendered in this session are different from cached and the cache does not want to be contaminated with them.

Properly used render cache can speed up pages significantly, but if used incorrectly it can also introduce very hard to find issues in the rendered content.

Carefully consider what pages to tag with "cacheable" tag. Benchmarking reports can be of great help for that.

Entries in the config /xao/page/render_cache_allow may include additional specifications for what parameters are checked when rendered content is cached. By default, if the value is '1' or 'on' all of Page template parameters are checked, but none of CGI or cookies. Values for parameters 'path' and 'template' are always checked, regardless of the configuration.

The configuration can look like this:

 xao => {
    page => {
        render_cache_name   => 'xao_page_render',
        render_cache_allow  => {
            'p:/bits/complex-template'  => 1,
            'p:/bits/complex-cgi'       => {
                param   => [ '*' ],
                cgi     => [ 'cf*' ],
            },
            'p:/bits/complex-cookie'    => {
                param   => [ '*', '!session*' ],
                cookie  => [ 'session' ],
            },
        },
    },
 }

BENCHMARKING

Benchmarking can be started and stopped by using benchmark_start() and benchmark_stop() calls. The hash with current benchmarking data can be retrieved with benchmark_stats() call.

When benchmarking is started all rendered paths (and optionally all templates) are timed and are also analyzed for potential cacheability -- if rendered content is repeatedly the same for some set of parameters.

Custom execution paths spanning multiple templates can be tracked by using benchmark_enter($tag) and benchmark_leave($tag) calls.

The data is "static", not specific to a particular Page object.

Benchmarking slows down processing. Do not use it in production.

For an easy way to control benchmarking from templates use <%Benchmark%> object.

NOTE FOR HARD-BOILED HACKERS

If you do not like something in the parser behavior you can define site-specific Page object and refine or replace any methods of system Page object. Your new object would then be used by all system and site-specific objects for your site and won't impact any other sites installed on the same host. But this is mentioned here merely as a theoretical possibility, not as a good thing to do.

TEST OUTPUT

The output of the test above would be:

 {}""
 []
 ""{}

In fact first two SetArg's would add two empty lines in front because they have carriage returns after them, but this is only significant if your HTML code is space-sensitive.

METHODS

Publicly accessible methods of Page (and therefor of all objects derived from Page unless overwritten) are:

display (%)

Displays given template to the current output buffer. The system uses buffers to collect all text displayed by various objects in a rather optimal way using XAO::PageSupport (see XAO::PageSupport) module. In XAO::Web handler the global buffer is initialized and after all displayable objects have worked their way it retrieves whatever was accumulated in that buffer and displays it.

This way you do not have to think about where your output goes as long as you do not "print" anything by yourself - you should always call either display() or textout() to print any piece of text.

Display() accepts the following arguments:

pass

Passes arguments from calling context into the template.

The syntax allows to map parent arguments into new names, and/or to limit what is passed. Multiple semi-colon separated rules are allowed. Rules are processed from left to right.

  NEWNAME=OLDNAME  - pass the value of OLDNAME as NEWNAME
  NEW*=OLD*        - pass all old values starting with OLD as NEW*
  VAR;VAR.*        - pass VAR and VAR.* under their own names
  *;!VAR*          - pass everything except VAR*

The default, when the value of 'pass' is 'on' or '1', is the same as passing '*' -- meaning that all parent arguments are passed literally under their own names.

There are exceptions, that are never passed from parent arguments: 'pass', 'objname', 'path', and 'template'.

Arguments given to display() override those inherited from the caller using 'pass'.

path => 'path/to/the/template'

Gives Page a path to the template that should be processed and displayed.

template => 'template text'

Provides Page with the actual template text.

unparsed => 1

If set it does not parse template, just displays it literally.

Any other argument given is passed into template unmodified as a variable. Remember that it is recommended to pass variables using all-capital names for better visual recognition.

Example:

 $obj->display(path => "/bits/left-menu", ITEM => "main");

For security reasons it is also recommended to put all sub-templates into /bits/ directory under templates tree or into "bits" subdirectory of some tree inside of templates (like /admin/bits/admin-menu). Such templates cannot be displayed from XAO::Web handler by passing their path in URL.

expand (%)

Returns a string corresponding to the expanded template. Accepts exactly the same arguments as display(). Here is an example:

 my $str=$obj->expand(template => '<%Date%>');
parse ($%)

Takes template from either 'path' or 'template' and parses it. If given the following template:

    Text <%Object a=A b="B" c={X<%C/f ca={CA}%>} d='D' e={'<$E$>'}%>

It will return a reference to an array of the following structure:

    [   {   text    => 'Text ',
        },
        {   objname => 'Object',
            args    => {
                a => [
                    {   text    => 'A',
                    },
                ],
                b => [
                    {   text    => 'B',
                    },
                ],
                c => [
                    {   text    => 'X',
                    },
                    {   objname => 'C',
                        flag    => 'f',
                        args    => {
                            ca => [
                                {   text    => 'CA',
                                },
                            ],
                        },
                    },
                ],
                d => 'D',
                e => '<$E$>',
            },
        },
    ]

With "unparsed" parameter the content of the template is not analyzed and is returned as a single 'text' node.

Templates are only parsed once, unless an "xao.uncached" parameter is set to true.

Normally the parsed templates cache uses a local perl hash. If desired a XAO::Cache based implementation can be used by setting /xao/page/parse_cache_name parameter in the site configuration to the desired cache name (e.g. "xao_parse_cache").

Statistics of various ways of calling:

    memcached-cache-path       1866/s
    memcached-cache-template   2407/s
    no-cache-path              5229/s
    no-cache-template          5572/s
    memory-cache-template     26699/s
    memory-cache-path         45253/s
    local-cache-template      49681/s
    local-cache-path         149806/s

Unless the site has a huge number of templates there is really no compelling reason to use anything but the default local cache. The performance of memcached is worse than no caching at all for example.

The method always returns with a correct array or throws an error.

object (%)

Creates a new displayable object correctly tied to the current one. You should always get a reference to a displayable object by calling this method, not by using XAO::Objects' new() method. Currently most of the objects would work fine even if you do not, but this is not guaranteed.

Possible arguments are (the same as for XAO::Objects' new method):

objname => 'ObjectName'

The name of an object you want to have an instance of. Default is 'Page'. All objects are assumed to be in XAO::DO::Web namespace, prepending them with 'Web::' is optional.

baseobj => 1

If present then site specific object is ignored and system object is loaded.

Example of getting Page object:

 sub display ($%) {
     my $self=shift;
     my $obj=$self->object;
     $obj->display(template => '<%Date%>');
 }

Or even:

 $self->object->display(template => '<%Date%>');

Getting FilloutForm object:

 sub display ($%) {
     my $self=shift;
     my $ff=$self->object(objname => 'FilloutForm');
     $ff->setup(...);
     ...
  }

Object() method always returns object reference or throws an exception - meaning that under normal circumstances you do not need to worry about returned object correctness. If you get past the call to object() method then you have valid object reference on hands.

textout ($)

Displays a piece of text literally, without any changes.

It used to be called as textout(text => "text") which is still supported for compatibility, but is not recommended any more. Call it with single argument -- text to be displayed.

Example:

 $obj->textout("Text to be displayed");

This method is the only place where text is actually gets displayed. You can override it if you really need some other output strategy for you object. Although it is not recommended to do so.

finaltextout ($)

Displays some text and stops processing templates on all levels. No more objects should be called in this session and no more text should be printed.

Used in Redirect object to break execution immediately for example.

Accepts the same arguments as textout() method.

dbh ()

Returns current database handler or throws an error if it is not available.

Example:

 sub display ($%)
     my $self=shift;
     my $dbh=$self->dbh;

     # if you got this far - you have valid DB handler on hands
 }
odb ()

Returns current object database handler or throws an error if it is not available.

Example:

 sub display ($%) {
     my $self=shift;
     my $odb=$self->odb;

     # ... if you got this far - you have valid DB handler on hands
 }
cache (%)

A shortcut that actually calls $self->siteconfig->cache. See the description of cache() in XAO::DO::Web::Config for more details.

cgi ()

Returns CGI object reference (see CGI) or throws an error if it is not available.

clipboard ()

Returns clipboard object, which inherets XAO::SimpleHash methods. Use this object to pass data between various objects that work together to produce a page. Clipboard is cleaned before starting every new session.

siteconfig ()

Returns site configuration reference. Be careful with your changes to configuration, try not to change configuration -- use clipboard to pass data between objects. See XAO::Projects for more details.

base_url (%)

Returns base_url for secure or normal connection. Depends on parameter "secure" if it is set, or current state if it is not.

If 'active' parameter is set then will return active URL, not the base one. In most practical cases active URL is the same as base URL except when your server is set up to answer for many domains. Base will stay at what is set in the site configuration and active will be the one taken from the Host: header.

Examples:

 # Returns secure url in secure mode and normal
 # url in normal mode.
 #
 my $url=$self->base_url;

 # Return secure url no matter what
 #
 my $url=$self->base_url(secure => 1);

 # Return normal url no matter what
 #
 my $url=$self->base_url(secure => 0);

 # Return secure equivalent of the current active URL
 #
 my $url=$self->base_url(secure => 1, active => 1);
is_secure ()

Returns 1 if the current the current connection is a secure one or 0 otherwise.

pageurl (%)

Returns full URL of current page without parameters. Accepts the same arguments as base_url() method.

pass_args ($) {

Helper method for supporting "pass" argument in web objects. Synopsis:

    $page->display($page->pass_args($args->{'pass'},$args),{
        path        => $args->{'blah.path'},
        template    => $args->{'blah.template'},
        FOO         => 'bar',
    });

If "pass" argument is not defined it will just return the original args, otherwise the following rules are supported:

    "on" or "1"     - pass all arguments from parent object
    "VAR=FOO"       - pass FOO from parent as VAR
    "VAR*=FOO*"     - pass FOO* from parent renaming as VAR*
    "*=FOO*"        - pass FOO* from parent stripping FOO
    "VAR"           - pass only VAR from parent
    "VAR*"          - pass only VAR* from parent

Multiple pass specifications can be given with semi-colon delimiter.

Several special tags are deleted from parent arguments: pass, path, template, and objname.

benchmark_enter($;$$$)

Start tracking the given tag execution time until benchmark_leave() is called on the same tag.

An optional second argument can contain a unique key that identifies a specific run for the tag (in case of recurrent tag execution). The third optional argument is a description of this run.

benchmark_leave ($)

Stop time tracking for the given tag and record tracking results in the history.

benchmark_start(;$)

Start automatic system-wide page rendering benchmarking.

By default only 'path' based rendering is benchmarked. If an optional single argument is set to '2' then templates are also benchmarked (this may demand a lot of extra memory!).

benchmark_stop()

Stop automatic system-wide rendering benchmarking.

benchmark_stats

Return a hash with accumulated benchmark statistics.

EXPORTS

Nothing.

AUTHOR

Copyright (c) 2005 Andrew Maltsev

Copyright (c) 2001-2004 Andrew Maltsev, XAO Inc.

<am@ejelta.com> -- http://ejelta.com/xao/

SEE ALSO

Recommended reading: XAO::Web, XAO::Objects, XAO::Projects, XAO::Templates. XAO::DO::Web::Benchmark.