Archive for the ‘ColdFusion’ Category

A while back I wrote a post about accessing form fields in ColdFusion as an array. This is useful when you have several fields with the same name, and there is a chance they could contain a comma. Working with the data as an array is much more robust.

But after updating to CF10 I realized this didn’t work anymore! I was surprised, since I thought the getPageContext() stuff was pretty standard, and not undocumented. So anyway I’ve rewritten my function to work in CF10. The code is much simpler now.

<cffunction name="formFieldAsArray" returntype="array" output="false" hint="Returns a Form/URL variable as an array.">
	<cfargument name="fieldName" required="true" type="string" hint="Name of the Form or URL field" />
	
	<cfset var content = getHTTPRequestData().content />
	<cfset var returnArray = arrayNew(1) />
	
	<cfloop list="#content#" delimiters="&" index="local.parameter">
		<cfif listFirst(local.parameter,"=") EQ arguments.fieldName>
			<cfif ListLen(local.parameter,"=") EQ 2>
				<cfset arrayAppend(returnArray,URLDecode(listLast(local.parameter,"="))) />
			<cfelse>
				<cfset arrayAppend(returnArray,"") />
			</cfif>
		</cfif>
	</cfloop>
	
	<cfreturn returnArray />

</cffunction>

You may have heard about the new sameformfieldsasarray setting in CF10. This is another option. But it is application-wide. That is, anytime you have form fields with the same name they will come through as an array. This may or may not work for you. In my application, enabling that would break a lot of code so I wrote this separate function to handle when I need the values as an array.

I ran into an interesting issue today. Our database timestamps are all stored in UTC. So when a user runs a report from a webpage, we convert the time they entered into a UTC timestamp. If we didn’t do this, the report may miss some records.

For example, a customer buys a product at 11:00pm our time on the 1st of the month. The timestamp in the database for this record will be 5:00am on the 2nd, since we are -6 hours from UTC here in the Central timezone.

So we modify timestamps in our reporting queries like this:

...  WHERE
order.createDate > <cfqueryparam value="#DateConvert('local2utc', arguments.startDate)#" cfsqltype="cf_sql_timestamp">
AND
order.createDate < <cfqueryparam value="#DateConvert('local2utc', arguments.endDate)#" cfsqltype="cf_sql_timestamp"></cfqueryparam>

There is an issue with this in CF10. Take this code:

<cfset arguments.startDate = CreateDateTime(2012,1,1,00,00,00)></cfset>
<cfquery>
...
WHERE order.createDate > <cfqueryparam value="#DateConvert('local2utc', arguments.startDate)#" cfsqltype="cf_sql_timestamp">
....
</cfqueryparam></cfquery>
<cflog text="I think I passed #DateConvert('local2utc', arguments.startDate)# to the database"></cflog>

If you look in your log you’ll see I think I passed {ts '2012-01-02 6:00:00'} to the database (since I am -6 from UTC). But if you look in the debugging information for this query, or use SQL Server Profiler to view it, you’ll see it gets sent to the database as 2012-01-01 00:00:00:000!
Continue reading ‘Using DateConvert to get a UTC timestamp may not return what you expect’ »

When accepting uploads from a browser, it can sometimes be handy to have access to the filename before using CFFILE to “upload” the file onto the file system. For example say you want to show an error message if the file is not a PDF. In that case what I’ve usually done is use CFFILE to place the file onto the file system, and then check the extension. If its not in my allowed list, then I delete the file and send an error message to the client. But using the code below I can check the file extension without having to use CFFILE first. For example you could do something like this:

<cfset theClientFilename = getClientFileName("myFormField")>
<cfif ListLast(theClientFilename,".") NEQ "pdf">
   // do your error handling here
<cfelse>
  // else the extension is ok. Use cffile to handle the upload and proceed
</cfelse></cfif>
</cfset>

Here is the getClientFileName function, both in cfscript and regular tag formats.
Continue reading ‘Getting the client filename before using cffile’ »

Wanting to try out the new ColdFusion 10 beta without downloading and installing it? Hostek is offering free CF 10 beta hosting. You’ll get 5GB of storage space, and 200GB of transfer. Plenty of resources to play around with. You’ll get your own subdomain to use, and access to a MySQL database.

Get it here: http://hostek.com/hosting/coldfusion/coldfusion10-hosting.asp

I love the CFSPREADSHEET tag that was added to ColdFusion9. It makes working with spread sheet data so easy. The spreadsheets I am given to work with often contain descriptive column names that contain spaces like “First Name” or “Home Phone”. This causes a problem when you try to work with the data in a QoQ (query of a query).

