Article Author: Dave Read
Not too long ago, I wanted to display a collection of photographs on my personal web site. I had done this in the past via static pages that contained links to the individual photographs (who hasnt?), but that solution wasn’t satisfactory to me any more. I had so many pictures to manage that it was taking significant amounts of time to keep the pages updated. So I started thinking about ways to drive the whole application from a database, and ended up with a pretty nifty solution that has some other neat applications. Here I’ll show you an application that uses HTML template pages to generate thumbnail and closeup pages dynamically, all driven from two scripts and a database.
The ASP Developer’s Dream
Part of what drove my interest in this application was the way my company (and most companies, I suspect) develop their ASP applications. You generally have teams that consist of an ASP developer or two, an "HTML Guy," and possibly a graphic designer. What happens is this: the ASP developer writes the application functionality, processing forms, inserting & pulling data from a database, etc. Then the graphic designer gets into the mix, laying out application appearance and creating the graphics. Finally the "HTML Guy" (possibly the same person as the graphic designer) creates the HTML to render the appearanceand then the whole thing returns to the ASP developer, who is left to integrate the graphics into the existing code, a boring task. Worse, though, is when the HTML Guy tries to integrate things, which almost invariably ends with broken code that the ASP developer has to fix anyway. So what is the ASP developer’s dream? To find a way to separate the graphic appearance of the site from the code. To be able to write code that doesn’t require any integration. To be able to write it once, and forget about it after that.
Have I achieved that dream? No, but what I have done is to achieve a part of that dream, at least enough of it that I can manage many different photo albums with different appearances, all from the same block of code.
Application Structure
The basic idea is to create a script that reads in an HTML template file, replacing marker tags in the template with information from the database. Then if someone wants to change the graphic appearance of an album, all they have to do is edit the template, something easily done with FrontPage, DreamWeaver, HomeSite, or any other WYSIWYG HTML editor. Since the markup "tags" I use are embedded in HTML comments, these editors work with the templates transparently. As long as they don’t move or delete the tags, everything will work just fine. I have used all three of these tools to edit my templates, and encountered no problems.
The way the scripts work is this: they read in the template file, looking for embedded tags in the text. My tags are identified by surrounding them with ## , as in ##WIDTH## , which in this example will cause the script to replace ##WIDTH## with the width of the picture. The picture data is pulled from an Access database, the picture identified by a query parameter in the URL. There’s also a script for generating pages with thumbnails of all the pictures. It doesn’t actually resize the pictures; that’s done ahead of time, and the thumbnails stored in a separate directory. It’s very similar to the singlepage script, so if you want to see the complete details of this script, download the source code from the link at the end of this article.
Application Code
Before we start, a word or two about the database tables is in order. There are two tables in the database. One table ("master") stores ‘meta’ information, namely the name of the entire photo gallery, and the names of the two template files used by the gallery. The other table ("files") contains one row for each picture in the gallery. Each row contains such information as the names of the fullsize and thumbnail images, the sizes of those images, captions, titles, etc. Again, if you want to see the details of these tables, download the files at the end of the page.
The code is pretty simple, really. Let’s look at the code for displaying an individual picture, then I’ll show you the thumbnail code (which is much more complex).
<%
‘ Slideshow.asp
‘ This ASP merges a template file with information from a database
‘ to produce output data sent to the browser. It takes one parameter
‘ to start the ASP, namely the ‘gallery’ name. This identifies the name
‘ of the .MDB file to use as a source of gallery information‘ Set up the path to the gallery database for this gallery show
dsnConn = Server.MapPath (Request("gallery")) & ".mdb"‘ Open the connection to the gallery
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=" & dsnConn‘ Get the gallery metadata out of the database
Set rs = Conn.Execute ("select * from master")
if not rs.eof then sMasterTitle = rs("name") sMasterTemplate = Server.MapPath(rs("template"))
end if
rs.Close
set rs = Nothing‘ Find out how many files are in the gallery
Set rs = Conn.Execute ("select count(*) as num from files")
iFiles = CLng (rs("num"))
rs.Close
set rs = Nothing‘ Which file are we supposed to show?
‘ pos is part of the query string, and specifies which picture we are
‘ to show.
ShowFile = Request("pos")
if ShowFile <> "" then ShowFile = CLng (ShowFile)
else ShowFile = 1
end ifif ShowFile > iFiles then ShowFile = 1 + (ShowFile mod iFiles)
end ifPrevFile = ShowFile 1
if PrevFile <= 0 then PrevFile = iFiles
end ifNextFile = ShowFile + 1
if NextFile > iFiles then NextFile = 1 + (ShowFile mod iFiles)
end if
What we’ve done so far is just setup stuff. We’ve opened the Access database specified in the request URL, figured out how many "files" (i.e. pictures) are in it, and figured out which has been requested. Then we created a recordset containing that picture’s info from the database. Now let’s get into the template processing. It’s worth mentioning that this application allows you to set a default template for each photo gallery, but you can override the default for each file if you wish.
‘ Get the file list for the gallery out of the database
Set rs = Conn.Execute ("select * from files where order_id=" & ShowFile)
if not rs.EOF then ‘ Use the template to create output and send it to the browser if rs("template") <> "" then sTemplate = Server.MapPath (rs("template")) ‘ override the default else sTemplate = sMasterTemplate ‘ use the default end if ‘ Create a filesystemobject so we can read in the template file Set fso = Server.CreateObject ("Scripting.FileSystemObject") Set ts = fso.OpenTextFile (sTemplate) do until ts.AtEndOfStream = true sLine = ts.ReadLine pos = InStr (1, sLine, "##") pLast = 1 sOut = "" do while pos > 0 pend = InStr (pos + 2, sLine, "##") ‘ We found a start marker, but did we find an end marker? if pend > 0 then ‘ Yes? Great. Replace the tag. taglen = pend pos 2 tag = Mid (sLine, pos + 2, taglen)
In this last block we used a filesystemobject to open the template file, and we started reading in lines of text from it. With each line, we look for tags, which always start and end with ## . If we find a leading ## , we look for a trailing ## on the same line. When we find one, we find the length of the entire tag (including both sets of ## ) so we’ll know how much text to skip over as we build the output line.
Next we’ll process the tag:
‘ Figure out which tag we have
select case UCASE
case "TITLE"
if rs("pagetitle") <> "" then
sReplace = rs("pagetitle")
else
sReplace = sMasterTitle
end if
case "PREV"
sReplace = PrevFile
case "NEXT"
sReplace = NextFile
case "PICTURE"
sReplace = rs("filename")
case "TEXT"
sReplace = rs("text")
case "GALLERY"
sReplace = Request("gallery")
case "GALSTART"
sReplace = "slideshow.asp?gallery=" & Request("gallery")
case "GALPREV"
sReplace = "slideshow.asp?gallery=" & Request("gallery") _
& "&pos=" & PrevFile
case "GALNEXT"
sReplace = "slideshow.asp?gallery=" & Request("gallery") _
& "&pos=" & NextFile
case "PTITLE"
sReplace = rs("picturetitle")
case "PWIDTH"
sReplace = rs("width")
case "PHEIGHT"
sReplace = rs("height")
case "PNUM"
sReplace = ShowFile
case "NUMPICS"
sReplace = iFiles
case else
sReplace = ""
end select
‘ Add skipped text, plus replaced text, to the
‘ alreadyprocessed text
sOut = sOut & Mid (sLine, pLast, pos pLast) & sReplace
‘ Mark where we started looking for the next marker
pLast = pend + 2
‘ Look for the next occurrence of the marker
pos = InStr (pLast, sLine, "##")
else ‘ Uhoh. No end marker found.
pos = 0 ‘ Just bail from the loop
end if
loop
‘ write out all processed text, plus whatever was left over
WriteLn sOut & Mid (sLine, pLast, Len (sLine) pLast + 1) loop
ts.Close
Set ts = Nothing
Set fso = Nothing
else
‘ Error we should have gotten a file from the DB
end if
rs.Close
set rs = Nothing
‘ Clean up the database connections
Conn.Close
Set Conn = Nothing
function WriteLn (s)
Response.Write s & vbCrLf
end function
The replacement part is pretty easy; all we have to do is use the Mid function to add our replaced text (skipping over the ## tags, of course) to whatever text we had previously processed from the template, and continue on. When we reach the end of a line, we pump our processed string out to the browser, and go for the next line of template.
Why does all of this look so easy? Because it is. It will help to show you an example of a template file and the finished output. Here’s a template:
<html><head>
<title>##TITLE## Image # ##PNUM##: ##PTITLE##</title>
</head><body bgcolor="#e8e8e8">
<table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr> <td></td> <td align="center"><h3>##TITLE## Image # ##PNUM## </h3> </td> <td></td> </tr> <tr> <td></td> <td align="center"><h2>##PTITLE##</h2> </td> <td></td> </tr> <tr> <td align="left"><a href="##GALPREV##"><img src="framebg/prev.jpg" border="0" HEIGHT="60" WIDTH="60"></a></td> <td align="center"><table BORDER="0" CELLPADDING="0" CELLSPACING="0"> <tr> <td><img SRC="framebg/frame0101.jpg" WIDTH="22" HEIGHT="22" BORDER="0"></td> <td><img SRC="framebg/frame0102.jpg" WIDTH="##PWIDTH##" HEIGHT="22" BORDER="0"></td> <td><img SRC="framebg/frame0103.jpg" WIDTH="42" HEIGHT="22" BORDER="0"></td> </tr> <tr> <td><img SRC="framebg/frame0201.jpg" WIDTH="22" HEIGHT="##PHEIGHT##" BORDER="0"></td> <td><img SRC="##PICTURE##" WIDTH="##PWIDTH##" HEIGHT="##PHEIGHT##" BORDER="0"></td> <td><img SRC="framebg/frame0203.jpg" WIDTH="42" HEIGHT="##PHEIGHT##" BORDER="0"></td> </tr> <tr> <td><img SRC="framebg/frame0301.jpg" WIDTH="22" HEIGHT="42" BORDER="0"></td> <td><img SRC="framebg/frame0302.jpg" WIDTH="##PWIDTH##" HEIGHT="42" BORDER="0"></td> <td><img SRC="framebg/frame0303.jpg" WIDTH="42" HEIGHT="42" BORDER="0"></td> </tr> </table> </td> <td align="right"><a href="##GALNEXT##"><img src="framebg/next.jpg" border="0" HEIGHT="60" WIDTH="60"></a></td> </tr> <tr> <td colspan="3"><p align="center"><font size="1">Copyright © 1999 David M. Read All rights reserved</font> </td> </tr> <tr> <td colspan="3"> </td> </tr> <tr> <td></td> <td align="center">##TEXT##</td> <td></td> </tr> <tr> <td></td> <td align="center"> </td> <td></td> </tr> <tr> <td></td> <td align="center"> </td> <td></td> </tr> <tr> <td></td> <td align="center"><small><a href="##GALPREV##">PREVIOUS</a> | <
a href="##GALSTART##">START</a> | <a href="##GALNEXT##">NEXT</a> | <a href="/gallery/thumbnails.asp?gallery=uwgallery">
THUMBNAILS</a> | ABOUT | <
a href="http://www.daveread.com">HOME</a></small></td> <td></td> </tr>
</table>
</body>
</html>
And here’s a screenshot of the output:

Now clearly the script didn’t generate the white frame, the drop shadow, the "next" and "previous" buttons, did it? No, those are all static graphic elements that are referenced in the template.
What about thumbnails? A gallery is no good if viewers have to page through it manually; you want to be able to give them the ability to see all (or at least one page worth) of small pictures in one glance, and then pick the one for closer inspection. A thumbnail page presents a number of thorny problems, because you’d like to create a table of pictures, but you still want the ability to customize the appearance of those pictures.
The solution I developed uses the template mechanism twice. The first one is identical to the template mechanism for single pictures. The second template is embedded in the HTML, and describes how each cell of the thumbnail table should be formatted, along with how many columns of pictures you want displayed. Then you tell the script where you want the table built, and the script builds the whole table, processing the thumbnail cell template repeatedly for as many pictures as desired.
Since most of the code is similar in both scripts, I’ll only show you the parts relevant to processing the cell template. First, the code block that processes the template tags changes a little. Most of the tags we used in the singlepicture mode are gone, and there are three new ones one for indicating how many columns you want in the table ( ##THUMBCOLS## ), one to tell the script you’re starting a thumbnail definition ( ##THUMBDEFSTART## ), and one to tell the script to start outputting the actual thumbnail table ( ##SHOWTHUMBS## ):
‘ Figure out which tag we have
select case UCASE
case "TITLE"
if rs("pagetitle") <> "" then
sReplace = rs("pagetitle")
else
sReplace = sMasterTitle
end if
case "NUMPICS"
sReplace = iFiles
case "THUMBCOLS"
iThumbCols = CLng(sVal)
sReplace = ""
case "THUMBDEFSTART"
‘ Grab the thumbnail definition section and
‘ keep it in memory
ExtractThumbnailDefinition (ts) sReplace = ""
bNoOutput = true
case "SHOWTHUMBS"
‘ Use the thumbnail definition to display thumbnails
ShowThumbnails 0, iThumbCols
sReplace = ""
bNoOutput = true
case else
sReplace = ""
end select
Notice that ##THUMBDEFSTART## ends up calling ExtractThumbnailDefinition to copy the thumbnail definition into memory, and that ##SHOWTHUMBS## calls ShowThumbnails to build the actual thumbnail table.
Here’s the code that extracts the thumbnail definition into memory:
sub ExtractThumbnailDefinition (ts)
do until ts.AtEndOfStream = true
sLine = ts.ReadLine
pos = InStr (1, sLine, "##THUMBDEFEND##")
‘ found the end marker > we’‘re done
if pos > 0 then
exit sub
‘ didn’t find it > store this line
else
iThumbdefLines = iThumbdefLines + 1
if iThumbdefLines <= 100 then
thumbdefs(iThumbdefLines) = sLine
end if
end if
loop
end sub
This function is pretty braindead; all it does is copy lines into memory, looking for the ##THUMBDEFEND## tag.
Here’s the code that builds the thumbnail table:
sub ShowThumbnails (iRows, iCols)
sSQL = "select * from files order by order_id asc"
set rs2 = Conn.Execute (sSQL)
WriteLn "<table>"
iColCount = 0
do until rs2.EOF
if iColCount = 0 then
WriteLn "<tr><td>"
else
WriteLn "<td>"
end if
iColCount = iColCount + 1
BuildThumbnailCell rs2
WriteLn "</td>"
if iColCount = iCols then
WriteLn "</tr>"
iColCount = 0
end if
rs2.MoveNext
loop
WriteLn "</table>"
end sub
sub BuildThumbnailCell (item)
for i = 1 to iThumbdefLines
ProcessLine thumbdefs(i), item
next
end sub
ShowThumbnails() and BuildThumbnailCell are pretty straightforward. ShowThumbnails() opens the database and gets the list of pictures out of it. It also builds the table, calling BuildThumbnailCell to build the contents of the <td> for each picture. BuildThumbnailCell loops through the stored lines of the thumbnail definition, calling ProcessLine for each one. ProcessLine is basically identical in function to the way templates are processed in the singlepicture display, so I won’t bother going over it. There are a couple of tags specific to the thumbnails, but that’s the only difference.
Here’s a thumbnail template file:
<html><head>
<title>Dave Read’s Under Water Photo Gallery</title>
</head><body bgcolor="#e8e8e8">
<h3>This is Dave Read’s Photo Gallery.</h3>
<p>There are currently ##NUMPICS## images in the gallery.
Here they are, as thumbnail versions. Click on an image to see a larger
version. Once you click on one, you can cycle through the others in
the gallery.</p><p> </p>
##THUMBCOLS=3##
##THUMBDEFSTART##
<center>
<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0> <TR> <TD><IMG SRC="framebg/thumbnail0101.gif" WIDTH="7" HEIGHT="7" BORDER="0"></TD> <TD><IMG SRC="framebg/thumbnail0102.gif" WIDTH="##TWIDTH##" HEIGHT="7" BORDER="0"></TD> <TD><IMG SRC="framebg/thumbnail0103.gif" WIDTH="24" HEIGHT="7" BORDER="0"></TD> </TR> <TR> <TD><IMG SRC="framebg/thumbnail0201.gif" WIDTH="7"
HEIGHT="##THEIGHT##" BORDER="0"></TD> <td><a href="##PICURL##"><img SRC="##THUMB##"
WIDTH="##TWIDTH##" HEIGHT="##THEIGHT##" BORDER="0" ALT="##PTITLE##"></a></td> <TD><IMG SRC="framebg/thumbnail0203.gif" WIDTH="24" HEIGHT="##THEIGHT##" BORDER="0"></TD> </TR> <TR> <TD><IMG SRC="framebg/thumbnail0301.gif" WIDTH="7" HEIGHT="24" BORDER="0"></TD> <TD><IMG SRC="framebg/thumbnail0302.gif" WIDTH="##TWIDTH##" HEIGHT="24" BORDER="0"></TD> <TD><IMG SRC="framebg/thumbnail0303.gif" WIDTH="24" HEIGHT="24" BORDER="0"></TD> </TR>
</TABLE>
</center> ##THUMBDEFEND####SHOWTHUMBS##
</body>
</html>
The thumbnail template lets you create any general background appearance you want, then specify how many columns you want in the thumbnail table, then specify where you want the thumbnail table to appear on the final page. The script builds the table cell by cell until all pictures have been displayed. If you want to limit the number of thumbnails displayed on one page, it’s a relatively simple modification to make. All you have to do is provide a way for the template to specify how many to show (imitate the ##THUMBCOLS=nn## handler), and then limit the loop with the value you read. You’ll also want a way to specify (in the query string) which range of pictures to show that is, whether the script should show the first 10, numbers 1120, 2130, etc. This is left as "an exercise for the reader."
Here’s a screenshot of the thumbnail view:

Pluses and Minuses
There are some good points to this application, and some bad ones. The good ones are that you can manage any number of photo galleries with just two scripts and one database for each gallery, plus a pair of HTML templates for each gallery. Another good point is that the template mechanism couldn’t care less about the actual nature of your HTML templates; anything it doesn’t recognize as a replacement tag gets passed straight through to the browser. That means your templates can use style sheets, javascript, java, or whatever technology you want, with no impact on the scripts.
The bad points of this application are mainly performancerelated. Processing singlepicture pages is plenty fast enough, but processing thumbnail pages with more than 20 or 30 pictures can be very slow. I have deployed this product for customers, one of whom uses it to manage 5000+ pictures in a searchable family photo album. For that customer, I felt the performance was unacceptable, so I made two modifications to the thumbnail code. One involved the way the thumbnail cell templates are stored; rather than process the cell "by hand" for each picture, I preprocess the templates so that what’s stored is a block of static text paired along with the tag that needs replacing. Then all I have to do is walk that structure: output the static part verbatim, and do the replacement. Then move on to the next pair. That got me a quick 23x speedup. I also implemented some code to limit how many thumbnails are displayed on screen at once. Between the two, performance was restored to an acceptable level. That’s beyond the scope of this article, though.
Deploying the Tool
You need only a few things in order to deploy and use this tool as is. The ZIP file linked at the end includes everything you need except the ImageSize component (see below), including the gallery .mdb , the two ASP scripts, and two sample template files. I have also included a short "readme" file that describes each of the tags that the scripts support. In order to keep the file size down I haven’t included the picture files themselves but you can see these at daveread.com. A word of warning: the newfile form in the database (for adding new pictures to the gallery) is really quite fragile. I haven’t spent much time on making it generically useful, so you might have to hack the code a little to make it work on your system. Also, you will need to download and install the freeware ImageSize component from http://www.serverobjects.com if you want to use the form. The form allows you to browse to locate a file. Once you have located the image you want to load into the gallery, the code uses the ImageSize component to get the dimensions of the image for storage in the "files" table. I can recommend you visit http://www.serverobjects.com, if you haven’t done so already.
Summary
In this article, I have shown you the basics of an application that processes HTML templates and uses information from a database to replace tags in the HTML with fields from the database. The application presented is a photoalbum application, but there are other applications. One company has contacted me about turning this code into an internal contentmanagement tool. They have a large number of articles to maintain on their site, and the graphic appearance needs to be consistent across all of the articles. By using this template mechanism, the article text can be stored in a database and template files can specify the graphic appearance of the final product. In their case, I will probably write the tool to process the articles once and save static HTML to disk, but that’s another story.

