Your Daily Source for Apache News and Information  
Breaking News Preferences Contribute Triggers Link Us Search About
Apache Today [Your Apache News Source] To internet.com

Apache HTTPD Links
PHP Server Side Scripting
Apache XML Project
The Apache Software Foundation
Apache-Perl Integration Project
The Apache FAQ
Apache Module Registry
The Jakarta Project
Apache Project
Apache-Related Projects
The Java Apache Project
ApacheCon

  internet.com

Internet News
Internet Investing
Internet Technology
Windows Internet Tech.
Linux/Open Source
Web Developer
ECommerce/Marketing
ISP Resources
ASP Resources
Wireless Internet
Downloads
Internet Resources
Internet Lists
International
EarthWeb
Career Resources

Search internet.com
Advertising Info
Corporate Info
E-Commerce Solutions: Template-Driven Pages, Part 2
Sep 13, 2000, 12 :00 UTC (1 Talkback[s]) (5205 reads) (Other stories by Martin C. Brown)

By

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 ($template,%subs) = @_;

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

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

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 ($template,%subs) = @_;

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

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

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 or through the MCwords website.

Related Stories:
E-Commerce Solutions: Template-Driven Pages(Jun 28, 2000)
E-Commerce Solutions: An Apache Overview(May 31, 2000)

  Current Newswire:
perl.com: mod_perl in 30 minutes

Sun to allow open source Java implementations

SECURITY: Vulnerability in Apache for Win32 batch file processing

WebReference.com: mod_perl Developer's Cookbook

mod_l33t added to Apache Module Registry

Linux Easy Installer - Security Fixes

Daemon News: Jakarta-Tomcat on FreeBSD 4.4

Moto, a compilable server-side scripting language

SECURITY: Flaws Found in PHP Leave Web Servers Open to Attack

Everything Solaris: Apache: Handling Traffic

 Talkback(s) Name  Date
  Separating code and content
This article raises an important point: separating code (Perl/Python) and HTML makes projects easier to manage. I tend to use Perl, so I'll discuss that.

HTML coders don't want to mess with Perl, and Perl coders don't want to mess with complicated HTML.

Developing an in-house templating system is fine, but there are already several well-developed modules out there for doing this. In my job, we've used the HTML::Template module regularly. It makes life easy for both HTML and Perl developers and is flexible enough to do most of what we want.

Recently, I've been impressed with the Template Toolkit, but I've not got round to using this yet. I'd strongly consider using it for projects where HTML::Template isn't enough, though.

I consider CPAN to be one of Perl's major strengths, and I think its a shame that people still advocate spending time reinventing wheels that have already been fine-tuned to do a great job of performing mundane tasks.

Also, I tend to steer clear of SSI as it has problems with Last-Modified times, which can speed up Web applications considerably.

Tom   
  Sep 13, 2000, 19:47:41
Enter your comments below.
Your Name: Your Email Address:


Subject: CC: [will also send this talkback to an E-Mail address]
Comments:

See our talkback-policy for or guidelines on talkback content.

About Triggers Media Kit Security Triggers Login


All times are recorded in UTC.
Linux is a trademark of Linus Torvalds.
Powered by Linux 2.4, Apache 1.3, and PHP 4
Copyright 2002 INT Media Group, Incorporated All Rights Reserved.
Legal Notices,  Licensing, Reprints, & Permissions,  Privacy Policy.
http://www.internet.com/