Skip to main content
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Phill Nacelli
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Phill Nacelli

Ask Ben: Creating ColdFusion Templates On The Fly

By
Published in , Comments (14)

Hi Ben, frequent reader, first time writer. I'd be surprised if this hasn't been asked yet, but can't seem to find the answer anywhere. I'm trying to figure out a way to allow an application to build basic Coldfusion pages on the fly. i.e. this is a simple CMS that I'm building for someone, where actual page templates are 10 lines long and have a few CF tags and CF includes in them. Ideally, I'd like to allow the files to be created as soon as they name them, rather than (currently) having to physically create the pages. I have tried cffile and cfsavecontent, but it tried to process the Coldfusion tags, rather than include them in the created cfm file like I'd like. Have you come across anything like this? Thanks!

When it comes to creating ColdFusion code that writes ColdFusion code, the biggest trip-up, as you are seeing, is that your newly written ColdFusion code has a tendency to want to execute rather than being stored as text. In my experience, there are two ways to get around this: One is to work from a template into which you replace variable values; and Two, is to escape your ColdFusion code as you craft it, then unescape it before you write it to disk. Each of these methods has a use case, so I wouldn't say that one is better than the other. In general, picking a methodology comes down to how "templatable" your code is.

For these demos, I am going to keep it extremely simple; we are going to create a ColdFusion file that sets a name value and then includes a display file which outputs the name in a very "Hello World" type way.

Let's explore the templating technique first. In this approach, we are going to start off with an existing ColdFusion file that has place holders for the values we want to set. In this example, I created a "template.cfm" to hold our root template:

template.cfm

<!--- Set the person's name. --->
<cfset name = "$$name$$" />

<!--- Include the display file. --->
<cfinclude template="./display.cfm" />

As you can see, this ColdFusion file has a place holder for our name variable. This place holder is defined by "$$name$$". Now that we have this, we can read in the file contents, replace the target variables, and write the new code back to disk:

<!---
	Read in the template file as a text file.

	NOTE: You do not want to CFInclude thie file as that will
	evaluate the ColdFusion tags in the process.
--->
<cfset personTemplate = fileRead(
	expandPath( "./template.cfm" )
	) />

<!---
	Now that we have the person's display template as a text
	file, we want to replace out the variables with our known
	values. In our case, we are simply going to replace out
	the variable $$name$$ with the appropriate value.
--->
<cfset personTemplate = replace(
	personTemplate,
	"$$name$$",
	"Molly",
	"all"
	) />

<!---
	Now that have our template updated with the appropriate
	names, we can write the new ColdFusion code to a physical
	file where it can be used.
--->
<cfset fileWrite(
	expandPath( "./molly.cfm" ),
	personTemplate
	) />

Here, we are reading in the template file contents and replacing the variable "$$name$$" with the value "Molly." Then, we write the new code back to the disk with at the file "molly.cfm". When we open up this file, we see:

molly.cfm

<!--- Set the person's name. --->
<cfset name = "Molly" />

<!--- Include the display file. --->
<cfinclude template="./display.cfm" />

As you can see, our value "Molly" was replaced into the templated content in the appropriate place.

The template technique works really well if the code you are outputting is fairly static. If you really need to create some dynamic code, however, then we need to take a slightly more complex approach in which we actually define the future code using ColdFusion code. At this point, we hit the problem you were facing above - the ColdFusion code we try to output gets executed rather than saved for later. To cope with this problem, we need to escape the ColdFusion code that we want to execute at a later time. To keep this as readable as possible, I use the following escape combinations:

  • #val# becomes <%=val=%>
  • < becomes <%
  • > becomes %>

Not only does this prevent the escaped ColdFusion code from executing, in my particular editor (HomeSite), code that has <% .. %> notation is highlighted in yellow (it thinks it is ASP / PHP code I believe). This makes it extremely clear which code is your "here and now" code and which code is your "future" code.

In this demo, rather than reading in the "template.cfm" code base, we are going to create it from scratch using CFSaveContent and escaped ColdFusion code:

<!---
	Store the variable values that we want to put into our
	new ColdFusion template.
--->
<cfset name = "Tricia" />

<!---
	Create the new content for our ColdFusion page. Because we
	want to WRITE ColdFusion code and NOT EXECUTE ColdFusion
	code, we need to escape our CF Tags. So, in the following
	content, our ColdFusion tags will look more like ASP tags.

	NOTE: Notice that the # marks below ARE evaluated in order
	to complete the template.
--->
<cfsavecontent variable="templateCode">
	<cfoutput>

		<%!--- Set the person's name. ---%>
		<%cfset name = "#name#" /%>

		<%!--- Include the display file. ---%>
		<%cfinclude template="./display.cfm" /%>

	</cfoutput>
</cfsavecontent>

<!---
	Now that we have our new ColdFusion code in memory, we need
	to write it to disk. But, before we do that, we need to
	unescape the code so that it will propertly execute later on.
--->

