A reader (nicely) asked me something before I left for Boston, and I never got around to answering. He had an interesting problem. He wanted to list directories and files, in a recursive fashion, using HTML's unordered list display to handle the directories and their children.

Now I thought this was a simple thing - just use the recurse=true option in <cfdirectory>. However - the more I thought about it - the more difficult it seemed. You can sort the <cfdirectory> result - but not in an way you can simply output with HTML.

My first thought was to switch back to a recursive <cfdirectory>, and while that would work, I assumed I'd lose a lot in terms of speed due to all the file operations. So what I came up with was a mix of recursive CFML and the built-in recursive <cfdirectory> tag:

<cfset initialDir = "c:\apache2\htdocs\testingzone\blogcfc_flex2"> <cfdirectory directory="#initialDir#" recurse="yes" name="files" sort="directory asc">

<cfset display(files,initialDir)>

<cffunction name="display" returnType="void" output="true"> <cfargument name="files" type="query" required="true"> <cfargument name="parent" type="string" required="true"> <cfset var justMyKids = "">

&lt;cfquery name="justMyKids" dbtype="query"&gt;
select	*
from	arguments.files
where	directory = &lt;cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.parent#"&gt;
&lt;/cfquery&gt;	

&lt;cfoutput&gt;&lt;ul&gt;&lt;/cfoutput&gt;

&lt;cfoutput query="justMyKids"&gt;
	&lt;li&gt;#directory#\#name#&lt;/li&gt;
	&lt;cfif type is "Dir"&gt;
		#display(arguments.files, directory & "\" & name)#
	&lt;/cfif&gt;
&lt;/cfoutput&gt;

&lt;cfoutput&gt;&lt;/ul&gt;&lt;/cfoutput&gt;

</cffunction>

As you can see, I do the initial <cfdirectory> and have it fetch all the files. The UDF simply handles displaying items from the query. I don't normally do output from UDFs, so to be honest, I feel a bit dirty. I'd probably just wrap it up in a cfsavecontent and return that, but this was written in about 5 minutes. Another problem - note I hard code \ as my file delimiter. I could have made this more dynamic by using a Java call:

<cfset separator = createObject("java","java.io.File").separator>

In general, the use of "/" will work just fine in any OS, however, since I was doing a string comparison in my query, I'd probably want to use the same separator CF used.