Thursday, June 12, 2008

Overlooked Techniques

We're all creatures of habit, and forming good ones is key to a long and productive career. I want to post at least a few of my favorite techniques that I believe are 'highly' overlooked in almost every application I look at/code I review.

1.) ColdFusion HTMLEDITFORMAT on HTML form field values

First off, let me give an example of what can happen if you don't use HTMLEDITFORMAT on your data that is either redisplayed from the user, or directly from a database.

Example:
<input type="text" value=""/>I am now Writing directly to this page and can do whatever I want, including relocate a person to another website using javascript etc. "/>


Trusting user input is not only unsafe but can lead to strange formatting issues when someone has quotation marks in their text (and you are using quotation marks in your html input attributes)
Rule of thumb - always use it to Wrap all cfusion dynamic data in html inputs
i.e.
<input type="text" value="#htmleditformat(variables.dbData)#"/>


2.) ColdFusion 'output=false' on cfcomponent tag and all cffunction tags within a component

Trying to debug this issue can be a nightmare, so just always set
output=false
Your cfcomponent and cffunction tags both have the 'output' attribute and should be set to 'false' if you're not returning data to the page (which you should limit the display from cfc method anyway)
.
If you're like us and use Eclipse/cfeclipse, then have this in your snippet for the creation of functions, and you will never have to try and hunt down a single space issue in your application again.

3.) JavaScript escape() function for url data

When you append data to the url in a javascript string, the characters you are appending need to be url safe. The escape() function does exactly this.
Example:

document.location.href="index.cfm?page=x.y&firstname=" + escape(x)


Special characters and spaces etc will need special treatment and not unlike the urlencodedformat() in coldfusion, javascript data needs to be treated with the same respect through the url.


Tuesday, June 10, 2008

Internet Explorer Firefox Form Posting differences

I ran into one I haven't in a while or forgot about: the differences between the major browsers in how it posts form data.

Regardless of whether it's a method="post" or method="get" in your form, if you submit a form using 'Enter' (the Enter key on your keyboard) vs. if you click the submit button to submit your form, you will get different values passed in the different browsers.

I'm lumping FireFox and Safari together on this one as they act the same, Internet Explorer 7 being the odd man out.

Firefox/Safari will pass the 'First' submit button along with your form (first in the DOM or on page) when you hit the 'Enter' button to submit the form (like in a simple search box etc.)
Internet Explorer on the other hand will NOT pass any submit buttons if you don't click on them explicitly.

This could be looked at as a good thing, in the event that you have multiple submit buttons and are doing different things based on the value of the submit button etc.

Regardless of which process I believe is correct or not, the Internet Explorer interpretation of this data was unexpected for me as I test in Firefox all the time. My solution was to simply put a hidden form field that is named the same as my submit button which I knew will get passed through the form regardless of what browser they are using. This fixed the problem for me (there are other solutions like disabling the 'enter' button to be pressed in a given box etc)

Code:

<form method="get">
<input name="txtBox" value="" type="text">
<input name="otherstuff" value="x" type="hidden">
<input name="btnSubmit" value="Submit" onclick="return true;" type="submit">
<input name="btnSubmit" value="Submit" type="hidden">
</form>

Comparing two ColdFusion Objects (object comparison)

The problem I am trying to figure out is if there was a great way in ColdFusion 8 to compare 2 objects just like you would compare 2 strings using a 'Compare' function
For example:
Compare(string1,string2)


I found no such function and so the hunt begun to find a suitable solution.

I tried looking into some under the hood java functionality to help, but settled on another approach that seems to work pretty well, here's how it goes.

Our 'Beans' are all structured where their 'instance' data is in a variable structure i.e. etc.

I also have a generic method in my beans called 'getInstance' which returns a Structure containing all the instance data, it's a simple but powerful method that helps in debugging what data is contained in the Bean at a glance

...cffunction methog="getInstance" returntype="struct"

..cfreturn variables.instance


So I added a 'compareObject' method to my bean where I can pass in a bean of the same type and compare the 2 objects by introspecting the data from the 'getInstance' call on each of them and then simply looping through the structure keys and comparing them -
So regardless of whether the data is a date, numeric or string, the
<!--- Author: penny - Date: 6/9/2008 --->
<cffunction name="compareObject" output="false" access="public" returntype="boolean" hint="Compares 2 objects that have a getInstance Interface defined for them - ">
<cfargument name="ruleBean" type="ruleBean" required="true"/>

<!--- first compare the Structure Keys if that's the same (which they should be if they're the same object so let's ignore) --->
<!--- Next for each key in the Structure, compare the values - if there is one that is a non-match, then return a false and exit --->
<cfset var item = ''/>
<cfset var returnValue = true/>
<cfset var tmpObj =arguments.ruleBean.getInstance() />
<cfloop collection="#variables.instance#" item="item">
<cfif isSimpleValue(variables.instance[item])>
<cfif variables.instance[item] neq tmpObj[item]>
<cfset returnValue = false/>
<cfbreak/>
</cfif>
</cfif>
</cfloop>

<cfreturn returnValue />
</cffunction>


Note: this is calling 'isSimpleValue' to compare only numerics, boolean, datetime, strings and not Structures and Arrays, which simplifies the comparison scope, but may likely solve the 90% rule while leaving it up to you to implement or extend this method to do comparison accross your complex instance data.

So calling this is as simple as instantiating the bean, and calling the compareObject method on it, and passing in the bean you want to compare it to..

<cfset rule = createObject( "component", "ruleBean").init(1)/>
<cfset rule2 = createObject( "component", "ruleBean").init(2)/>
<cfset x =rule.compareObject(rule2) />
<cfdump var="#x#"/>

Returns 'false' as they are not equal (instance data in one object is '1' while the other is '2'.

Until we have a true 'compareObject' function to compare 2 objects, this will have to work for now.

Tuesday, June 3, 2008

Not your average Yo

Thanks Yo for the blog post linking here.

Yo's a good friend who's got lots of great posts relating to ColdFusion as well.

Check him out at JustAnAverageYo.com. He also runs the Southern Minnesota ColdFusion user group sponsored by my employer at BitterColdFusion.com.

Oh yeah and he's a fan of trance music as well ;)

Thanks again!