Lets say you have a spread sheet containing these columns: Name, City, State, Postal Code. Then pull it into a query using the cfspreadsheet tag:
<cfspreadsheet action="read" src="c:\Customers.xls" query="customers" headerrow="1" rows="2-65536" sheetname="Customers">

Now try to narrow down the results to only those in a certain postal code. This won’t work of course:
<cfquery name="qryCA" dbtype="query">
SELECT * FROM customers WHERE [Postal Code] = '90210'
</cfquery>

Neither will this:
<cfquery name="qryCA" dbtype="query">
SELECT * FROM customers WHERE [Postal\ Code] = '90210'
</cfquery>

Or this:
<cfquery name="qryCA" dbtype="query">
SELECT * FROM customers WHERE ['Postal Code'] = '90210'
</cfquery>

I have not found any way to escape a space in a column name inside QoQ. But, thankfully there is a way we can manipulate the column names to remove the space.

<cfset colNameArray = customers.getColumnNames() />
<cfloop from="1" to="#arrayLen(colNameArray)#" index="i">
	<cfset colNameArray[i] = colNameArray[i].replace(' ','') />
</cfloop>
<cfset customers.setColumnNames(colNameArray) />

You could use this to completely rename column names if you wanted to. This isn’t just useful for working with cfspreadsheet either, I could see this being used in other circumstances to change the column names.

Thanks to Steven Neiland for pointing me in this direction.

A while back I had the need to do some formatting on a cfgrid. I was using a typical product/price table but the prices are stored with 4 decimals and we needed to display that much resolution. The html version of CFGRID does not directly support this.

Consider this “fake” query set:

<cfset materials=QueryNew("ProductCode,Description,Price","VarChar, VarChar, decimal")>  
</cfset><cfset Temp=QueryAddRow(materials,1)>
</cfset><cfset Temp=QuerySetCell(materials,"ProductCode","MAT1")>  
</cfset><cfset Temp=QuerySetCell(materials,"Description","Copper Bar")>  
</cfset><cfset Temp=QuerySetCell(materials,"Price",3.3400)>  
</cfset><cfset Temp=QueryAddRow(materials)>
</cfset><cfset Temp=QuerySetCell(materials,"ProductCode","MAT2")>  
</cfset><cfset Temp=QuerySetCell(materials,"Description","Feeler Gauge")>  
</cfset><cfset Temp=QuerySetCell(materials,"Price",2.2900)>  
</cfset><cfset Temp=QueryAddRow(materials)>
</cfset><cfset Temp=QuerySetCell(materials,"ProductCode","MAT3")>  
</cfset><cfset Temp=QuerySetCell(materials,"Description","Plastic Retainer")>  
</cfset><cfset Temp=QuerySetCell(materials,"Price",1.3201)>  </cfset>

Note the prices have 4 decimal values, this is how they come to us from the database. If you try to display these with this cfgrid code:

<cfform>
<cfgrid name="testgrid" format="html" query="materials">
</cfgrid>
</cfform>

You will end up with this:

Continue reading ‘Formatting CFGRID with JavaScript’ »

When you pass several form or URL variables into ColdFusion with the same name, they end up as a comma separated list. This is commonly done with checkboxes – a user can check as many items as they want, then they will end up in your code all in a single variable. In many other languages, Ruby or PHP for example, the selected items will end up in an array. Getting these values as a list usually works fine, until one of your values contains the list delimiter (comma). Take this form, for example:

<form action="test.cfm" method="post">
<p>Select your books:</p>
<input type="checkbox" name="books" value="A Wrinkle in Time"/>
A Wrinkle in Time<br />
<input type="checkbox" name="books" value="A Tale of Two Cities"/>
A Tale of Two Cities<br />
<input type="checkbox" name="books" value="The Lion, the Witch and the Wardrobe"/>
The Lion, the Witch and the Wardrobe<br />
<input type="submit" name="submitButton" value="Go"/>
</form>

example of the book form

If you check the first two boxes and submit the form, you will end up with this in the form scope:
a dump of the form scope shows two books separated by a comma

But if you check the third book, which has a title containing commas, things start to get messy:
Dumping the form scope with a book containing commas - you can't tell them apart.
Continue reading ‘Accessing ColdFusion form values as an array’ »

At work we had an issue where we had too many FCKeditor rich text controls on a page at once. We probably had 6 or 7, each on their own tab. For the tabs we are using the <cflayout type="tab"> tag. FireFox would handle the page with no problems, but IE would sometimes fail to load one or more of the rich text controls and would not throw any kind of error about it. The controls simply did not display.

A solution we came up with was to load the rich text controls on demand, when the user needed to use them, rather than load them all when the page first loads. At first I thought we’d have to install a separate, stand alone copy of FCKeditor (now called “CKEditor”) to do this, but we found out this is unnecessary, you use the version that comes with CF to do this.