<!---
	While I didn't have any escaped HASH marks in the current
	code, I would normally escape them with something like:

	<%=CF_VARIABLE=%>

	... which we need to turn into ...

	#CF_VARIABLE#

	As such, let's unescape these to single hash values. I am
	going to these ones first so they don't interfear with the
	other escaped ColdFusion tags.
--->
<cfset templateCode = reReplace(
	templateCode,
	"<%=|=%>",
	"##",
	"all"
	) />

<!--- UNEscape opening bracket. --->
<cfset templateCode = replace(
	templateCode,
	"<%",
	"<",
	"all"
	) />

<!--- UNEscape closing bracket. --->
<cfset templateCode = replace(
	templateCode,
	"%>",
	">",
	"all"
	) />

<!---
	Now that our code is fully UNEscaped, we can write it
	out to the disk.
--->
<cfset fileWrite(
	expandPath( "./tricia.cfm" ),
	templateCode
	) />

As you can see, this is a bit more work, but it gives us a lot more flexibility in the code we need to generate. Because we are escaping the ColdFusion code within the CFSaveContent buffer, it does not execute right away. But, that is not to say that standard ColdFusion code in the CFSaveContent buffer will not execute; if you look at my example, you'll notice that the hash marks we left in (#name#) did execute right away, putting the value "Tricia" into our code buffer value.

When we run this code, we produce the following template, tricia.cfm:

tricia.cfm


		<!--- Set the person's name. --->
		<cfset name = "Tricia" />

		<!--- Include the display file. --->
		<cfinclude template="./display.cfm" />

One thing you'll notice here is that all the tabbing in the code creation file gets left in the generated code file. You can, of course, start to tweak the tabbing and line breaks in the code that generates your template; but, at that point, you have to ask yourself where the readability is most important: in the file you generated? Or, in the file that generated the code? That's not a rhetorical question - it really depends on how you are going to use the generated file. If it is a file that will be updated manually after generation, then readability in the generated file is most important; however, if it is never touched again, then I would say readability in the generator is the most useful.

All code generation that I have ever seen operates off of one (of both) of these two techniques. Like I said above, each is good in its own way and selecting a technique really depends on how static your code can be. The more static it is, the easier a template is to work with; the more dynamic it has to be, the more you will have to mix escaped and unescaped ColdFusion code files. I hope this helps!

Want to use code from this post? Check out the license.

Reader Comments

14 Comments

@Ben a method I've found for doing a similar thing is to Evaluate and then immediately DE the content.

So for example:

<cfset name = "Tricia" />
<cfset templateCode=Evaluate(DE('<cfset name = "#name#" />
<cfinclude template="./display.cfm" />'))>
<cfset fileWrite("#uploadpath#/test.cfm" ,templateCode) />

This avoids the necessity for finding and replacing escaped var's

:)

4 Comments

Thanks for the post Ben! This is definitely a big help, and I'll look into it. I was able to also achieve what I wanted using Java to write out the file. Trevor's blog post her was a big help: http://www.burnette.us/blog/index.cfm/2006/1/30/Using-Java-Instead-of-cffile-to-Write-to-Disk

52 Comments

I just went through this exact same thing. I think this is where reMatch really shines nicely. No matter what your variable holder is (mine was {var}) you can use a regular expression to return an array of all your "variables" in the document. If your variables are named the same as your replacement variables your replace function can be pretty easy. Here is a quick write up on reMatch and how I was using it.

http://www.danvega.org/blog/index.cfm/2009/9/17/reMatch-returns-an-array-of-matches

15,848 Comments

@Tom, @Steve,

While the use of string concatenation works for delayed evaluation of the code you generate, I'd be cautious about using it as it does not lend itself well to readability / maintainability; that is the beauty of using CFSaveContent - very readable.

@Dan,

Word up - regular expressions are awesome for this kind of thing.

12 Comments

Based on the original question it seems that Steve only needs to create CF templates so that pages from CMS can have proper URLs.

I would suggest to use mod_rewrite or similar approach to rewrite pretty much any URLs in the site to a page handler which can be traditional CF page. This is much cleaner specially when moving site from server to server (e.g. testing to production).

Also I think it's necessary to point out that writing CF templates on the fly can be a massive security whole. If for example on this code:

<cfset name = "$$name$$" >

value is taken directly from the client then malicious user could set it to something like

nothing"> <cfquery>drop everything</cfquery><cfset name="still nothing

I guess You get the point...

Hope this helps.

Tero

15,848 Comments

@Tero,

You make some excellent points. In the long run, using some URL rewriting would probably be the best way to go, especially if you ever need to change up your strategy.

As far as a security hole, that would only be if the data written to the template was user-data; while that is possible, the askee gave me some code samples that they were working on and the data being written was more along the lines of a database-generated ID.

But, most excellent points!

@Hatem,

My only concern with XSLT is that it's not friendly :) I've used it a bunch, but it just never is fun. Plus, I find the output control to be very tedious.

1 Comments

@Ben,
Thanks for this...really helped with an issue I was having with static cfm pages and changing navigation menus.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel