Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris

Very Flexible HTML Template System

by Torgo (Beadle)
on Jun 19, 2001 at 21:09 UTC ( #89724=sourcecode: print w/replies, xml ) Need Help??
Category: HTML Utility
Author/Contact Info almackey at fur dot com
Description: This is a little bit of Perl code that can be included into any CGI script or HTML file generator that has been an absolute life-saver for me, both at home and at work. I'm new to the site, so I thought I'd share it with yous all.
Why do we need something like this, and what is it? Well, we all know it's far easier and cleaner to write and edit HTML code when it isn't in quotes and escaped, or thrown into a multiline print. But, it's also a pain to load in bunches of template files and use complex regexes to parse them.

This little bit of code lets you mark up a single HTML template, and has simple routines and regexes for dealing with the code segments in an intelligent manner. I've created things from simple tools to entire sites based on this system.

You mark up segments in your HTML template with an HTML-like segment tag, and put replaceable variables within curly brackets. For example:
<segment form_option>
  <option value="{value}">{label}</option>

Then, you can construct a perl routine to intelligently deal with the code. There's a command to load in the template file and parse it into a hash, and return the code that wasn't in any segments:
my %seg;
my $content = ExtractAllSegments(ReadFile("template.html"), \%seg);

Once the segments are extracted, they're replaced by placeholders (HTML comments) that can be removed before the script prints/saves its output. Segment placeholders can be replaced with segments, or anything else your perl code can provide.

Here's an example that makes a variable $options contain a list of options from a hash %form_elements using the form_option template from above.
my $options;
foreach my $form_label (sort(keys(%form_elements)_ {
  my $element = $seg{"form_option"};
  ReplaceVariable (\$element, "label", $form_label);
  ReplaceVariable (\$element, "value", $form_elements{$form_label});
  $options .= $element;
InsertAtPlaceholder(\$content, "form_option", $options);

There's more comments that make it a bit clearer in the code itself. Anyway, I hope some among you find this little bit of code as useful as I have!

Here's the meat:

#  HTML Template System

sub ExtractAllSegments {
#USAGE: $remaining_content = ExtractAllSegments ($template_content, \%
#where $template_content is a template with segments;
#      $remaining_content is where the template without segments will 
+be returned
#  and \%segment_hash is a pointer to the hash where the segments will
+ go
#Mark segments like this:  <segment [segment_name]> ... </segment>
#Mark segment placeholders (for copies of a segment) like this: <!-- p
+laceholder [segment_name] -->
#Mark variables like this: {[variable_name]}
#segments, variables and placeholders are case sensitive!

  my ($content, $hash) = @_;
  my $count = 0;
  while ($content =~ /^.*<segment (\w*)>\n?(.*?)<\/segment>.*?$/s) {
    $count ++;
    if ($count > 1000) {return $content;} #Don't let it loop forever i
+f something goes wrong!
    my $name = $1;
    my $data = $2;
    $content =~ s/<segment $name>.*?<\/segment>/<\!-- placeholder $nam
+e -->/s;
    $$hash{$name} = $data;
  return $content;

sub ReplaceVariable {
#USAGE: ReplaceVariable (\$content, $variable_name, $variable_content)

  my ($content, $name, $insert) = @_;
  $$content =~ s/\{$name\}/$insert/g;

sub InsertAtPlaceholder {
#USAGE: InsertAtPlaceholder (\$content, $placeholder_name, $content_to

  my ($content, $name, $insert) = @_;
  $$content =~ s/<\!-- placeholder $name -->/$insert/g;

sub FlushPlaceholders {
#USAGE: FlushPlaceholders(\$content)

  my $content = $_[0];
  $$content =~ s/<\!-- placeholder \w* -->//g;
  $$content =~ s/\n\n\n+/\n\n/g;

# End HTML Template System
# File Access Routines

sub ReadFile {
# USAGE: $file_contents = ReadFile($file_name);

  unless (open(FILE,"< $_[0]")) {return 0;}
  my $readin = join "", <FILE>;
  close FILE;
  $readin =~ s/\r//g;
  return ($readin);

# End File Access Routines
Replies are listed 'Best First'.
Re: Very Flexible HTML Template System
by Zaxo (Archbishop) on Jun 20, 2001 at 04:32 UTC

    I think sub ReadFile should be more portable about line ends:

    sub ReadFile { unless (open(INFILE,"< $_[0]")) {return 0;} my $readin = join //, map {chomp;$_} <INFILE>; close(INFILE); $readin; }

    I expect there are other spots needing similar treatment

    I was tempted to change your open statement, but it's your code and that would change the interface.

    After Compline,

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://89724]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (4)
As of 2017-09-21 04:22 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (241 votes). Check out past polls.