E-Commerce Solutions: Template-Driven Pages, Part 2

Wednesday Sep 13th 2000 by Martin Brown
Share:

If you're not working with templates, you should be -- templates are an easy way to impose order on sometimes-sprawling Web sites. In this article, Martin C. Brown will describe how to use Perl and Python to deliver SSI templates straight into CGI documents. straight into CGI documents.

First of all, I feel I should apologize to those readers who've been awaiting the follow-up to my original article. Unfortunately, pressures of work on other projects has meant that I've been unable to switch my attention to any other projects, including the E-Commerce series. However, I'm now back on track, so here we go with the follow-up on template-driven pages, this time using Perl and Python to deliver those SSI templates straight into CGI documents.

In the last article we examined the different ways in which we could introduce pages purely using the SSI (Server Side Includes) features of Apache. The problem with SSI is that it doesn't apply to pages that have been served by a CGI script. Instead, you need to manually read and regurgitate the template for yourself within the script or CGI service that you are using.

You don't actually need to use a different template, you can continue to use the SSI templates that we've seen before, as long as you keep to the same rules as you would with a server-parsed HTML file.

The obvious way of outputting an HTML file is just to open the file, slurp in the text, and print it out. But, if we're going to go to all this trouble, why don't we do a little more than just regurgitate the text?

Using Perl

Perl is ideally suited to reading in entire files, doing a bit of processing, and then print it out again. I have for years now used the same basic function of reproducing templates in Perl. The function looks like this:

sub parse_template
{
    my (,%subs) = @_;

    open(TEMPLATE,)
        or print "I tried to load <br>\n";
    {
        local $/;
        sh = <TEMPLATE>;
    }
    close(TEMPLATE);

    foreach  (sort keys %subs)
    {
        sh =~ s/\%\%\%\%/{}/g;
    }
    return sh;
}

There are a few points to note about this function before we look at how best to use it. First and foremost, you'll notice that we load the entire template file into memory. This is because we want to process the file in it's entirety. The second point is that we don't actually print the template from within the function, instead, we return the translated text to the caller. This is just in case we want to use the template for something other than an active HTML page generated by a CGI script. We could use the same function to introduce templates into a static HTML file, whilst still allowing us to reproduce and parse the template in the process.

The third point is just a small nicety. If the file that's been selected doesn't exist, we print a little message to say that there's been an error. We could equally return nothing, but I prefer to be able to spot the problem. In production systems, I've actually used an SSI type error message, and also taken the time to mail an error message to the webmaster to highlight a possible problem.

Now for the important part. The second half of the function actually processes the template so that we can embed elements into the templates that can be replaced on the fly. We replace strings of the form %%string%% by using a hash which we supply to the function. The key of the hash is the string, and the value is the replacement text. For example, take the simple template:


<title>%%title%%</title>

Using the function above we can print out the template using:

print parse_template('template','title' => 'This is the title text');

This will produce the desired:

<title>This is the title text</title>

You can create as many templates as you like, and have as many different replacement strings as you like. It'll also replace the same string a number of times, useful if you want the page title, and the title displayed within the page to be the same.

There is of course a little problem with this, in that in order for this to work, you need to have a different set of templates that support the %%text%% construct. So, the final trick is to change the way in which we search for the matching string that we want to replace. Instead of using %%text%%, you use a standard SSI construct, using a comment to encapsulate the text to be replaced. For example, we could have a template with:

<font size=+2><b><!--#include perltext=title --></b></font>

Now if you use the template as an SSI include in another document, the 'replacement' text will be ignored, because the SSI system will treat it as a comment. But when parsed by an updated version of our function, the 'title' gets replaced with the desired text.

All you have to do is modify the function to replace the quoted string. I've included the full version of that function below:

sub parse_template
{
    my (,%subs) = @_;

    open(TEMPLATE,"")
        or print "I tried to load <br>\n";
    {
        local $/;
        sh = <TEMPLATE>;
    }
    close(TEMPLATE);

    foreach  (sort keys %subs)
    {
         = quotemeta '<!--#include perltext=' .  . ' -->';
        sh =~ s//{}/g;
    }
    return sh;
}

I've used quotemeta here to make sure that the whole string is suitable for use as a search string in the regular expression.

Using Python

Although Python lags slightly behind Perl, it's use as a CGI scripting technology is increasing, especially with the popularity of the XML tools (which have been completely rewritten in the new Python 2.0 beta) and complete web serving solutions such as ZOPE (Z-Objects Publishing Environment). The sequences with Python is much the same, we slurp in the text and then use the re (regular expression) module to do the replacement on each line. The actual function looks like this:


import re

def parse_template(file,sub):
    try:
        tempfile = open(file)
    except:
        return ''
    template = tempfile.read()
    for subtext in sub.keys():
        template = re.sub('%%'+subtext+'%%',sub[subtext],template)
    return template

We call it in much the same way as with Perl, supplying the name of the template and a dictionary that contains the replacement strings:

print parse_template('t2.html',{ 'title':'This is the new title' })

Now you might wonder we I haven't used readlines to read in all the lines to a list. It's basically because I want to be able to output a complete template string without any extra processing, rather than using a list of strings. I also want to avoid map and instead just use a single pass to do the replacement.

Template Principles

All of the template principles I demonstrated in the last article apply. Providing we continue to use the same templates, and follow the same basic rules about dividing and splitting the individual elements that make a page, there's no reason why using a CGI based template parser should produce any different results than when using the SSI system.

What's Next?

Next time, I'm going to take a minor diversion from the raw HTML templates that we've used here, and instead look at using XML to define a document, and a Perl-based parser for converting that XML document into an HTML document that can be displayed by your browser.

About the Author

Martin C. Brown is a full-time writer and consultant specializing in multi-platform integration and Internet technologies. He is author of both the Perl and Python Annotated Archives and Perl: The Complete Reference. He can be contacted at mc@mcwords.com or through the MCwords website.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved