OCCASIONAL NEWSLETTER
March 2001
1. Training Update
The training courses are starting to be fleshed out more fully now. To date, I have:
- Programming Foundations: 2-1/2 day class for folks new to ColdFusion.
- Best Practices with ColdFusion and Fusebox: 4-day class providing tools and techniques for using Extended Fusebox (XFB) and some excellent SQL/Relational database training.
- Developing Applications with Extended Fusebox: 5-day class, 10 hours per day. Learn to develop enterprise-level Fusebox applications. Get both exhausted and invigorated at the same time.
2. Kill Your Back Button!
Tom Wesson emailed me to tell me about a way of effectively disabling the brower's back button that he discovered. In his writeup, Tom refers to some sample code. This is available here. Here's Tom:
If a user submits a form and then attempts to back up and resubmit it again, this presents a potential problem to the programmer. What if the form the user backed up to is no longer "valid" based on the previous form submittal? Also, if the user attempts to reload the form processing page, this can present a problem. Why are we allowing them to do this? The consequences of such an action range from "no effect" to "unforeseen circumstance".
These "bugs" are difficult to trace because they are dependent on the user's choice of navigation controls which (up until now) have remained largely uncontrollable and undetectable.
(1) We shouldn't have to be concerned about the some forms being submitted more than once via the back button. Note that this concern does not apply to "all forms" -- with many forms, it simply doesn't matter if the user backs up into them.
(2) We don't want to process the same form data more than one time via the reload button. This should apply to all forms.
(3) We don't want to prevent the user from using the back button where it makes sense. The back button is useful!
I have included 5 ColdFusion pages that address (1) and (2). (available at www.halhelms.com)
Application.cfm includes code that turns off the browser's cache. To date, this combination of the CFHEADER tag and meta-tags has proven itself to cover the widest range of browsers that I know of. Additionally there is code that prevents IE from allowing the backspace key to function as a back button (not tested in all versions of IE). Credits to www.irt.org for the backspace key cancel functionality. Lastly, session management is turned on -- this is necessary for our example.
page1.cfm sets up a simple form which submits to page2.cfm. The session variable, SESSION.str_message, is set to "*". We use SESSION.str_message to cleanly communicate the results of the form submittal to the user.
page2.cfm actually processes the form data entered (although we have no actual processing code in our example). This includes setting the response message we wish to communicate to the user through SESSION.str_message. In order to prevent any possibility of page2.cfm being run more than once for the same form, we "cover it up" via a special Javascript redirection function called replace (). What we are doing in our example is redirecting to page3.cfm and covering up the browser history entry for page2.cfm. This works for browsers that support the document.images object (most do).
Computers are incorporated in modern ice cream vending machines to enhance their functionality. Ice Cream Vending machines are manufactured by many companies. Your competition will try to overcome all requests for high-tech ice cream vending machines and credit card acceptors
|
|
page3.cfm runs after page2.cfm and takes page2.cfm's place in the browser history (via the replace () function in page2.cfm). page3.cfm acts as the barrier between the form and the response page. As page2.cfm redirects to page3.cfm, page3.cfm basically ignores the window.history.go ( 1 ) command (because there isn't anywhere to go forward yet) and redirects to page4.cfm. If the user attempts to back up from page4.cfm, page3.cfm will "quickly" do a browser forward command back to page4.cfm. If the user presses the back button repeatedly and very quickly on page4.cfm, it is possible to back up to the form (the effect of this varies between machine speeds and browser versions) -- although you never have to worry about them backing up to the form processing code (unless they're using a very old browser).
page4.cfm shows the form response message exactly one time.
That's it! Of course, this isn't a perfect solution. If your user has a very old browser on a very fast machine and has the ability to click the back button extremely fast -- you just might run into the problem that this solution is attempting to solve. However, running into this kind of case is remote and lessening the potential for problems is what we all strive for.
3. Syndicating content with HTTP and WDDX.
I decided to create an affiliate program so that other sites who recommend people to our training can be paid. One affiliate asked if we could have a WDDX packet that would send the latest information on available courses, dates, and locations. Once she got those, she could display them as she saw fit. Luckily, ColdFusion makes this very easy to do and I thought an exploration of them might be worthwhile this month. Even if you don't get into the affiliate business, the techniques used are very useful in syndicating any content. Plus, you'll get to see the use of the QuerySim tag. But I'm getting ahead of myself...
I asked the affiliate what kind of information she wanted. She determined that she wanted the course name, description, locations and dates it was being held. In addition, I decided to pass her the courseID, so that if someone clicked on a specific course, we could show them information on that course.
I then talked to my training colleague, who thought that validating the requester before giving the information might be a good idea. That made sense. I created a page, UpdateCourseInfo.cfm, that would serve up the information. First, I made sure that the requester passed me their reference number. Without one, I just gave an error message and aborted.
<cfoutput>
<cftry>
<cfparam name="form.ref" type="string">
<cfcatch>
Invalid request.
<cfabort>
</cfcatch>
</cftry>
Next, I included a query file that checked to make sure
that the "ref" passed to me is a valid affiliate name.
<cfinclude template="qryValidateRequester.cfm">
<cfif NOT ValidateRequester.recordCount>
Sorry, we couldn't validate you.
<cfabort>
</cfif>
If I made it past this, I include another query file that gets the course
information from a database.
<cfinclude template="qryCourseInfo.cfm">
If it finds some information, it turns this into a WDDX
packet and then outputs this packet on the page.
<cfif CourseInfo.recordCount>
<cfwddx
action="CFML2WDDX"
input="#CourseInfo#"
output="info">
#info#
</cfif>
</cfoutput>
That's all there is to the first page. Now, for the page that calls this one. RequestUpdate.cfm, uses the CFHTTP tag to query a ColdFusion server. If you haven't used CFHTTP before, it's a very handy tag that operates almost as a little web browser. When used with CFHTTPPARAM tags, it can pass information to a server as cgi, cookie, file, URL or form variables. Here's the code I wrote. In this case, the information I wanted to pass was a variable called "ref" that would contain the unique identifier assigned to each affiliate. I decided to pass "ref" as a form variable:
<cfoutput>
<cfhttp
method="POST"
url="http://www.coldfusiontraining.com/ClassSyndication/
UpdateCourseInfo.cfm">
<cfhttpparam
name="ref"
value="TEST"
type="FORMFIELD">
</cfhttp>
The variable, cfhttp.filecontent, gives me a handle on the page returned by the HTTP call. Since this will be a WDDX packet, I use WDDX again to "unpack" the string and return it to a query result set.
<cfwddx
action="WDDX2CFML"
input="#Trim( cfhttp.filecontent )#"
output="CourseInfo">
</cfoutput>
Finally, I output the results of the query, which was the information the affiliate wanted in the first place.
<cfoutput query="CourseInfo" group="courseID">
<br>
#courseID# - #courseName#:#courseDescription#<br>
<cfoutput>
#startDate# to #endDate# in #location#<br>
</cfoutput>
</cfoutput>
I wrote this code at my local Caribou Coffee (better than Starbucks!) and wasn't connected to the server where the database lives. But I wanted to test my code! I was able to do this using QuerySims. I've written about these before, I believe, but here I can show them to you in use.
My application relies on two separate query files, qryValidateRequester.cfm and qryCourseInfo.cfm. Eventually, of course, I would have to place either CFQUERY or CFSTOREDPROC tags, but for now, QuerySims fit the bill. Here's what qryCourseInfo.cfm looks like (I've included the Fusedoc to make the code clearer):
<!---
|| BEGIN FUSEDOC ||
|| Properties ||
Name: qryCourseInfo.cfm
Author: hal.helms
|| Responsibilities ||
I run a query and return the info on all the current courses.
|| Attributes ||
<-- variables.CourseInfo: a QUERY result set
< courseID: an INTEGER
< courseName: a STRING
< courseDescription: a STRING
< startDate: a STRING
< endDate: a STRING
< location: a STRING
|| END FUSEDOC ||--->
<cfoutput>
<cf_QuerySim
queryname="CourseInfo"
simfile="#GetDirectoryFromPath(GetCurrentTemplatePath())#\
CourseInfo.sim">
</cfoutput>
You can see that the only code calls a custom tag called "QuerySim", providing it with both a "queryname" and a "simfile". The custom tag, QuerySim, will take the information in the simfile and translate this into an actual query, using queryname as the query's name.
Here is the part of the CourseInfo.sim code:
[CourseInfo]
QueryColumns = courseID, courseName, courseDescription,
startDate, endDate, location
SimData1 = 100|Programming Foundations with ColdFusion|
Start off with a solid programming foundation! This class
teaches the basics you need to do real-world programming in
ColdFusion. This class is kept small and inexpensive,
so you will receive maximum instruction for a minimum price.
How can we provide such an exclusive beginner's
class for so little dough? Because we want you to get a good
start right off the bat, and we want you to save your money
for our more advanced courses! :) But you'd better move
quickly if you want to attend. With so few seats, they'll go fast!
|April 2, 2001|April 4, 2001|Duluth, GA
The file uses the standard Windows .ini file format. It begins with a reiteration of the query name in square brackets. Then it sets the various column names in the order they will be returned by the query. Finally, it creates one row, denoted by the name "SimData1". The values for each column are in this string, separated by pipe symbols ( | ).
If I wanted more than one row, I would add to the file, like this:
SimData2 = 200|Best Practices with ColdFusion and Fusebox|
Congratulations! You know that "little app" the sales guy
asked you to do? Well, it turns out the VP of Sales loves
it and will be deploying it to the national salesforce...
Tomorrow! It seems inevitable: what you do today will grow
beyond what anyone anticipated. So you need code that's
robust, scalable and maintainable. All the books tell you
so. What they don't tell you nearly so clearly is how to
write that code. We Will. If you're ready for training
that goes beyond Powerpoint slides and canned exercises,
then you need to attend Best Practices with ColdFusion and
Fusebox.|April 23, 2001|April 26, 2001|Herndon, VA
QuerySim.cfm, the custom tag, reads this in and returns a true query, complete with columnlist and recordcount properties.
This technique of using QuerySims is a very powerful one, as it lets us work on the code before the database schema is finalized. This may seem like heresy, but it has some great benefits: First, it lets the project begin before it normally would. Since so many projects run late, this can only be good news. Second, it lets the coder define the names of database columns so that the code is very readable (and thus, maintainable). When the actual query is run, the DB designer simply aliases the column names the coder wants to the ones actually used in the database. Finally, such a separation of code from database development allows for better scheduling, since dependent events are eliminated.
You can get the custom tag, QuerySim, at www.halhelms.com.
|