First, we manually load the FCKeditor JavaScript:
<script type="text/javascript" src="/CFIDE/scripts/ajax/FCKeditor/fckeditor.js"></script>

Then create a function that can change a regular textarea into a rich text controls:

<script type="text/javascript">
function changeToRichText(elementToHide,id,width,height) {
	var oFCKeditor = new FCKeditor(id,width,height,'my_toolbar_set') ;
	oFCKeditor.BasePath = "/CFIDE/scripts/ajax/FCKeditor/";
	oFCKeditor.ReplaceTextarea();
	if (elementToHide) elementToHide.style.display = 'none';
	}
</script>

Then we changed the places on the page that used to have rich text controls to instead use plain textareas. Then placed an link next to each one the user can click on to change the textarea into a rich text. Sometimes they don’t need the functionality of rich text and will just enter plain text into a textarea. Here is the code:

<div id="rootCause_outter" style="text-align: left; width: 100%;">click to use editor<br /></div>
<textarea name="rootCause" style="width:740px; height: 290px;"></textarea><br />

This is used for creating records only, not editing. But it would not be hard to modify this to make it work on an edit page also.

I have been involved in setting up a new Mura site on Windows 2008 Server. Mura is a full featured CMS written in ColdFusion.

By default Mura URLs look something like /index.cfm/SiteID/pagename. So for example the contact us page might look like /index.cfm/default/contact-us. Not a great URL. But its fairly simple to translate into /contact-us which is much nicer.

Getting rid of the SiteID

Getting rid of the siteID is easy in the newest version of Mura. In the config/settings.ini.cfm file there is a setting named “siteidinurls”. Set this to 0 and Mura will no longer add the siteID to the URLs it generates. Of course this only works if you plan on only using Mura for one site. If you had more than one site, Mura wouldn’t know which one you are trying to access. There are several ways to get around this if you have more than one site, but I won’t get into that in this article.

Getting rid of the index.cfm

Getting rid of the index.cfm takes a little more work. There is another setting in the ini file called indexfileinurls. Setting this to 0 will remove the index.cfm from the URLs Mura generates. But when you click on any of those links you are going to get the 404 page. To fix this, you’ll need to tweak your webserver.

Apache is pretty straight forward, as you would expect. Enable the mod_rewrite module and drop this into an .htaccess file in your webroot:

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule ^([a-zA-Z0-9/-]+)$ /index.cfm%{REQUEST_URI} [PT]

Our site happens to be hosted on Windows 2008 Server / IIS7. To do the rewriting on II7 you’ll need to install Microsoft’s URL rewriting extension. You can get it from here: http://www.iis.net/download/URLRewrite

Once its installed, open IIS Manager. Click on your website, then double click on the new URL rewrite icon.

Continue reading ‘Mura URL rewriting on Windows 2008 / IIS7’ »

I ran into an issue today where users were unable to lookup a certain part in our system. The part in question had a µ character in the product code (don’t get me started!). I started adding some debug outputs and found that when the part number was passed to our back end system, the µ had been replaced with an ‘M’!

After some more troubleshooting I figured out the culprit was ucase(). We run all part numbers through ucase() before passing them in. At first I thought this was a CF bug, after verifying that the java string.toUpperCase() method did exactly the same thing I had to look into it some more.

It turns out that M actually is the uppercase version of µ, sort of. The µ character, which is ascii code 181, is often used to abbreviate ‘micro’ as in microamp (µA) or microfarad (µF). Its the 12th letter of the greek alphabet, ‘Mu‘. The lowercase version is μ, the uppercase version is M.

I tested this on Railo and got exactly the same result, which makes sense because I think both engines just call the .toUpperCase() method in the JVM.

So while this is technically correct, I’m certain is almost never what the developer actually wants to do. I have not found a good way around this issue, except to maybe look specifically for this character, replace it out, do the ucase(), then put the character back. For now I was able to remove the ucase() calls because they were not absolutely necessary. Any other ideas?

Update: This is what I came up with as a work around. I thought it would be slow but benchmarking it shows its 0ms even with a 50 character string.

<cffunction name="safeUcase" returntype="string" hint="Uppercases only letters">
	<cfargument name="inputString" />
	<cfset local.outputString = "" >
	<cfloop from="1" to="#Len(arguments.inputString)#" index="local.i">
		<cfset local.chr = Mid(arguments.inputString,local.i,1) />
		<cfset local.asciiCode = Asc(local.chr) />
		<cfif local.asciiCode LTE 122 AND local.asciiCode GTE 97>
			<cfset local.outputString &= ucase(local.chr) />
		<cfelse>
			<cfset local.outputString &= local.chr />
		</cfif>
	</cfloop>

	<cfreturn local.outputString />
</cffunction>