ColdFusion Tutorial

Document Sample
ColdFusion Tutorial Powered By Docstoc
					ColdFusion Tutorial Basic cold fusion Tutorial of cold fusion for beginners DIOGO 26/9/2009

ColdFusion Tutorial

ColdFusion Basics In this lesson of the ColdFusion tutorial, you will learn... 1. 2. 3. 4. 5. 6. How ColdFusion works. The basics of ColdFusion Markup Language (CFML). To write a simple ColdFusion page. To use ColdFusion operators. To pass values from one page to another via the URL. To process a form with ColdFusion.

How ColdFusion Works ColdFusion MX 7 code is compiled into Java bytecode, making it extremely portable, flexible and powerful. ColdFusion is made up of two parts: 1. ColdFusion Application Server 2. ColdFusion Markup Language (CFML) ColdFusion Application Server When a user navigates in a browser to a page that ends with a .cfm extension, the request is sent to a Web server, which directs the request to

the ColdFusion Application Server (CFAS).

As shown in the diagram above, the CFAS processes the page, communicating with the file systems, databases, and email servers as necessary, and then delivers a web page to the web server to return to the browser. ColdFusion Markup Language The ColdFusion Markup Language (CFML) looks very similar to HTML or XML in that it is tag-based. A ColdFusion page generally has a mix of HTML and ColdFusion tags, and may contain other tags as well. ColdFusion tags can be distinguished from all other tags by their first two letters - cf (e.g, <cfloop>). ColdFusion Variables ColdFusion is weakly typed, meaning that you do not have to specify the data type that a variable holds. Rather, ColdFusion determines the data type of a variable at the time the variable is used. A common way to set a variable is with the <cfset> tag. <cfset firstname = "Paul"> <cfset age = 63>

Variable Names In ColdFusion, variable names:
  

consist of letters, digits, underscores, and dollar signs cannot begin with a digit are case-insensitive

Variable Prefixes and Scope There are many different flavors of variables in ColdFusion:
   

variables specific to a page variables specific to a user's session variables specific to an entire application etc.

These flavors of variables each have a different scope, which determines where and how long a variable exists. For example, variables specific to a user's session are of the Session scope. They exist for the duration of the session and are accessible on every page that is a part of the session. Variables specific to a page (i.e, Local variables) are of the Variables scope. A prefix is used to specify the scope of a variable. The syntax is shown below. Syntax SCOPE.VariableName The following table shows several variable scopes and their prefixes. Prefixes are not required for variables of all scopes. Required prefixes are in bold. Variable Scope Scope Local CGI Prefix VARIABLES CGI Description Defined and accessible on the current page only. Accessible on any page. Contains server environment variables.

Variable Scope Scope URL Prefix URL Description Accessible on the current page. Contains values passed in on the query string. Accessible on the current page. Contains values passed in through a form. Accessible on any page. Contains variables held in cookies on the client machine. Accessible on any page. Contains variables created using the Client prefix. Defined and accessible within a user-defined function or a ColdFusion Component method. Prefix Required. Accessible on any page to a single user for the duration of a client session. Prefix Required. Accessible on any page to all users until the application is restarted. Prefix Required. Accessible on any page that is delivered from specific server.

Form

FORM

Cookie

COOKIE

Client

CLIENT

Arguments ARGUMENTS

Session

SESSION

Application APPLICATION

Server

SERVER

Variables of different scopes can have the same names. For example, a local variable could have the same name as a form variable. If such a variable is referred to without a prefix, ColdFusion needs to know which variable to use. ColdFusion will search through the scopes in the following order: 1. 2. 3. 4. 5. 6. Arguments Local (Variables) CGI URL Form Cookie

7. Client In general, it is a good idea to include the prefix when referring to variables as this will help avoid confusion and could make the page process more quickly as ColdFusion will know exactly where to look for the variable. Using <cfoutput> A ColdFusion page is generally a mixture of CFML tags, HTML tags, and plain text. ColdFusion ignores any content that is not contained within a CFML tag. That is, content outside of CFML tags is returned as is to the browser. The <cfoutput> tag is used to tell ColdFusion to attempt to evaluate any variable or expressions contained within pound signs (#). Let's take a look at an example. You guessed it - "Hello World". Code Sample: Basics/Demos/HelloWorld.cfm <cfset VARIABLES.greeting="Hello"> <html> <head> <title>Hello World!</title> </head> <body> <cfoutput>#VARIABLES.greeting# World!</cfoutput> </body> </html> Code Explanation The code isn't very exciting. In fact, ColdFusion doesn't buy us anything here as we could have just as easily output the code using HTML. There is nothing dynamic about the script. Let's try something a little more interesting. Passing Variables on the URL A common way to pass values from the browser to the server is by appending them to the URL as follows: http://webucator.com/hello.cfm?greet=Hello&person=Worl d

The part of the url that follows the question mark is called the query string. One or more name-value pairs can be passed to the server in this way. Each name-value pair is separated by an ampersand (&). The processing page can read these name-value pairs and use them to determine its response. The HTML page below shows an example of how these name-value pairs might be passed. Code Sample: Basics/Demos/HelloHi.html <html> <head> <title>Preferred Greeting</title> </head> <body> Do you prefer a formal greeting or an informal greeting? <ul> <li><a href="HelloHi.cfm?greet=Hello">Formal</a></li> <li><a href="HelloHi.cfm?greet=Hi">Informal</a></li> <li><a href="HelloHi.cfm?greet=Howdy">Friendly</a></li> </ul> </body> </html> Code Sample: Basics/Demos/HelloHi.cfm <html> <head> <title><cfoutput>#URL.greet#</cfoutput> World!</title> </head> <body> <cfoutput>#URL.greet# World!</cfoutput> </body> </html> Code Explanation The <cfoutput> tags tell ColdFusion to look for variables and expressions to evaluate. Within <cfoutput> tags, any content not in pound signs (#) is simply ignored. Another way to write this page would be to replace the two sets of <cfoutput> tags with an open <cfoutput> tag before the open <html> tag and a close </cfoutput> tag after the close </html> tag.

Note that <cfoutput> tags, as a general rule, cannot be nested. (see footnote) ColdFusion Comments ColdFusion comments are similar to HTML comments except that they take three dashes instead of two. <!-- This is an HTML comment --> <!--- This is a ColdFusion comment ---> Exercise: Passing Variables via the Query String Duration: 10 to 20 minutes. In this exercise, you will write a script that says hello to different people based on what is passed through the query string. 1. Open Basics/Exercises/HelloWho.html in your editor. 2. Modify HelloWho.html so that each Beatle name is a link passing the name of that Beatle (Paul, John, George or Ringo) to HelloWho.cfm. 3. Open Basics/Exercises/HelloWho.cfm in your editor. 4. Modify HelloWho.cfm so that it outputs a greeting based on the link that is clicked on HelloWho.html. 5. Test your solution in a browser. Code Sample: Basics/Exercises/HelloWho.html <html> <head> <title>Greeting the Beatles</title> </head> <body> Choose a Beatle to greet. <ul> <li><a href="HelloWho.cfm">Paul</a></li> <li><a href="HelloWho.cfm">John</a></li> <li><a href="HelloWho.cfm">George</a></li> <li><a href="HelloWho.cfm">Ringo</a></li> </ul> </body> </html> Code Sample: Basics/Exercises/HelloWho.cfm <html> <head>

<title></title> </head> <body> </body> </html> Change the links so that each Beatle gets a custom greeting (e.g, Howdy Paul, Hi John, Bye George, Hey Ringo). Where is the solution? HTML Forms and ColdFusion Variables How HTML Forms Work A very common way to submit data to a web server is through HTML forms. There are two methods of submitting data through a form: the get method and the post method. The method used is determined by the value of the method attribute of the form tag. The default method is get. Get Method When the get method is used, data is sent to the server in name-value pairs as part of the query string. The get method is most commonly used by search pages and is useful when it is important to be able to bookmark the resulting page (i.e, the page that is returned after the form is submitted). Post Method When the post method is used, data is sent to the server in name-value pairs behind the scenes. The two major advantages of the post method are: 1. The name-value pairs are not visible in the location bar, so sensitive data such as passwords are not displayed on the screen. 2. Files, such as images and Office documents, can be uploaded via the form. The major disadvantage is that the resulting page cannot be bookmarked.

A Sample HTML Form The following is a sample HTML form for calculating the time it takes to run a specified distance at a specified speed. Code Sample: Basics/Demos/Calculator.html <html> <head> <title>Marathon Time Calculator</title> </head> <body> <h1>Marathon Time Calculator</h1> <form method="post" action="ProcessCalculator.cfm"> <table> <tr> <td>Your Name:</td> <td><input name="yourname" type="text" size="30"></td> </tr> <tr> <td>Your Speed:</td> <td><input name="yourspeed" type="text" size="4"></td> </tr> <tr valign="top"> <td>Friend's Name:</td> <td><input name="friendname" type="text" size="30"></td> </tr> <tr> <td>Friend's Speed:</td> <td><input name="friendspeed" type="text" size="4"></td> </tr> <tr> <td>Units</td> <td> <select name="units"> <option value="mph">MPH</option> <option value="kph">KPH</option>

</select> </td> </tr> <tr> <td colspan="2" align="right"> <input type="submit" value="Calculate"> </td> </tr> </table> </form> </body> </html> Code Explanation The above is a simple HTML form and contains no embedded ColdFusion code. Its action page is ProcessCalculator.cfm, which would contain ColdFusion code for processing the submitted form. Exercise: Processing Form Input Duration: 15 to 25 minutes. Next we will create a page that processes the form data. Our form entry page, excalculator- simpleform1.html, is already complete and is identical to democalculator- simpleform1.html above. Filled out, it would look like this: 1. Our form entry page, Basics/Exercises/Calculator.html, is already complete and is identical to Basics/Demos/Calculator.html above. Filled

out, it would look like this:

2. In this exercise, we will create a page that simply outputs the form entries as a list. 3. Open Basics/Exercises/ProcessCalculator.cfm in your editor. 4. Output the form variables as list items using <cfoutput>. 5. Save your work and test your solution in the browser by navigating to Basics/Exercises/Calculator.html and submitting the form. Code Sample: Basics/Exercises/ProcessCalculator.cfm <html> <head> <title>Calculator Entries</title> </head> <body> <h1>Calculator Entries</h1> You have entered: <ul> <!--Add list items that display your name, your friend's name, your speed, your friend's speed, and the units. ---> </ul> </body> </html>

Where is the solution? Built-in Functions ColdFusion has many built-in functions for performing all sorts of tasks, such as manipulating strings, formatting data, managing dates and times, parsing XML, etc. The syntax of a function is functionname(arguments), where arguments is a list of zero or more values passed to the function. Like variables, function names are case-insensitive. It is impossible (at least for this author) to memorize all the function names and the parameters that each function takes. So, every ColdFusion developer should have a good function reference. Following are few suggestions: ColdFusion Function References






Dreamweaver's built-in Macromedia CF Function Reference, which you can get to by pressing shift+F1 or selecting Window > Reference. Macromedia LiveDocs, where the functions are divided neatly into categories. The URL is http://livedocs.macromedia.com/coldfusion/7/htmldocs/00000354.htm Macromedia ColdFusion MX 7 Web Application Construction Kit by Ben Forta and Nate Weiss has an excellent function reference in Appendix C.

We will be looking at many functions throughout this course, but to give you a feel for what they how they work, here are a few date and time functions. Date & Time Functions Function Now() DateFormat("date" [,"mask"]) TimeFormat("time" [,"mask"]) Description Returns the current date and time on the server. Formats the date as specified by the mask.

Formats the time as specified by the mask.

Function CreateDate(year, month, day) IsDate(string) DaysInMonth("date")

Description Creates a date/time object with the time set to 00:00:00. Returns true if a string can be converted to a valid date/time object. Returns the number of days in a month.

The file below shows these functions in practice: Code Sample: Basics/Demos/DateFunctions.cfm <html> <head> <title>Date Functions</title> </head> <body> <cfset RightNow = Now()> <cfoutput> #RightNow#<br /> #DateFormat(RightNow)#<br /> #DateFormat(RightNow,"mm/dd/yy")#<br /> #TimeFormat(RightNow)#<br /> #TimeFormat(RightNow,"hh:mm tt")#<br /> #IsDate(RightNow)#<br /> #IsDate("January 31, 2007")#<br /> #IsDate("foo")#<br /> #DaysInMonth(RightNow)# </cfoutput> </body> </html> Code Explanation

The output is shown below: And here is another demo showing a friendlier page using Now() and DateFormat(): Code Sample: Basics/Demos/HelloWorldDate.cfm <cfset greeting="Hello"> <cfset today = Now()> <html> <head> <title>Hello World!</title> </head> <body> <cfoutput> #greeting#, World!<br> Today is #DateFormat(Now(),"dddd, mmmm d, yyyy")#. </cfoutput> </body> </html> Code Explanation

The output is shown below: Pound Sign Usage You may notice that no pound signs are used around the Now() function in the <cfset> tag, while there are pound signs around the DateFormat() function. The use of pound signs to indicate that a variable or expression should be evaluated can be a bit confusing. Here are the rules:






Use pound signs when the expression to be evaluated is not within a ColdFusion tag (e.g, <cfoutput>#Now()#</cfoutput>. Use pound signs around expressions that are within a ColdFusion tag ONLY when the expression is also in quotes (e.g, <cfset greeting = "Hello #person#">). With nested functions, use pound signs only around the outermost function (e.g, <cfoutput>#DateFormat(Now())#</cfoutput>).

Examples of Proper and Improper usage of pound signs Proper Usage <cfset person = "Paul"> <cfset greeting = "Hello #person#"> <cfoutput> #greeting#, today is #DateFormat(Now(),"dddd")#. </cfoutput> Improper Usage <!---The improper use is italicized---> <cfset person = "Paul"> <cfset greeting = "Hello" & #person#> Improper Usage <!---The improper use is italicized---> <cfoutput> #greeting#, today is #DateFormat(#Now()#,"dddd")#.

</cfoutput> Arithmetic and String Operators ColdFusion's arithmetic and string operators are, for the most part, pretty standard. Operator Type Operator Arithmetic + * / MOD ^ String & Name Addition Subtraction Example <cfset c = a + b> <cfset c = a - b>

Multiplication <cfset c = a * b> Division Modulus <cfset c = a / b> <cfset c = a MOD b>

Exponentiation <cfset c = a^b> Concatenation <cfset greeting = "Hello" & " world!">

Exercise: Performing Calculations Duration: 20 to 30 minutes. In this exercise, you will modify the calculator processing page to calculate the time it takes to finish a marathon at certain speeds. 1. Open Basics/Exercises/ProcessCalculator-2.cfm in your editor. 2. Beneath the "Calculate Speeds" heading, set a variable called Marathon with the value of 26.2. 3. Set two more variables, yourmarathontime and friendmarathontime and assign them values as calculated from the form input and the Marathon variable. 4. For now we will assume the units are in MPH. 5. Return text to the browser that reads: 6. John, at 10 7. mph, you would run a marathon in 2.62 hours. 8. At 5

mph, Marylin would run a marathon in 5.24 hours . *Replace the highlighted words with variable values. 9. At the bottom of the page, add a paragraph that reads: Form processed at 11:30AM on October 23, 2006. *Replace the bold words with the current time and date from the server. Code Sample: Basics/Exercises/ProcessCalculator-2.cfm <html> <head> <title>Calculate Speeds</title> </head> <body> <h1>Calculate Speeds</h1> <!--Set a variable called Marathon with the value of 26.2 ---> <!--Calculate the values for yourmarathontime and friendmarathontime ---> <!--Return text to the browser that reads "John, at 10 mph, you would run a marathon in 2.62 hours. At 5 mph, Marylin would run a marathon in 5.24 hours." ---> <!--Add a paragraph that reads: Form processed at 11:30AM on February 23, 2007. ---> </body>

</html> Return text to the browser that reads: John, at 10 mph, you would run a marathon in 2 hours and 37 minutes. At 5 mph, Marylin would run a marathon in 5 hours and 14 minutes. Hint: you may find it useful to use the Int() function to reformat the marathon times output to show a whole number (e.g, no decimal point). You may also find it useful to use modulus (MOD). Where is the solution? ColdFusion Basics Conclusion In this lesson of the ColdFusion tutorial, you have learned how to write a simple ColdFusion page, to recognize ColdFusion functions, to work with variables. to pass variables from one page to another via the query string and to process form data. Footnotes 1. <cfoutput> tags can only be nested if the outer <cfoutput> tag uses a query and a group attribute. This is an advanced application of <cfoutput> that applies to looping through complex queries.

Flow Control In this lesson of the ColdFusion tutorial, you will learn... 1. 2. 3. 4. 5. 6. To work with <cfif><cfelseif><cfelse> blocks. To work with <cfswitch><cfcase> blocks. To create a self-processing form page. To use loops in ColdFusion. To set defaults for variables. To use <cflocation> to redirect pages.

Conditional Processing Conditional processing allows programmers to output different code based on specific conditions. There are two conditional structures in ColdFusion <cfif> blocks and <cfswitch> blocks. If-Else if-Else Blocks Simple <cfif> The syntax for a simple <cfif> condition is shown below: Syntax <cfif conditions> Hello World! </cfif> In the above code, Hello World will be displayed only if the conditions are true. <cfif><cfelse></cfif> The syntax for a simple <cfif><cfelse> condition is shown below: Syntax <cfif conditions> Hello World! <cfelse> Goodbye World! </cfif> <cfif><cfelseif><cfelse></cfif> The syntax for a simple <cfif><cfelse><cfelseif> condition is shown below: Syntax <cfif conditions> Hello World! <cfelseif conditions> World, are you there? <cfelse> Goodbye World!

</cfif> There can be an unlimited number of <cfelseif> blocks, but a maximum of one <cfelse> block. Switch Blocks A <cfswitch> statement is similar to a <cfif> statement, except that it can only check for an equality comparison of a single expression. It cannot, for example, be used to check if one value is higher than another. Syntax <cfswitch expression="#language#"> <cfcase value="German"> Hallo </cfcase> <cfcase value="Spanish"> Hola </cfcase> <cfcase value="French"> Bonjour </cfcase> <cfdefaultcase> Hello </cfdefaultcase> </cfswitch> Decision and Boolean Operators ColdFusion use words rather than symbols for its decision and boolean operators. Operator Type Decision Operator IS EQ EQUAL IS NOT NEQ NOT EQUAL GREATER THAN Description

Returns True if the values are equal.

Returns True if the values are not equal. Return True if the value on the left is

Operator Type GT

Operator

Description greater than the value on the right. Return True if the value on the right is greater than the value on the left.

LESS THAN LT

GREATER THAN OR Returns True if the value on the left is EQUAL TO greater than or equal to the value on the GTE right. GE LESS THAN OR EQUAL TO LTE LE CONTAINS DOES NOT CONTAIN Boolean NOT AND OR Short-circuiting ColdFusion stops evaluating a Boolean condition as soon as it knows the result. For example, when checking to see if "a AND b" is true, ColdFusion will only look at b if a is true, because if a is false then the whole expression is also false, regardless of the value of b. Likewise, when checking to see if "a OR b" is true, ColdFusion will only look at b if a is false, because if a is true then the whole expression is also true, regardless of the value of b. This behavior is called short-circuiting. Returns True if the value on the right is greater than or equal to the value on the left. Returns True if the value on the left contains the value on the right. Returns True if the value on the left does not contain the value on the right. Reverses the value of an argument. Returns True if both arguments are True. Returns True if either argument is True.

Condition Examples Following are some examples of if-else if-else and switch-case expressions. Code Sample: FlowControl/Demos/If.cfm <html> <head> <title>If-Else If-Else</title> </head> <body> <cfset Age = 21> <cfif Age GTE 21> You can vote and drink! <cfelseif Age GTE 18> You can vote, but can't drink. <cfelse> You cannot vote or drink. </cfif> </body> </html> Code Explanation The file is relatively simple. You can see the different results by changing the value of Age. Code Sample: FlowControl/Demos/If2.cfm <html> <head> <title>If-Else If-Else - Compound</title> </head> <body> <cfset Age = 21> <cfset Citizen = false> <cfif Age GTE 21 AND NOT Citizen> You can drink but you can't vote! <cfelseif Age GTE 21> You can vote and drink! <cfelseif Age GTE 18 AND Citizen> You can vote but can't drink! <cfelse> You cannot vote or drink. </cfif>

</body> </html> Code Explanation The example above shows the use of compound if conditions using boolean operators. Code Sample: FlowControl/Demos/Switch.cfm <html> <head> <title>Switch-Case</title> </head> <body> <cfset Quantity = 1> <cfswitch expression="#Quantity#"> <cfcase value="1"> Quantity is 1 </cfcase> <cfcase value="2"> Quantity is 2 </cfcase> <cfdefaultcase> Quantity is not 1 or 2 </cfdefaultcase> </cfswitch> </body> </html> Code Explanation In many languages, a break is needed to end switch-case processing. In ColdFusion, this is not the case. As soon as the expression is matched, that case block code is executed, and the switch-case block exits. Redirection Using <cflocation> The <cflocation> tag is used to redirect the user to a different page. It is commonly used when a page has been moved or when a user doesn't have permissions to view a requested page. The syntax is as follows: Syntax <cflocation url="some_url">

For example: <cflocation url="http://www.webucator.com"> Exercise: Form Validation Duration: 20 to 30 minutes. In this exercise, you will modify the calculator processing page so that it validates the form input. If there are no errors, the page will be processed. However, if errors are found, the user will be returned to the form page. We will also be checking the units to see if the speeds are in mph or kph. 1. Open FlowControl/Exercises/ProcessCalculator.cfm in your editor. 2. At the very top of the page, write an if condition that checks to make sure that: o Neither of the name form fields is empty. (see footnote) o The speeds entered are numeric. (see footnote) 3. If there are errors, redirect the page back to Calculator.cfm. Otherwise, continue with the page processing. 4. Define a variable Conversion with the value of .62. (see footnote) 5. Write an if condition that checks to see if the speeds are in mph or kph. o If they are in kph, yourmarathontime and friendmarathontime will have to be calculated using the Conversion variable. 6. To test your new page, open FlowControl/Exercises/Calculator.cfm in your browser and submit the form. Code Sample: FlowControl/Exercises/ProcessCalculator.cfm <!--Write an if condition that checks to make sure that: A. Neither of the name form fields is empty. B. The speeds entered are numeric. If there are errors, redirect back to the FORM. Otherwise, continue with the page processing. ---> <html> <head> <title>Calculate Speeds</title> </head> <body> <h1>Calculate Speeds</h1>

<cfset Marathon = 26.2> <!--Define a variable Conversion with the value of .62 Write an if condition that checks to see if the speeds are in mph or kph. If they are in kph, yourmarathontime and friendmarathontime will have to be calculated using the Conversion variable. ---> <cfset yourmarathontime = Marathon/FORM.yourspeed> <cfset friendmarathontime = Marathon/FORM.friendspeed> <cfset <cfset <cfset <cfset 60> yourhours = Int(yourmarathontime)> yourminutes = (yourmarathontime * 60) MOD 60> friendhours = Int(friendmarathontime)> friendminutes = (friendmarathontime * 60) MOD

<cfoutput> #FORM.yourname#, at #FORM.yourspeed# #FORM.units#, you would run a marathon in #yourhours# hours and #yourminutes# minutes.<br> At #FORM.friendspeed# #FORM.units#, #FORM.friendname# would run a marathon in #friendhours# hours and #friendminutes# minutes.<br> <p>Form processed at #TimeFormat(Now(),"h:mmtt")# on #DateFormat(Now(),"mmmm d, yyyy")#.</p> </cfoutput> </body> </html> Add a basic error message to Calculator.cfm. To do this, you will need to figure out a way to pass data from ProcessCalculator.cfm to Calculator.cfm letting it know that there has been an error. Where is the solution?

isDefined() and <cfparam> ColdFusion errors when it is asked to try to evaluate an undefined variable. In some cases, you will want to check to see whether a variable is defined before trying to evaluate it. You can use the isDefined() function to check this. <cfif isDefined("Form.firstname")> <cfoutput>Hello, #Form.firstname#!</cfoutput> <cfelse> Hello, stranger! </cfif> In other cases, you will want to set a default value for a variable in case it is accessed before it is explicitly defined. Use <cfparam> for this. <cfparam name="Form.firstname" default="stranger"> <cfoutput>Hello, #Form.firstname#!</cfoutput> In the example above, if Form.firstname is defined before the <cfparam> tag is reached, then the <cfparam> tag would be ignored. Otherwise, the default value of stranger would be assigned to Form.firstname. Exercise: Self-submitting Form Duration: 20 to 30 minutes. In the solution in the last exercise, when there is an error in the form, all the form data gets lost. This is not ideal. In this exercise, you will create a page that contains both the form and the processing code. We use the CGI.SCRIPT_NAME variable, which holds the name of the current file, as our form action. (see footnote) The page works as follows: If the form has not yet been submitted (e.g, on the first visit to the page), the page outputs the form. Likewise, if the form has been submitted with errors, the page re-outputs the form, but keeps any valid values that were entered. If the form has been submitted with no errors, we'll process the form data and output the form showing the entered valued. 1. Open FlowControl/Exercises/CalculatorSelfProcess.cfm in your editor. 2. Much of the code is already written. Review it to make sure you have a good understanding of what's going on.

Notice the hidden field named "submitted" You can use this field to check to see if the form has been submitted. 3. In the comments of the code you will find three tasks to complete. 4. After completing the tasks, open FlowControl/Exercises/CalculatorSelfProcess.cfm in your browser and test your solution.
o

Code Sample: FlowControl/Exercises/CalculatorSelfProcess.cfm <html> <head> <title>Marathon Time Calculator</title> </head> <body> <h1>Marathon Time Calculator</h1> <!--TASK 1: Write if condition that checks if form was submitted and that there were no errors. ---> <cfif WRITE_CONDITION_HERE> <!---Set Variables---> <cfset Marathon = 26.2> <cfset Conversion = .62> <!---Check units---> <cfif FORM.units EQ "mph"> <!---Specify which option should be selected---> <cfset mphselected=" selected"> <cfset kphselected=""> <cfset yourmarathontime = Marathon/FORM.yourspeed> <cfset friendmarathontime = Marathon/FORM.friendspeed> <cfelse> <!---Specify which option should be selected---> <cfset mphselected=""> <cfset kphselected=" selected"> <cfset yourmarathontime = Marathon/Conversion/FORM.yourspeed> <cfset friendmarathontime = Marathon/Conversion/FORM.friendspeed> </cfif> <cfset yourhours = Int(yourmarathontime)> <cfset yourminutes = (yourmarathontime * 60) MOD 60> <cfset friendhours = Int(friendmarathontime)>

<cfset friendminutes = (friendmarathontime * 60) MOD 60> <cfoutput> #FORM.yourname#, at #FORM.yourspeed# #FORM.units#, you would run a marathon in #yourhours# hours and #yourminutes# minutes.<br> At #FORM.friendspeed# #FORM.units#, #FORM.friendname# would run a marathon in #friendhours# hours and #friendminutes# minutes.<br> <!--TASK 2: Write a nested if condition that determines who will win and by how many minutes. The output to the browser should read: "You would beat Marylin by 10.3 minutes." OR "Marylin would beat you by 10.3 minutes." OR "Marylin and you would finish at the same time." ---> <p>Form processed at #TimeFormat(Now(),"h:mmtt")# on #DateFormat(Now(),"mmmm d, yyyy")#.</p> </cfoutput> <cfelseif isDefined("FORM.submitted")> <!---Checks if form was submitted with errors.---> <p class="errors">All fields are required.</p> <p class="errors">Speeds must be numeric.</p> </cfif> <!--TASK 3: Use <cfparam> to create default values for the Form variables of empty strings. Replace all instances of "FOO" in the form below with appropriate values. You will also need to create default values for mphselected and kphselected. ---> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Your Name:</td> <td><input name="yourname" value="FOO" type="text" size="30" maxlength="50"></td> </tr>

<tr> <td>Your Speed:</td> <td> <input name="yourspeed" value="FOO" type="text" size="4" maxlength="5"> </td> </tr> <tr valign="top"> <td>Friend's Name:</td> <td><input name="friendname" value="FOO" type="text" size="30" maxlength="50"> </td> </tr> <tr> <td>Friend's Speed:</td> <td> <input name="friendspeed" value="FOO" type="text" size="4" maxlength="5"> </td> </tr> <tr> <td>Units</td> <td> <select name="units"> <option value="mph"#mphselected#>MPH</option> <option value="kph"#kphselected#>KPH</option> </select> </td> </tr> <tr> <td colspan="2" align="right"> <input type="submit" value="Calculate"> </td> </tr> </table> </form> </cfoutput> </body> </html> In your solution, the user probably gets no feedback if he submits the form with errors. Add an <cfelseif> block to the outer <cfif> block that checks to see if the form was submitted with errors and gives the user feedback if it was. Where is the solution? Loops

As the name implies, loops are used to loop (or iterate) over code blocks. The following section shows the syntax for some of the different types of loops. All of these loops can be found in FlowControl/Demos/Loops.cfm. Index Loops An index loop is like a for loop in other languages. It steps through a process based on some type of count. <cfloop index="count" from="1" to="5" step="2"> <cfoutput>#count#</cfoutput> </cfloop> This loop will output 1 3 5. Conditional Loops A conditional loop is like a while loop in other languages. It continues to iterate through a process as long as the specified condition is true. <cfset count=1> <cfloop condition="count LTE 5"> <cfoutput>#count#</cfoutput> <cfset count = count + 2> </cfloop> This loop will output 1 3 5. List Loops Lists are simply strings delimited by a specified character or set of characters. By default, the delimiter is a comma. ColdFusion has many functions for working with lists. <cfloop> can be used to iterate through a list, performing some function with each list item. <cfset numlist="1,2,3,4,5"> <cfloop index="num" list="#numlist#"> <cfoutput>#num#</cfoutput> </cfloop> <cfset beatles="paul john ringo george"> <ul>

<cfloop index="beatle" list="#beatles#" delimiters=" "> <li><cfoutput>#beatle#</cfoutput></li> </cfloop> </ul> The first loop will output 1 2 3 4 5. The second loop will output an unordered list of the Beatles' names. Other Types of Loops <cfloop> can also be used to loop through other types of data, including queries and structures. Some of these advanced uses of <cfloop> will be covered later in the course. <cfbreak> The <cfbreak> tag is used to break out of loops before they have finished processing. The syntax is shown below: <cfloop index="count" from="1" to="5" step="1"> <cfoutput>#count#</cfoutput> <cfif count GTE 3> <cfbreak> </cfif> </cfloop> <cfsavecontent> ColdFusion's <cfsavecontent> tag provides another mechanism for storing data in a variable. The syntax is as follows: Syntax <cfsavecontent variable="var_name"> Variable value </cfsavecontent> The great thing about <cfsavecontent> is that it can contain other ColdFusion code such as conditionals and loops. This makes it easy to save the results of complicated code as a string of text without appending to a variable with <cfset>. The following example illustrates the value of <cfsavecontent>.

Code Sample: FlowControl/Demos/cfsavecontent.cfm <html> <head> <title>cfsavecontent Demo</title> </head> <cfset beatles = ArrayNew(1)> <cfset ArrayAppend(beatles,"John")> <cfset ArrayAppend(beatles,"Paul")> <cfset ArrayAppend(beatles,"George")> <cfset ArrayAppend(beatles,"Ringo")> <body> <h1>Store Beatles as HTML List</h1> <h2>With cfset</h2> <cfset BeatlesList = "<ul>"> <cfloop from="1" to="#ArrayLen(beatles)#" index="i"> <cfset BeatlesList = BeatlesList & "<li>" & beatles[i] & "</li>"> </cfloop> <cfset BeatlesList = BeatlesList & "</ul>"> <cfoutput>#BeatlesList#</cfoutput> <h2>With cfsavecontent</h2> <cfsavecontent variable="BeatlesList2"> <ul> <cfloop from="1" to="#ArrayLen(beatles)#" index="i"> <cfoutput><li>#beatles[i]#</li></cfoutput> </cfloop> </ul> </cfsavecontent> <cfoutput>#BeatlesList2#</cfoutput> </body> </html> Code Explanation As you can see from the bolded sections, setting and changing values with <cfset> can become ugly and <cfsavecontent> provides a much more elegant solution.

Flow Control Conclusion In this lesson of the ColdFusion tutorial, you have learned to work with ifelse if-else and switch conditionals, to recognize and use ColdFusion loops and to redirect pages with <cflocation>. Footnotes 1. 2. 3. 4. Simply check to see if the values are equal to an empty string. You cna use the IsNumeric() function. There are .62 miles in a kilometer. According to the HTML specification, the action attribute is required; however, in practice, if you leave it out the page will submit to itself.

Lists and Arrays In this lesson of the ColdFusion tutorial, you will learn... 1. To work with lists. 2. To work with one- and two-dimensional arrays. Up to this point, we have dealt only with variables that store single values. Lists and arrays are used to store multiple values in a single variable. Lists Lists are really nothing more than strings delimited by a specified character or set of characters. By default, the delimiter is a comma. A list can be created using <cfset>. <cfset numlist="1,2,3,4,5"> The delimiter can be changed to any character or group of characters <cfset beatles="Paul John George Ringo"> To following code shows how to loop through these lists. Code Sample: ListsAndArrays/Demos/ListLoops.cfm <html> <head> <title>List Loops</title> </head> <body>

<h1>List Loops</h1> <h2>List Loop 1</h2> <cfset numlist="1,2,3,4,5"> <cfloop index="num" list="#numlist#"> <cfoutput>#num#</cfoutput> </cfloop> <h2>List Loop 2</h2> <cfset beatles="paul john ringo george"> <ul> <cfloop index="beatle" list="#beatles#" delimiters=" "> <li><cfoutput>#beatle#</cfoutput></li> </cfloop> </ul> </body> </html> List Functions ColdFusion provides many built-in functions for working with lists. The table below shows some of the most common list functions. List Functions Function ListLen() ListFind() Description Determines the number of elements in a list. Returns the index of the first list element in which a specified value occurs. Case-sensitive. Determines the index of the first list element in which a specified value occurs. Case-insensitive. Returns the index of the first list element that contains a specified substring. Case-sensitive. Returns the index of the first list element that

ListFindNoCase()

ListContains() ListContainsNoCase()

List Functions Function Description contains a specified substring. Case-insensitive. ListValueCount() Counts instances of a specified value in a list. The search is case-sensitive. Counts instances of a specified value in a list. The search is case-insensitive. Returns a copy of the list without the specified element. Gets a list element at a specified position. Replaces the contents of a list element. Sorts list elements according to a sort type and sort order. Appends a list or element to a list. Inserts an element at the beginning of a list. Inserts an element in a list. Gets the first element of a list. Gets a list, without its first element. Gets the last element of a list. Converts a one-dimensional array to a list. Converts a list to a one-dimensional array. Replaces occurrences of the elements from a delimited list in a string with corresponding elements from another delimited list. The search is case-

ListValueCountNoCase()

ListDeleteAt() ListGetAt() ListSetAt() ListSort() ListAppend() ListPrepend() ListInsertAt() ListFirst() ListRest() ListLast() ArrayToList() ListToArray() ReplaceList()

List Functions Function sensitive. ListChangeDelims() ListQualify() Changes a list delimiter. Inserts a string at the beginning and end of list elements. Description

Exercise: Formatting the Running Log Duration: 15 to 25 minutes. In this exercise, you will change the running log so that it displays the entries in an HTML table. 1. Open ListsAndArrays/Exercises/RunningLog.cfm in your editor. 2. Before the loop that is used to display the entries, begin an HTML table and add a header row that contains the following table headers: Date, Distance, Time, and Comments. 3. Write code that will create a table row. Use <cfloop> to output the table data cells. 4. Close the HTML table.

Write code so that the resulting table's table rows have alternate colors as in

the following diagram. Note that, if you use hexadecimal color values, you may need to escape the pound sign in locations in which ColdFusion will interpret it as the beginning of an expression to be evaluated. The escape character is another pound sign. For example, the following code properly sets a variable called bgcolor. <cfset bgcolor="##ff0000"> Where is the solution? One-dimensional Arrays One-Dimensional arrays are similar to lists or tables with a single column. An array can contain zero or more elements. In ColdFusion, unlike in many programming languages, the first element of an array has an index of 1. An array with no elements has a zero length. Creating Arrays Arrays are created with the built-in ArrayNew() function, which takes one argument - the number of dimensions in the array. The following line of code creates a onedimensional array and then adds four elements to the array. <cfset beatles = ArrayNew(1)>

<cfset <cfset <cfset <cfset

beatles[1] beatles[2] beatles[3] beatles[4]

= = = =

"John"> "Paul"> "George"> "Ringo">

The first line above is actually optional as the second line will create the array if one does not already exist. However, it is a better coding practice to explicitly set the array. Reading from Arrays Reading from arrays is just a matter of pointing to a specific index of an array. <cfoutput>#beatles[3]#</cfoutput> <!---outputs George to the browser---> Looping through Arrays The following code sample shows how to create the array, output a single element in the array, and then loop through the entire array outputting each element to the browser. Code Sample: ListsAndArrays/Demos/Arrays.cfm <head> <title>Array Demo</title> </head> <body> <cfset <cfset <cfset <cfset <cfset beatles = ArrayNew(1)> ArrayAppend(beatles,"John")> ArrayAppend(beatles,"Paul")> ArrayAppend(beatles,"George")> ArrayAppend(beatles,"Ringo")>

<!---outputs George to the browser---> <cfoutput>#beatles[3]#</cfoutput> <ul> <cfloop from="1" to="#ArrayLen(beatles)#" index="i"> <li><cfoutput>#beatles[i]#</cfoutput></li> </cfloop> </ul>

</body> </html> Code Explanation Note that the code uses the ArrayLen() function to determine the length of the array, which is the same as the highest index. The next sections discusses other array functions. Array Functions As with lists, ColdFusion provides many built-in functions for working with arrays. The table below shows some of the most common array functions. Array Functions Function ArrayNew() ArrayLen() IsArray() Creates a new array. Determines the number of elements in an array. Returns true if value is an array; false otherwise. Description

ArrayIsEmpty() Returns true if array has zero elements. ArraySort() Sorts array elements according to a sort type and sort order.

ArrayAppend() Appends an element to an array. ArrayPrepend() Prepends an element to an array. Inserts a value into an array. Array elements whose indexes ArrayInsertAt() are greater than the new position are incremented by one. The array length increases by one. ArrayDeleteAt() Deletes an element from an array. ArrayClear() ArrayMin() Deletes all elements from an array. Returns the smallest numeric value in an array.

Array Functions Function ArrayMax() ArrayAvg() ArraySum() ArrayResize() ArrayToList() ListToArray() ArrayToList() Description Returns the greatest numeric value in an array. Calculates the average of the values in an array. Calculates the sum of the values in an array. Gets the first element of a list. Converts a one-dimensional array to a list. Converts a one-dimensional array to a list. Converts a list to a one-dimensional array.

Two-dimensional Arrays ColdFusion supports two-dimensional arrays, which are similar to tables or grids. You can think of the outer array as containing the rows and the inner arrays as containing the data cells in those rows. For example, a twodimensional array called rockbands could contain the names of the bands and some of the songs that they sing. Below is a grid that represents such a two-dimensional array. Rockband Beatles Song1 Love Me Do Song2 Hey Jude Song3 Helter Skelter Yesterday's Papers

Rolling Stones Waiting on a Friend Angie Eagles

Life in the Fast Lane Hotel California Best of My Love

The following code sample shows how this two-dimensional array is created. Note that the header row is not included. Code Sample: ListsAndArrays/Demos/Arrays-2Dim-create.cfm <html> <head>

<title>Rockbands</title> </head> <body> <h1>Rockbands</h1> <cfset rockbands=ArrayNew(2)> <cfset rockbands[1][1]="Beatles"> <cfset rockbands[1][2]="Love Me Do"> <cfset rockbands[1][3]="Hey Jude"> <cfset rockbands[1][4]="Helter Skelter"> <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset </body> </html> Reading from Two-dimensional Arrays To read an element from a two-dimensional array, you must first identify the index of the "row" and then identify the index of the "column." For example, the song "Angie" is in row 2, column 3, so it is identified as rockbands[2][3]. Looping through Two-dimensional Arrays To loop through a two-dimensional array, you need to nest one loop inside of another. The following code will create an HTML table from our twodimensional array. Code Sample: ListsAndArrays/Demos/Arrays-2Dim-read.cfm ---- Code Omitted ---<table border="1"> <cfloop from="1" to="#ArrayLen(rockbands)#" index="i"> <tr> rockbands[2][1]="Rolling Stones"> rockbands[2][2]="Waiting on a Friend"> rockbands[2][3]="Angie"> rockbands[2][4]="Yesterday’s Papers"> rockbands[3][1]="Eagles"> rockbands[3][2]="Life in the Fast Lane"> rockbands[3][3]="Hotel California"> rockbands[3][4]="Best of My Love">

<cfoutput> <cfloop from="1" to="#ArrayLen(rockbands[i])#" index="j"> <td>#rockbands[i][j]#</td> </cfloop> </cfoutput> </tr> </cfloop> </table> </body> </html> Lists and Arrays Conclusion Lists and arrays can be very helpful for working with data sets. In most cases, arrays are much more efficient than lists as a list is not actually builtin data type and has to be created and parsed by ColdFusion at the time it is required. So, unless data is already in a list format (e.g, form data from a multiple select field), it is generally better practice to use arrays.

Form Validation In this lesson of the ColdFusion tutorial, you will learn... 1. To understand the basics of the <cfform> tag. 2. To work with ColdFusion's auto-generated client-side form validation using <cfform>. 3. To work with ColdFusion's auto-generated server-side form validation using <cfform>. 4. To work with ColdFusion's auto-generated server-side form validation without <cfform>. 5. To create input masks. 6. To write your own server-side form validation code. (see footnote) <cfform> Basics (see footnote) The <cfform> tag is used to generate HTML forms. ColdFusion form controls, such as <cfinput>, <cfselect>, and <cftextarea> are used inside of <cfform> to create HTML entry fields. The table below shows <cfform>'s attributes for HTML forms.

Major <cfform> Attributes for HTML Forms Attribute name action method format Description form name form action form method: "post" or "get" "html" for HTML forms

Code Sample: FormValidation/Demos/AddEntry-cfform.cfm <html> <head> <title>Running Log</title> </head> <body> <cfform name="RunningForm" method="post" action="AddEntry-cfform.cfm" format="html"> <table> <tr> <td>Date:</td> <td><cfinput type="text" name="date" size="20"></td> </tr> <tr> <td>Distance:</td> <td><cfinput type="text" name="distance" size="20"></td> </tr> <tr> <td>Time:</td> <td><cfinput type="text" name="time" size="20"></td> </tr> <tr> <td>Comments:</td> <td><cftextarea rows="3" cols="40" name="comments"></cftextarea></td> </tr> <tr>

<td colspan="2" align="right"> <cfinput type="submit" name="Add Entry" value="Add Entry"> </td> </tr> </table> </cfform> </body> </html> Code Explanation The generated HTML form code is shown below: <form name="RunningForm" action="AddEntry-cfform.cfm" method="post" onsubmit="return _CF_checkRunningForm(this)"> <table> <tr> <td>Date:</td> <td><input name="date" id="date" type="text" size="20"/></td> </tr> <tr> <td>Distance:</td> <td><input name="distance" id="distance" type="text" size="20"/></td> </tr> <tr> <td>Time:</td> <td><input name="time" id="time" type="text" size="20"/></td> </tr> <tr> <td>Comments:</td> <td><textarea name="comments" rows="3" cols="40" ></textarea></td> </tr> <tr> <td colspan="2" align="right"> <input name="Add Entry" id="Add Entry" type="submit"/>

</td> </tr> </table> </form> As you can see, it is a basic HTML form with nothing too special about it. However, ColdFusion form tags can do some pretty neat things with the right attributes. In this lesson, we'll learn how to use them to validate forms. Auto-generated Form Validation Form Validation with <cfform> Using the <cfform> in place of the <form> tag allows you to take advantage of ColdFusion's auto-generated validation code, which can be run client-side with JavaScript before the form actually submits or server-side after the form submits. For input fields that you wish to validate in this way, use <cfinput> instead of <input>. To require that a field be filled in, set required="yes" in the <cfinput> tag. In addition to checking whether or not a field is filled in, you can check that fields conform to specific types of patterns by assigning the appropriate value to the validate attribute of <cfinput> or <cftextarea>. <cfinput> and <cftextarea> Validate Values Suffix numeric Description Numeric. The following special characters are allowed and kept: $ ¤ ¥ £ +. Integer. The following special characters are allowed but stripped: $ ¤ ¥ £ +. Float. The following special characters are allowed but stripped: $ ¤ ¥ £ +. Value must be within range specified by the value attribute in the format "min=minvalue max=maxvalue". No custom error

integer

float range

<cfinput> and <cftextarea> Validate Values Suffix message is allowed. date Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC date format (without the time). Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC time format (without the date). U.S. date. Allowed formats: m/d/y, m-d-y , or m.d.y European date. Allowed formats: d/m/y, d-m-y, or d.m.y Number between 13-16 digits that conforms to the mod10 algorithm. Spaces and dashes are stripped. Nine-digit Social Security number separated by dashes or spaces. U.S. telephone number with or without area codes and extensions. 5 or 9 digit U.S. ZIP code. Valid email address. Valid URL. Boolean. (Yes, No, True, False, 1, 0) Universally unique identifier (UUID) that follows the ColdFusion format, xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx, where x is a hexadecimal number. Unique identifier that follows the Microsoft/DCE format, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is a hexadecimal number. Description

time usdate eurodate creditcard SSN telephone zipcode email URL boolean

UUID

GUID

<cfinput> and <cftextarea> Validate Values Suffix noblanks Description Value must contain at least one non-whitespace character. Field must also be specified as required using required. Value cannot contain more characters than specified by the maxlength attribute. No custom error message is allowed. Value must match regular expression defined in the pattern attributed. Used in submit buttons to prevent a user from submitting a form multiple times by mistake.

maxlength

regex

SubmitOnce

The validateat Attribute The validateat attribute specifies where the form validation should take place. Options are onsubmit (default), onblur, and onserver. The first two options use client-side form validation. The last one uses server-side validation. Below is an example of how to use <cfform> to validate the running log entry form using client-side form validation. Code Sample: FormValidation/Demos/AddEntry-onsubmit.cfm ---- Code Omitted ---<cfform name="RunningForm" method="post" action="#CGI.SCRIPT_NAME#" format="html"> <table> <tr> <td>Date:</td> <td><cfinput type="text" name="date" size="20" required="yes" validate="date" validateat="onsubmit" message="You must enter a valid date."></td> </tr> <tr> <td>Distance:</td> <td><cfinput type="text" name="distance" size="20" required="yes" validate="float" validateat="onsubmit"

message="You must enter a valid number for the distance."></td> </tr> <tr> <td>Time:</td> <td><cfinput type="text" name="time" size="20" required="yes" validate="regex" pattern="^\d{1,3}:\d{0,2}$" validateat="onsubmit" message="You must enter a valid time in the format mm:ss."></td> </tr> <tr> <td>Comments:</td> <td><cftextarea rows="3" cols="40" name="comments" required="yes" validate="maxlength" maxlength="50" validateat="onsubmit" message="The comment must be between 1 and 50 characters."></cftextarea></td> </tr> <tr> <td colspan="2" align="right"> <cfinput type="submit" name="AddEntry" value="Add Entry"> </td> </tr> </table> </cfform> </body> </html> Code Explanation The form checks for the following: 1. The date is filled in and is a valid date. 2. The distance is filled in and is a valid float. 3. The time is filled in and corresponds to a specified regular expression. (Note this is a time in minutes and seconds (mm:ss), not a time of day, so we don't check to see if it is a valid time.) 4. The comments field is filled in.

If the form is submitted with errors, the following alert will pop up:

Below is an example of how to use <cfform> to validate the running log entry form using server-side form validation. The only change is the value of the validateat attribute. Code Sample: FormValidation/Demos/AddEntry-onserver.cfm ---- Code Omitted ---<cfform name="RunningForm" method="post" action="#CGI.SCRIPT_NAME#" format="html"> <table> <tr> <td>Date:</td> <td><cfinput type="text" name="date" size="20" required="yes" validate="date" validateat="onserver" message="You must enter a valid date."></td> </tr> <tr> <td>Distance:</td> <td><cfinput type="text" name="distance" size="20" required="yes" validate="float" validateat="onserver" message="You must enter a valid number for the distance."></td> </tr> <tr> <td>Time:</td> <td><cfinput type="text" name="time" size="20" required="yes" validate="regex" pattern="^\d{1,3}:\d{0,2}$" validateat="onserver" message="You must enter a valid time in the format mm:ss."></td> </tr> <tr> <td>Comments:</td> <td><cftextarea rows="3" cols="40" name="comments" required="yes" validate="maxlength" maxlength="50"

validateat="onserver" message="The comment must be between 1 and 50 characters."></cftextarea></td> </tr> <tr> <td colspan="2" align="right"> <cfinput type="submit" name="AddEntry" value="Add Entry"> </td> </tr> </table> </cfform> </body> </html> Code Explanation The form makes the same checks as we saw with the client-side validation, but is validated server-side. If the form is submitted with errors, the page will display as follows:

Although it is possible to customize this error template, you do not have as much control over the look and feel of the page as you would if you wrote the form validation code yourself. Behind the Curtain of Server-Side Validation What's happening behind the curtain here? The <cfform> tag and its friends (<cfinput>, <cfselect>, etc.) are performing a little magic by autogenerating standard HTML form controls. For each piece of validation, hidden fields are added that ColdFusion later uses to know what type of

validation to perform. The developer is also able to add these hidden fields directly, which is useful when not using <cfform>. In older versions of ColdFusion (MX 6 and earlier), this was the only way to get ColdFusion to auto-generate server-side validation. This is discussed in the next section. Server-Side Validation without <cfform> Through the use of hidden <input> fields in a form, you can get ColdFusion to do some basic server-side form validation for you. The name of the hidden fields must be the same name as the field to be validated followed by an underscore (_) followed by a suffix, which indicates the type of validation. Starting with ColdFusion MX 7, suffixes begin with "cfform"; however, for the validation types that were supported in older versions, the old suffix will still work. ColdFusion returns the value of the hidden field as the error message. The table below describes the hidden fields suffixes. Hidden Field Form Validation Suffixes Suffix Old Suffix Description

_cfformrequired

Field must be filled out or selected. If this is not _required set, then the field will not be required, even if other validation rules are set. n/a Numeric. The following special characters are allowed: $ ¤ ¥ £ + and kept. Integer. The following special characters are allowed: $ ¤ ¥ £ + but stripped. Float. The following special characters are allowed: $ ¤ ¥ £ + but stripped. Value must be within range specified by the value attribute in the format "min=minvalue max=maxvalue". No custom error message is allowed.

_cfformnumeric

_cfforminteger

_integer

_cfformfloat

_float

_cfformrange

_range

Hidden Field Form Validation Suffixes Suffix Old Suffix Description Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC date format (without the time). Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC time format (without the date). U.S. date. Allowed formats: m/d/y, m-d-y , or m.d.y European date. Allowed formats: d/m/y, d-m-y, or d.m.y Number between 13-16 digits that conforms to the mod10 algorithm. Spaces and dashes are stripped. Nine-digit Social Security number separated by dashes or spaces. U.S. telephone number with or without area codes and extensions. 5 or 9 digit U.S. ZIP code. Valid email address. Valid URL. Boolean. (Yes, No, True, False, 1, 0) Universally unique identifier (UUID) that follows the ColdFusion format, xxxxxxxx-xxxx-xxxxxxxxxxxxxxxxxxxx, where x is a hexadecimal

_cfformdate

_date

_cfformtime

_time

_cfformusdate

n/a

_cfformeurodate _eurodate

_cfformcreditcard n/a

_cfformSSN

n/a

_cfformtelephone n/a _cfformzipcode _cfformemail _cfformURL _cfformboolean _cfformUUID n/a n/a n/a n/a n/a

Hidden Field Form Validation Suffixes Suffix Old Suffix number. Unique identifier that follows the Microsoft/DCE format, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is a hexadecimal number. Value must contain at least one non-whitespace character. Field must also be specified as required using _cfformrequired. Value cannot contain more characters than specified by the value attribute. No custom error message is allowed. Value must match regular expression. Description

_cfformGUID

n/a

_cfformnoblanks n/a

_cfformmaxlength n/a

_cfformregex Syntax

n/a

<input type="text" name="age"> <input type="hidden" name="age_cfforminteger" value="Age must be a valid integer."> Below is an example of how to use hidden fields to validate the running log entry form. Code Sample: FormValidation/Demos/AddEntry-hidden.cfm ---- Code Omitted ---<form method="post" action="#CGI.SCRIPT_NAME#"> </cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Date:</td> <td><input type="text" name="date" size="20"></td> <input type="hidden" name="date_cfformrequired" value="You must enter a date.">

<input type="hidden" name="date_cfformdate" value="The date you entered is not a valid date."> </tr> <tr> <td>Distance:</td> <td><input type="text" name="distance" size="20"></td> <input type="hidden" name="distance_cfformrequired" value="You must enter a distance."> <input type="hidden" name="distance_cfformfloat" value="The distance you entered is not a valid number."> </tr> <tr> <td>Time:</td> <td><input type="text" name="time" size="20"></td> <input type="hidden" name="time_cfformrequired" value="You must enter a time."> <input type="hidden" name="time_cfformregex" value="^\d{1,3}:\d{0,2}$"> </tr> <tr> <td>Comments:</td> <td><input type="text" name="comments" size="50"></td> <input type="hidden" name="comments_cfformrequired" value="You must enter a comment."> <input type="hidden" name="comments_cfformmaxlength" value="50"> </tr> <tr> <td colspan="2" align="right"> <input type="submit" name="Add Entry"> </td> </tr> </table> </form> </body> </html> Code Explanation

The form makes the same checks as before, but using this method, it is possible to have different error messages based on the type of error. If the form is submitted without filling any of the fields in, a page that looks like the screenshot below will appear:

If the form is completely filled in, but the fields are invalid, the resulting page will look like this:

Although it is possible to customize this error template, you do not have as much control over the look and feel of the page as you would if you wrote the form validation code yourself. Masking Input Values The <cfinput> tag has a mask attribute that controls the format the user enters into a text field. The syntax is as follows: Syntax

<cfinput type="text" name="phone" mask="(999) 9999999"> The table below shows the mask characters for HTML fields: Text Input Mask Characters Character A X 9 ? Description A-Z (case-insensitive) A-Z (case-insensitive) or 0-9 0-9 Any character

Other Characters Inserts literal character The code sample below shows some samples of masking: Code Sample: FormValidation/Demos/Masks.cfm <html> <head> <title>Masks</title> </head> <body> <cfform method="post" action="#CGI.SCRIPT_NAME#" format="html"> Phone: <cfinput type="text" name="phone" mask="(999) 999-9999"><br/> SSN: <cfinput type="text" name="ssn" mask="999-999999"><br/> Course ID: <cfinput type="text" name="course_id" mask="AAA99X"><br/> <cfinput type="submit" name="Submit" value="Submit"> </cfform> </body> </html> Code Explanation

Open the page in your browser and enter fields. You will notice that the format of your entry is controlled and that some characters are automatically inserted. Custom Server-Side Form Validation Writing your own custom server-side form validation gives you a lot more flexibility and control. Take a look at the following code. Code Sample: FormValidation/Demos/AddEntry-custom.cfm <cfparam name="FORM.date" default=""> <cfparam name="FORM.distance" default=""> <cfparam name="FORM.time" default=""> <cfparam name="FORM.comments" default=""> <html> <head> <title>Running Log</title> <style> .errors {color:red; font-weight:bold} .cool {color:black} </style> </head> <body> <cfparam name="errors" default=""> <cfif isDefined("FORM.submitted")> <cfif NOT isDate(FORM.date)> <cfset errors = errors & "<li>The date is invalid.</li>"> <cfset dateclass="errors"> </cfif> <cfif NOT ListLen(FORM.distance," ") EQ 2> <cfset errors = errors & "<li>The distance must be in the format <i>num units</i>.</li>"> <cfset distanceclass="errors"> <cfelse> <cfset intDistance = ListGetAt(FORM.distance,1," ")> <cfset units = ListGetAt(FORM.distance,2," ")> <cfif NOT isNumeric(intDistance)> <cfset errors = errors & "<li>The distance must be in the format <i>num units</i>.</li>">

<cfset distanceclass="errors"> </cfif> </cfif> <cfif Len(errors) EQ 0> <cfset RunningLogPath = ExpandPath("Logs/RunningLog.txt")> <cfset Tab = chr(9)> <cfset outputstring = "#FORM.date##Tab##FORM.distance##Tab##FORM.time##Tab## FORM.comments#"> <cffile action="append" file="#RunningLogPath#" output="#outputstring#" addnewline="yes"> <h1 align="center">Entry added</h1> <a href="RunningLog.cfm">Running Log</a> <!---Clean up variables---> <cfset FORM.date=""> <cfset FORM.distance=""> <cfset FORM.time=""> <cfset FORM.comments=""> </cfif> </cfif> <h1 align="center">Add Entry</h1> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> <input type="hidden" name="submitted" value="true"> <table> <cfparam name="dateclass" default="cool"> <cfparam name="distanceclass" default="cool"> <cfparam name="timeclass" default="cool"> <cfparam name="commentsclass" default="cool"> <cfif Len(errors) GT 0><!---checking for errors---> <tr><td colspan="2" style="margin-left:20px"> <h3>Errors</h3> <ul class="errors"> #errors# </ul> </td></tr> </cfif>

<tr> <td>Date:</td> <td><input type="text" name="date" size="20" value="#FORM.date#" class="#dateclass#"></td> </tr> <tr> <td>Distance:</td> <td><input type="text" name="distance" size="20" value="#FORM.distance#" class="#distanceclass#"></td> </tr> <tr> <td>Time:</td> <td><input type="text" name="time" size="20" value="#FORM.time#" class="#timeclass#"></td> </tr> <tr> <td>Comments:</td> <td><input type="text" name="comments" size="50" value="#FORM.comments#" class="#commentsclass#"></td> </tr> ---- Code Omitted ---Code Explanation This form only validates the first two fields.
 

The date must be a valid date. The distance must be in the format "num units" (e.g, 9 miles).

If the form is submitted without filling any of the fields in, a page that looks like the screenshot below will appear:

The validation is handled as follows: 1. An errors variable is created using <cfparam> containing an empty string. 2. When the form is submitted, the fields needing validation are checked one by one. If an error is found, text is appended to the errors variable in the form of an HTML list item and a variable holding the class for that field is set to "errors". That variable is used inside the <input> fields to determine how the field should be displayed. 3. After each field has been checked, the length of the string held in errors is checked. If it is a zero-length string, that means there are no errors and the entry is added to the log. 4. Within the HTML form itself, the length of errors is checked again. If it is not a zero-length string, then the error message is output. IsValid() The IsValid() function is useful for checking whether a variable or value is of a certain data type or meets pattern, size or length constraints. There are three possible signatures for IsValid().

IsValid(type, value) isValid("range", value, min, max) isValid("regex", value, pattern) Possible types are the same as those shown in the "<cfinput> and <cftextarea> Validate Values" table at the beginning of this lesson. (see footnote) In addition, the type can be any of the following: Additional Types for IsValid() Suffix any Description Same as IsSimpleValue().

array Same as IsArray(). binary Same as IsBinary(). query Same as IsQuery(). struct Same as IsStruct(). Exercise: Custom Form Validation Duration: 20 to 30 minutes. 1. Open FormValidation/Exercises/AddEntry.cfm in your editor. 2. Add the following validation. o Time format should be "##:##". o Comments should be required and not longer than 50 characters. 3. Open AddEntry.cfm in your browser to test your new page. 1. The date entered should be in the past. You can use the DateCompare() function to compare it to the current time. If the date has not yet occurred, return an error. 2. Using your other knowledge of ColdFusion, let the user know how fast she was running. Where is the solution? Form Validation Conclusion

Although ColdFusion's auto-generated form validation makes it quick and easy to validate forms, it makes it very difficult to provide a customized user interface for handling errors. Footnotes 1. This lesson does not address writing custom client-side validation as that requires knowledge of JavaScript. 2. This lesson only covers using <cfform> with HTML forms. Starting with ColdFusion MX 7, ColdFusion can auto-generate Flash and XML/XSLT-generated forms. 3. With the exception of SubmitOnce.

Database Access and Authentication In this lesson of the ColdFusion tutorial, you will learn... 1. To create a login form and authenticate users. 2. To use <cfquery> to send queries to a database and store result sets. 3. To use <cfoutput> to output query results.

A Database-less Login Form Below is a simple login form that hardcodes the username and password. Code Sample: DatabaseBasics/Demos/Login-noDB.cfm <cfif isDefined("FORM.submitted")> <cfif FORM.email EQ "itsme@webucator.com" AND password EQ "password"> <cflocation url="index.cfm" addtoken="no"> </cfif> </cfif> <cfparam name="FORM.email" default=""> <html> <head> <title>Login Page</title>

</head> <body> <h2>Log in</h2> <cfoutput><form method="post" action="#CGI.SCRIPT_NAME#"></cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Email:</td> <td><input type="text" name="email" value="<cfoutput>#FORM.email#</cfoutput>" size="40"></td> </tr> <tr> <td>Password:</td> <td> <input type="password" name="password" size="14"> </td> </tr> <tr> <td align="right" colspan="2"> <input type="submit" value="Log in"> </td> </tr> <tr> <td colspan="2"> <br><a href="Register.cfm">Register</a> </td> </tr> </table> </form> </body> </html> Code Explanation As you can see, this page submits to itself. If the user has not yet submitted the form, the form is displayed. If the user submits the form with the correct email and password, the page is redirected to the home page. <cfquery>

ColdFusion has a special data type called query. Recordsets returned from a database are of this data type and are called "queries". The <cfquery> tag is used to send queries to a database and to store the results returned in a query variable. <cfquery> Attributes Attribute name datasource dbtype username password maxrows blockfactor timeout Name of query. Name of data source. Only possible value is "query". Used with query of queries. Overrides username set up in ColdFusion Administrator. Overrides password set up in ColdFusion Administrator. Maximum number of rows to return in record set. Maximum rows to get at a time from server. Number of seconds that each action of a query is permitted to execute before returning an error. Description

cachedafter Date value specifying when to drop query from cache. cachedwithin Timespan for which to hold query in cache. debug Turns debugging display on or off.

The query object created by this tag has the following properties. Query Properties Property currentRow Description Current record of the query being processed.

Query Properties Property columnList recordCount Description Comma-delimited list of column names. Number of records returned by query.

executionTime Time it took to execute query. You can find out how long a query takes to process by reading the cfquery.executionTime variable. This following example is very similar to the previous one, except that the valid username and password are not hardcoded in the script but instead are searched for in a database. Code Sample: DatabaseBasics/Demos/Login.cfm <cfif isDefined("FORM.submitted")> <cfquery name="logincheck" datasource="runners"> SELECT FirstName, LastName, Email FROM Users WHERE email='#FORM.email#' AND password='#FORM.password#' </cfquery> <cfif logincheck.RecordCount> <cflocation url="index.cfm" addtoken="no"> </cfif> </cfif> ---- Code Omitted ---Exercise: Creating a Registration Page Duration: 20 to 30 minutes. In this exercise, you will create a self-submitting registration page that displays a registration form the first time a user visits the page and processes the form when the user submits it. 1. Open DatabaseBasics/Exercises/Register.cfm in your editor. Much of the file is already complete. 2. In the main <cfelse> block follow the directions in the comments. You will write code that:

o o

Validates the passwords. Inserts the new record into the database (the fields in the database have the same names as those in the form: firstname, lastname, email, password).

Code Sample: DatabaseBasics/Exercises/Register.cfm <html> <head> <title>Register</title> </head> <body> <cfif NOT isDefined("FORM.submitted")> <h2>Registration Form</h2> <cfoutput><form method="post" action="#CGI.SCRIPT_NAME#"></cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Email:</td> <td> <input type="text" name="email" size="30"> </td> </tr> <tr> <td>Password:</td> <td> <input type="password" name="password" size="10"> </td> </tr> <tr> <td>Repeat Password:</td> <td> <input type="password" name="password2" size="10"> </td> </tr> <tr> <td>First name:</td> <td> <input type="text" name="firstname" size="10"> </td>

</tr> <tr> <td>Last name:</td> <td> <input type="text" name="lastname" size="10"> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="Register"> </td> </tr> </table> </form> <cfelse> <!--Write an if statement that checks to make sure the passwords are the same. ---> <cfif WRITE_CONDITION_HERE> <!--Write a query that inserts the new record into the Users table. The fields in the database have the same names as those in the form: firstname, lastname, email, password ---> <cfquery datasource="Runners"> </cfquery> You have registered successfully. <p><a href="index.cfm">Home Page</a></p> <cfelse> <p class="errors"><b>Your passwords do not match. Please <a href= "Register.cfm">try again</a>.</p> </cfif> </cfif> </body> </html>

Write code on the registration page that first checks to see if that email is already in the Users table. If it is, let the user know she is already registered and do not insert a new record. Where is the solution? Outputting Database Data We have used <cfquery> to check for the existence of a record and to insert a new record. It is also often used to select a group of records, known as a recordset, and output them to the web page. The most common way to output the data is to use the <cfoutput> tag with its query attribute set to the relevant query name. The following example illustrates this. Code Sample: DatabaseBasics/Demos/cfoutput.cfm <cfquery name="getUsers" datasource="#APPLICATION.datasource#"> SELECT firstname, lastname, email FROM Users </cfquery> <html> <head> <title>Using cfoutput</title> </head> <body> <ul> <cfoutput query="getUsers"> <li>#firstname# #lastname# (#email#)</li> </cfoutput> </ul> </body> </html> Code Explanation The output of this page is shown below: Exercise: Using <cfoutput> to Display Query Results Duration: 10 to 20 minutes. In this exercise, you will modify the demo we have just seen so that the records are output as a table rather than as a list.

1. Open DatabaseBasics/Exercises/cfoutput.cfm in your editor. 2. Fix the code so that each record is displayed as a row as shown in the screenshot below: Where is the solution? Database Access and Authentication Conclusion In this lesson of the ColdFusion tutorial, you have learned how to connect to a database to insert and select records and to use this ability to create registration and login forms. Unfortunately, as it is written currently, only the pages themselves are protected. To protect the whole site in this manner, we would have to force the user to log in to every page. That might frustrate our visitors a bit. You will need to learn about session management to allow the user to stay logged in.

A Database-less Login Form Below is a simple login form that hardcodes the username and password. Code Sample: DatabaseBasics/Demos/Login-noDB.cfm <cfif isDefined("FORM.submitted")> <cfif FORM.email EQ "itsme@webucator.com" AND password EQ "password"> <cflocation url="index.cfm" addtoken="no"> </cfif> </cfif> <cfparam name="FORM.email" default=""> <html> <head> <title>Login Page</title>

</head> <body> <h2>Log in</h2> <cfoutput><form method="post" action="#CGI.SCRIPT_NAME#"></cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Email:</td> <td><input type="text" name="email" value="<cfoutput>#FORM.email#</cfoutput>" size="40"></td> </tr> <tr> <td>Password:</td> <td> <input type="password" name="password" size="14"> </td> </tr> <tr> <td align="right" colspan="2"> <input type="submit" value="Log in"> </td> </tr> <tr> <td colspan="2"> <br><a href="Register.cfm">Register</a> </td> </tr> </table> </form> </body> </html> Code Explanation As you can see, this page submits to itself. If the user has not yet submitted the form, the form is displayed. If the user submits the form with the correct email and password, the page is redirected to the home page. <cfquery>

ColdFusion has a special data type called query. Recordsets returned from a database are of this data type and are called "queries". The <cfquery> tag is used to send queries to a database and to store the results returned in a query variable. <cfquery> Attributes Attribute name datasource dbtype username password maxrows blockfactor timeout Name of query. Name of data source. Only possible value is "query". Used with query of queries. Overrides username set up in ColdFusion Administrator. Overrides password set up in ColdFusion Administrator. Maximum number of rows to return in record set. Maximum rows to get at a time from server. Number of seconds that each action of a query is permitted to execute before returning an error. Description

cachedafter Date value specifying when to drop query from cache. cachedwithin Timespan for which to hold query in cache. debug Turns debugging display on or off.

The query object created by this tag has the following properties. Query Properties Property currentRow Description Current record of the query being processed.

Query Properties Property columnList recordCount Description Comma-delimited list of column names. Number of records returned by query.

executionTime Time it took to execute query. You can find out how long a query takes to process by reading the cfquery.executionTime variable. This following example is very similar to the previous one, except that the valid username and password are not hardcoded in the script but instead are searched for in a database. Code Sample: DatabaseBasics/Demos/Login.cfm <cfif isDefined("FORM.submitted")> <cfquery name="logincheck" datasource="runners"> SELECT FirstName, LastName, Email FROM Users WHERE email='#FORM.email#' AND password='#FORM.password#' </cfquery> <cfif logincheck.RecordCount> <cflocation url="index.cfm" addtoken="no"> </cfif> </cfif> ---- Code Omitted ---Exercise: Creating a Registration Page Duration: 20 to 30 minutes. In this exercise, you will create a self-submitting registration page that displays a registration form the first time a user visits the page and processes the form when the user submits it. 1. Open DatabaseBasics/Exercises/Register.cfm in your editor. Much of the file is already complete. 2. In the main <cfelse> block follow the directions in the comments. You will write code that:

o o

Validates the passwords. Inserts the new record into the database (the fields in the database have the same names as those in the form: firstname, lastname, email, password).

Code Sample: DatabaseBasics/Exercises/Register.cfm <html> <head> <title>Register</title> </head> <body> <cfif NOT isDefined("FORM.submitted")> <h2>Registration Form</h2> <cfoutput><form method="post" action="#CGI.SCRIPT_NAME#"></cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Email:</td> <td> <input type="text" name="email" size="30"> </td> </tr> <tr> <td>Password:</td> <td> <input type="password" name="password" size="10"> </td> </tr> <tr> <td>Repeat Password:</td> <td> <input type="password" name="password2" size="10"> </td> </tr> <tr> <td>First name:</td> <td> <input type="text" name="firstname" size="10"> </td>

</tr> <tr> <td>Last name:</td> <td> <input type="text" name="lastname" size="10"> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="Register"> </td> </tr> </table> </form> <cfelse> <!--Write an if statement that checks to make sure the passwords are the same. ---> <cfif WRITE_CONDITION_HERE> <!--Write a query that inserts the new record into the Users table. The fields in the database have the same names as those in the form: firstname, lastname, email, password ---> <cfquery datasource="Runners"> </cfquery> You have registered successfully. <p><a href="index.cfm">Home Page</a></p> <cfelse> <p class="errors"><b>Your passwords do not match. Please <a href= "Register.cfm">try again</a>.</p> </cfif> </cfif> </body> </html>

Write code on the registration page that first checks to see if that email is already in the Users table. If it is, let the user know she is already registered and do not insert a new record. Where is the solution? Outputting Database Data We have used <cfquery> to check for the existence of a record and to insert a new record. It is also often used to select a group of records, known as a recordset, and output them to the web page. The most common way to output the data is to use the <cfoutput> tag with its query attribute set to the relevant query name. The following example illustrates this. Code Sample: DatabaseBasics/Demos/cfoutput.cfm <cfquery name="getUsers" datasource="#APPLICATION.datasource#"> SELECT firstname, lastname, email FROM Users </cfquery> <html> <head> <title>Using cfoutput</title> </head> <body> <ul> <cfoutput query="getUsers"> <li>#firstname# #lastname# (#email#)</li> </cfoutput> </ul> </body> </html> Code Explanation The output of this page is shown below: Exercise: Using <cfoutput> to Display Query Results Duration: 10 to 20 minutes. In this exercise, you will modify the demo we have just seen so that the records are output as a table rather than as a list.

1. Open DatabaseBasics/Exercises/cfoutput.cfm in your editor. 2. Fix the code so that each record is displayed as a row as shown in the screenshot below: Where is the solution? Database Access and Authentication Conclusion In this lesson of the ColdFusion tutorial, you have learned how to connect to a database to insert and select records and to use this ability to create registration and login forms. Unfortunately, as it is written currently, only the pages themselves are protected. To protect the whole site in this manner, we would have to force the user to log in to every page. That might frustrate our visitors a bit. You will need to learn about session management to allow the user to stay logged in.

Reusing Code and Writing Functions In this lesson of the ColdFusion tutorial, you will learn... 1. To include files with <cfinclude>. 2. To to use Application.cfm and OnRequestEnd.cfm. 3. To to write user-defined functions. Writing reusable code results in time and money savings, more consistent and bug free code, and the ability to hide complex code from less seasoned developers. We'll cover several basic code reuse techniques in this lesson. Including Files Including files in ColdFusion is made simple with the <cfinclude> tag, which takes a single attribute: template. The template attribute points to the file to include. ColdFusion first looks for the included file relative to the current directory. If it cannot find it, it then looks in directories mapped in ColdFusion Administrator. Syntax

<cfinclude template="path_to_file"> Note that a ColdFusion tag cannot be opened in a calling file and then closed in an included file or vice versa. The ColdFusion code in the included file must be syntactically valid on its own. A Note on Security If included files are under the web root, they can be accessed just as any other file can. If they have an extension such as .inc then the browser may display them as plain text. With other extensions, the browser may attempt to download the file. If the included file is a ColdFusion file and a user navigates to it, the server will try to process the file and may return errors. As a precaution, you may want to place your included files in a directory above or outside of the web root. This will prevent users from accessing the files directly. Code Sample: ReusingCode/Demos/index.cfm <html> <head> <title>Runners Home&trade;</title> <link href="Styles/Main.css" rel="stylesheet"> </head> <body> <cfinclude template="Includes/NavBar.cfm"> <div id="greeting"> <cfoutput>The time is #TimeFormat(Now(),"h:mm tt")# on #DateFormat(Now(), "mmmm d, yyyy")#.</cfoutput> </div> <table align="center" cellpadding="10" cellspacing="0" width="100%" height="100%" id="hometable"> ---- Code Omitted ---</table> <cfinclude template="Includes/Footer.cfm"> </body> </html> Code Explanation

The above code is relatively straightforward. It contains to included files: Includes/NavBar.cfm and Includes/Footer.cfm. Application.cfm and OnRequestEnd.cfm (see footnote) Whenever a ColdFusion page is called, the ColdFusion Application Server checks the current directory for a file called Application.cfm. If Application.cfm is not found in the current directory, ColdFusion looks for the file in the parent directory and continues to look up the tree until it gets to the root directory of the file system. As soon as the file is found, ColdFusion stops looking up the tree and prepends the found Application.cfm to the called page. Application.cfm is often used for the following tasks:


 

Control state management variables and set the application name with <cfapplication>. Set default global variables such as data source names and file paths. Set custom error handling using <cferror>.

Application.cfm should not used for including code to be output to the browser, such as a common header. When and only when an Application.cfm file is found, ColdFusion will also look for a file called OnRequestEnd.cfm in the same directory. If it finds OnRequestEnd.cfm it will append it to the called file. OnRequestEnd.cfm is sometimes used for outputting debugging information or logging information about the page. User-defined Functions User-defined functions are used to make common tasks easier and to make code more modular and easier to read. Defining and Calling Functions Functions are defined with the <cffunction> tag as follows. Like built-in functions, user-defined functions can receive arguments. Arguments are defined using the <cfargument> tag. If no default is defined with the default attribute, then the argument is required. Syntax <cffunction name="function_name" returntype="type">

<cfargument name="arg" type="type" default="default"> </cffunction> Here is an example user-defined function for adding numbers. Code Sample: ReusingCode/Demos/UDF.cfm <html> <head> <title>User-defined Function</title> </head> <body> <cfset total = addNums(1)> <cfoutput>#total#</cfoutput> </body> </html> <cffunction name="addNums" returntype="numeric"> <cfargument name="num1" type="numeric"> <cfargument name="num2" type="numeric" default="0"> <cfargument name="num3" type="numeric" default="0"> <cfset sum=num1 + num2 + num3> <cfreturn sum> </cffunction> Code Explanation Notice that user functions are called in the same way as built-in functions. Exercise: Creating a File2Table Function Duration: 20 to 30 minutes. In this exercise, you will modify RunningLog.cfm to include a function that creates a table from a tab-delimited text file. 1. Open ReusingCode/Exercises/RunningLog.cfm in your editor. 2. Create a function called File2Table that takes two arguments: the path to the file and an array of the table headers. 3. Modify the page so that the function contains all the processing code that loads the file and creates and returns an HTML table. 4. If the file cannot be opened, the function should return "File not found". 5. the function and send the result to the browser.

Add formatting functionality to the function. For example, make the border size and the background color of the table controlled by the function call. The function currently has the headers hardcoded in. This is not great for reuse. Change this so the function excepts a Headers array and loops through the array to output the headers. Where is the solution? Exercise: Creating a Function Library Duration: 5 to 10 minutes. In this exercise, you will move the function you created in the last exercise into a function library and then include that library in RunningLog2.cfm with <cfinclude>. 1. Open ReusingCode/Exercises/RunningLog.cfm in your editor and save it as RunningLog2.cfm. 2. Cut the File2Table function and paste it in a new file. Save the new files as Functions.cfm. 3. Add code to RunningLog2.cfm so that it includes the new function library. Where is the solution? Reusing Code and Writing Functions Conclusion Using includes and functions are two common ways of writing reusable code. Very often developers will save common functions in an include file (e.g, functions.inc), so they don't have to copy and paste functions into scripts to make use of them. Footnotes 1. These are covered more in the lesson on Session and Application Management

Session & Application Management In this lesson of the ColdFusion tutorial, you will learn... 1. To understand the difference between ColdFusion session management and J2EE session management. 2. To use the <cfapplication> tag is used to set defaults for session state management. 3. To use Application variables. 4. To use session variables to remember that the users are logged in as they go from page to page. 5. To understand the basics of structures. 6. To use cookies to make it easier for users to log in on future visits. Sessions A session begins when a visiting client somehow identifies itself to the web server. The web server assigns the client a unique session id, which the client uses to re-identify itself as it moves from page to page on the website. Most of the time, these unique ids are stored in session cookies that expire after the client hasn't interacted with the server for some amount of time. The amount of time varies depending on the web application. For example, an online investment site might have very short sessions, so that if a user leaves her computer without logging out, another user who sits down at the same computer several minutes later cannot continue with the first user's session. Configuring Sessions The picture above shows the Memory Variables settings in ColdFusion Administrator, where Session management is configured. J2EE Session Variables The first checkbox gives the option of using J2EE session variables instead of ColdFusion session variables. The two major advantages of using J2EE session variables are: 1. Sessions end when a user closes the browser. This is not the case with ColdFusion session variables. 2. The Session scope is serializable, which allows session variables to be shared across servers. Again, this is not the case with ColdFusion session variables.

There is really no downside to using J2EE session variables, so it is recommended that you check this box. Enabling Application Variables The second checkbox gives the option to enable Application variables. Application variables are shared between all users of the application and live until ColdFusion Application Server is shut down, they are explicitly deleted, or the application times out due to a setting in <cfapplication>. Application variables are useful for storing data such as data source names and file paths that are the same for all users over the life of the application, so it is recommended that you check this box. Enabling Session Variables The third checkbox gives the option to enable Session variables. Session variables are variables that are specific to a user's session (or visit) on the site. If session variables are not enabled, it becomes very difficult to track a user throughout his visit, so it is recommended that you check this box as well. The <cfapplication> Tag The <cfapplication> tag is used to: 1. 2. 3. 4. 5. Define the name of a ColdFusion application. Enable or disable Client variables. Specify where Client variables are stored. Enable Session variables. Set Application variable timeouts. <cfapplication> Attributes Attribute name loginStorage Name of application. Where to store login variables (in Cookies or Session). Description

clientManagement Enable client variables. Default is "No".

<cfapplication> Attributes Attribute clientStorage setClientCookies Description Where to store client variables. Enable cookies. Default is "Yes".

sessionManagement Enable session management. Default is "No". sessionTimeout Lifespan of session variables.

applicationTimeout Lifespan of application variables. setDomainCookies Sets CFID and CFTOKEN for a domain.

The <cfapplication> tag is most often used at the top of the Application.cfm file. A typical <cfapplication> tag looks like this: <cfapplication sessionmanagement="yes" clientmanagement="yes" name="RunnersHome"> If sessionTimeout and applicationTimeout are not defined in the <cfapplication> tag, as in the tag above, the default values set in ColdFusion Administrator are used. Exercise: Creating an Application.cfm Page Duration: 5 to 10 minutes. In this exercise, you will create a simple Application.cfm page. 1. Open a new page and save it as Application.cfm in the SessionAndApplication/Exercises folder. 2. Use the <cfapplication> tag to: o Name of a application "RunnersHome". o Enable Client variables. o Enable Session variables. 3. Write an if condition to check if an Application variable named datasource is defined. If it is not, set an Application variable called datasource with the value of runners. Where is the solution?

Basics of Structures A ColdFusion structure is a data type that can be used to hold collections of like data. ColdFusion provides many functions to make it easy to work with and manipulate structures. A few of these functions are described in the table below. Structure Functions Function IsStruct StructNew Description Returns true if the specified variable is a structure. Creates a new structure.

StructClear Removes all data from specified structure. StructDelete Removes the specified item from the specified structure. StructFind Returns the value associated with the specified key in the specified structure.

The example below creates a structure called person, adds properties to the structure, deletes a property and then loops through a substructure. Code Sample: SessionAndApplication/Demos/Structure.cfm <html> <head> <title>Structure Demo</title> </head> <body> <cfset person = StructNew()> <cfset person.firstName = "Paul"> <cfset person.lastName = "McCartney"> <cfset person.age = 61> <cfset person.talents.sing = True> <cfset person.talents.playsGuitar = True> <cfset person.talents.bungyJumps = False> <cfset StructDelete(SESSION, "username")>

<cfoutput><p>Structure Elements: #StructCount(person)#</p></cfoutput> <ul> <cfloop collection="#person.talents#" item="talent"> <cfoutput><li>#talent#: #person.talents[talent]#</li></cfoutput> </cfloop> </ul> </body> </html> The Session, Application, and Client scopes are all available as ColdFusion structures, which means that they can be manipulated using the Structure functions. Session Example Code Sample: SessionAndApplication/Demos/Session1.cfm <cfset SESSION.SessVar = "Hello world!"> <html> <head> <title>Session Page 1</title> </head> <body> The content of SESSION.SessVar is <cfoutput>#SESSION.SessVar#</cfoutput>. <a href="Session2.cfm">Next page</a> </body> </html> Code Explanation This page sets and outputs a session variable. Code Sample: SessionAndApplication/Demos/Session2.cfm <html> <head> <title>Session Page 2</title> </head>

<body> The content of SESSION.SessVar is still <cfoutput>#SESSION.SessVar#</cfoutput>. <cfset StructDelete(Session,"SessVar")> <a href="Session3.cfm">Next page</a> </body> </html> Code Explanation This page outputs the same session variable and then deletes it with StructDelete(). Code Sample: SessionAndApplication/Demos/Session3.cfm <html> <head> <title>Session Page 3</title> </head> <body> The content of SESSION.SessVar is no longer <cfoutput>#SESSION.SessVar#</cfoutput>. </body> </html> Code Explanation This page tries to read the deleted session variable and fails. It errors with a message like " Element SESSVAR is undefined in SESSION." Cookies Cookies are small text files that sit on the client machine. Web pages with the right permissions can read from and write to cookies. They are generally used to track user information between visits. In ColdFusion, cookies are set with the <cfcookie> tag, which takes several attributes. <cfcookie> Attributes Attribute Description

<cfcookie> Attributes Attribute name value expires secure path Description The cookie's name (required). The cookie's value. The cookie's expiration date (if this isn't set, the cookie will expire when the browser window is closed). A flag indicating whether the cookie should only be read over https. The directory path on the server that can read the cookie.

domain The domain name that can read the cookie. The following code will set a cookie that expires in one week. <cfcookie name="firstname" value="Paul" expires="7"> The following code will set a cookie that expires in on December 31, 2008. <cfcookie name="firstname" value="Paul" expires="12/31/08"> The following code will set a cookie that never expires. <cfcookie name="firstname" value="Paul" expires="never"> To delete a cookie, set the expiration date to now(), like this. <cfcookie name="firstname" value="Paul" expires="now"> Exercise: Authentication with Session Control Duration: 45 to 60 minutes. In this exercise, you will create a login form that allows a user to log in to a site, rather than just a page on the site. You will also modify several other pages so that their content changes based on whether or not the user is logged in.

1. Open SessionAndApplication/Exercises/Login.cfm. 2. If the user logs in successfully, set session variables for firstname, lastname, and userid with values from the query. Then redirect to index.cfm. 3. In the user's log in fails, set a variable called badlogin to true. 4. Open SessionAndApplication/Exercises/index.cfm. 5. Write code that o Sets the firstname session variable with the value of "Stranger" if it doesn't already exist. o Changes the sentence that indicates the time and date to include the user's first name (e.g, "John the time is..."). o Replaces the login form with "Logged in as firstname lastname" if the user is logged in. 6. Open SessionAndApplication/Exercises/Logout.cfm. o Write code to log the user out. You'll need to use StructDelete(). 7. Open SessionAndApplication/Exercises/Includes/Navbar.cfm. o Change the code so that the last link is to Logout.cfm if the user is logged in and to Login.cfm if she is not. o If the user is logged in, add an additional link to MyAccount.cfm, which has been created for you. 8. Open SessionAndApplication/Exercises/Register.cfm. o Modify it to automatically log the user in on a successful registration. 9. When you have completed your work, test the site: o Go to SessionAndApplication/Exercises/Register.cfm and create a new account. o Click on the link to the home page. You should be logged in. o Click on the Logout link. You should be logged out. o Click on the Login link and login. Write code so that the user can indicate that she would like to be remembered between visits. If she chooses to be remembered, she should never have to login again. You will have to modify Login.cfm, Application.cfm and Logout.cfm. Where is the solution? Session & Application Management Conclusion

Session management is a key aspect necessary to create "web applications" from sets of web pages. In this lesson, you have learned to work with Session and Application variables and cookies. You have also learned how to use the <cfapplication> tag and how to work with structures.

Working with Files and Directories In this lesson of the ColdFusion tutorial, you will learn... 1. 2. 3. 4. 5. To work with <cffile> to read from and write to files. To upload new files to the server. To work with ColdFusion's file functions. To work with <cfdirectory> to list the contents of a directory. To work with ColdFusion's directory functions.

Most Web applications use databases to store large amounts of data. However, in some cases, it will be necessary to store data in or access data from files. In this lesson, you will learn how to read from and write to files on the server. Using <cffile> The <cffile> tag is used for working with files. The action attribute of <cffile> dictates what action will be performed on the file in question. The table below describes the different actions that can be performed. <cffile> Actions Action read Description Reads a text file on the server into a local variable.

read binary Reads a binary file on the server into a local variable. write append Writes a text file to the server. Appends text to a text file on the server.

<cffile> Actions Action move rename copy delete upload Description Moves a file on the server from one directory to another. Renames (or moves) a file on the server. Copies a file on the server. Deletes a file from the server. Saves a file to a directory the server.

Reading from a File When reading from a file, <cffile> requires three attributes: <cffile> Attributes for Reading Files Attribute action file Description Must be set to read. The absolute path to the file to be read.

variable The variable to hold the contents of the file. In this lesson of the ColdFusion tutorial, we will be working with FilesAndDirs/Logs/RunningLog.txt, which is a tab-delimited text file. Each line is formatted as follows: FirstName#chr(9)#LastName#chr(9)#time#chr(9)#comments# chr(10)##ch r(13)# The file is divided into "columns" using tabs (chr(9)) and rows using hard returns (chr(10)chr(13)). The file is shown below. Code Sample: FilesAndDirs/Demos/Logs/RunningLog.txt 11/10/07 3 miles 26:34 Man, am I out of shape 11/12/07 3.1 miles 28:23 I'm still sore from last time

11/24/07 2.5 miles 23:44 It's like I haven't run for two weeks 12/24/07 4.1 miles 34:02 I'll be in shape by Christmas! The code below reads and displays the file in the browser. Code Sample: FilesAndDirs/Demos/RunningLog.cfm <html> <head> <title>Running Log</title> </head> <body> <h1>Running Log</h1> <a href="AddEntry.cfm">Add Entry</a><hr/> <cfset RunningLogPath = ExpandPath("Logs/RunningLog.txt")> <cfset CrLf = chr(10) & chr(13)> <cfif FileExists(RunningLogPath)> <cffile action="read" file="#RunningLogPath#" variable="myfile"> <cfloop list="#myfile#" index="run" delimiters="#CrLf#"> <cfoutput>#run#<br/></cfoutput> </cfloop> <cfelse> You have apparently never been running. </cfif> </body> </html> Code Explanation Let's look at the ColdFusion code step by step. First, we create a variable that holds the absolute path to RunningLog.txt. Remember, the file attribute of <cffile> requires an absolute path to the file. <cfset RunningLogPath = ExpandPath("Logs/RunningLog.txt")> Next, we create a variable called CrLf that holds a carriage return/line feed. We'll use this as the delimiter when we loop through the lines in the file.

<cfset CrLf = chr(10) & chr(13)> Next, we use the FileExists() function to check to see if RunningLog.txt exists. If it does exist, we'll loop through it outputting the lines to the page. Otherwise, we output "You have apparently never been running." <cfif FileExists(RunningLogPath)> Next, we use the <cffile> tag to read the contents of the file into the myfile variable. <cffile action="read" file="#RunningLogPath#" variable="myfile"> Finally, we loop through the file using the CrLf variable as the delimiter. <cfloop list="#myfile#" index="run" delimiters="#CrLf#"> <cfoutput>#run#<br></cfoutput> </cfloop> Writing and Appending to Files

When writing or appending to a file, <cffile> requires three attributes: <cffile> Attributes for Writing Files Attribute action file output Description Must be set to write or append. The absolute path to the file to write. The text to write to the file.

The addnewline attribute is also useful. It takes a Yes/No value and determines whether a hard return should be appended to the output. Note: When ColdFusion tries to append to a file that doesn't exist, it will create the file if it has permission to do so and the specified directory exists.

Exercise: Writing to a File Duration: 15 to 25 minutes. In this exercise you will write code to append entries to the RunningLog.txt. 1. Open FilesAndDirs/Exercises/AddEntry.cfm in your editor. 2. Write code to save the entry in Logs/RunningLog.txt. Code Sample: FilesAndDirs/Exercises/AddEntry.cfm <html> <head> <title>Running Log</title> </head> <body> <!--Check to see if the form has been submitted. ---> <cfif WRITE_CONDITION_HERE> <!--Write code to append the entry to Logs/RunningLog.txt ---> <h1 align="center">Entry added</h1> <a href="RunningLog.cfm">Running Log</a> </cfif> <h1 align="center">Add Entry</h1> <cfoutput><form method="post" action="#CGI.SCRIPT_NAME#"></cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Date:</td> <td><input type="text" name="date" size="20"></td> </tr> <tr> <td>Distance:</td> <td><input type="text" name="distance" size="20"></td> </tr> <tr> <td>Time:</td>

<td><input type="text" name="time" size="20"></td> </tr> <tr> <td>Comments:</td> <td><input type="text" name="comments" size="50"></td> </tr> <tr> <td colspan="2" align="right"> <input type="submit" name="Add Entry"> </td> </tr> </table> </form> </body> </html> Add errror handling (not shown in solution). Where is the solution? Uploading a New File When uploading a new file, <cffile> requires three attributes: <cffile> Attributes for Uploading Files Attribute action fileField Must be set to upload. The form variable that contains the uploaded file. Description

destination The absolute path to the directory in which to save the file. Two other useful, but not required, attributes are nameconflict and accept. Additional <cffile> Attributes for Uploading Files Attribute Description

Additional <cffile> Attributes for Uploading Files Attribute nameconflict accept Description Possible values are error, overwrite, skip, and makeunique. error is the default. A list of file types that the can be uploaded.

The following demo allows a user to upload a new log file to the Logs directory. The screenshot below shows the form. And here is the results page. Code Sample: FilesAndDirs/Demos/AddLog.cfm <html> <head> <title>Add New Log</title> </head> <body> <cfif isDefined("FORM.submitted")> <cfset uploaddir = ExpandPath("Logs")> <cffile action="upload" filefield="FORM.logfile" destination="#uploaddir#" accept="text/plain" nameconflict="#FORM.conflict#"> <h2>Log Added</h2> <a href="RunningLogList.cfm">See All Running Logs</a> <cfelse> <h1>Add New Log</h1> <cfoutput> <form method="post" enctype="multipart/form-data" action="#CGI.SCRIPT_NAME#"> </cfoutput> <input type="hidden" name="submitted" value="true"> <p>Log: <input type="file" name="logfile" accept="text/plain"></p> <p>What do you want to do if a file by this name already exists?</p>

<select name="conflict"> <option value="makeunique">Create New Name</option> <option value="overwrite">Overwrite Existing File</option> <option value="error">Don't Overwrite and Report Error</option> <option value="skip">Don't Overwrite and Ignore</option> </select> <p align="center"><input type="submit" value="Add Log"></p> </form> </cfif> </body> </html> Code Explanation Things to note about this code:
 





This page has a self-submitting form. The <form> tag takes the attribute enctype with the value set to multipart/form-data. This allows file uploads. The form has a file input field, which allows the user to browse to a file on the client machine. The <cffile> tag uses this form variable as its filefield. The form has a select menu, which allows the user to determine how a file name conflict is handled. The <cffile> tag uses this form variable as the value of its nameconflict attribute.

File Functions The table below shows some useful ColdFusion file functions. File Functions Function FileExists(path_to_file) Explanation Returns true if the file exists; false if it doesn't.

File Functions Function ExpandPath(path_to_file) Explanation Returns an absolute path to the file.

GetFileFromPath(path_to_file) Returns the file name from an absolute path. Using <cfdirectory> The <cfdirectory> tag is used for working with directories. The action attribute of <cfdirectory> dictates what action will be performed on the directory in question. The table below describes the different actions that can be performed. <cfdirectory> Actions Action list Description Reads the contents of a directory on the server into a query object.

create Creates a new directory on the server. move Moves a directory on the server from one location to another.

rename Renames (or moves) a directory on the server. Listing Directory Contents The following demo shows how to list the contents in a directory with <cfdirectory>. Code Sample: FilesAndDirs/Demos/RunningLogList.cfm <html> <head> <title>Running Logs</title> </head> <body> <h1>Running Logs</h1> <cfdirectory action="list" directory="#ExpandPath('Logs')#" name="loglist">

<table border="1"> <tr> <th>File Name</th> <th>File Size</th> <th>File or Directory</th> <th>Date Last Modified</th> </tr> <cfoutput query="loglist"> <tr> <td><a href="RunningLog2.cfm?log=#name#">#name#</a></td> <td>#size#</td> <td>#type#</td> <td>#DateLastModified#</td> </tr> </cfoutput> </table> </body> </html> Code Explanation Things to note about this code:








The ExpandPath() function is used in the directory attribute of the <cfdirectory> tag to find the absolute path to the logs file. <cfdirectory> returns the directory list as a query object just as a query to a database would. The <cfoutput> tag is used with the query attribute set to the name specified in <cfdirectory> to loop through the directory contents. Columns of the result set returned by <cfdirectory> include Name, Size, Type, DateLastModified. The file name is linked to RunningLog-2.cfm with the file name sent on the query string.

RunningLog-2.cfm is the same as RunningLog.cfm except that the file it reads is determined from the value passed in the query string. The code follows. Code Sample: FilesAndDirs/Demos/RunningLog-2.cfm <cfparam name="URL.log" default="RunningLog.txt"> <html>

<head> <title>Running Log</title> </head> <body> <h1>Running Log</h1> <a href="AddEntry.cfm">Add Entry</a><hr/> <cfset RunningLogPath = ExpandPath("Logs/#URL.log#")> <cfset CrLf = chr(10) & chr(13)> <cfif FileExists(RunningLogPath)> <cffile action="read" file="#RunningLogPath#" variable="myfile"> <cfloop list="#myfile#" index="run" delimiters="#CrLf#"> <cfoutput>#run#<br></cfoutput> </cfloop> <cfelse> You have apparently never been running. </cfif> </body> </html> Directory Functions The table below shows some useful ColdFusion directory functions. Directory Functions Function DirectoryExists(path_to_dir) ExpandPath(path_to_dir) GetDirectoryFromPath(path_to_dir) Explanation Returns true if the directory exists; false if it doesn't. Returns an absolute path to the directory. Returns the directory from an absolute path.

Working with Files and Directories Conclusion In this lesson of the ColdFusion tutorial, you have learned to read from and manipulate files and directories on the server. Although writing to and

reading from files can be useful in certain situations, when it is important to be able to access and change data quickly and to maintain the integrity of that data, it is often better to use a database. We will learn about databases later in the course.

Sending Email with ColdFusion In this lesson of the ColdFusion tutorial, you will learn... 1. 2. 3. 4. To configure email settings in ColdFusion Administrator. To use <cfmail> to send confirmation emails and password reminders. To send email as plain text or HTML. To include email attachments.

Configuring Settings Some of the mail server settings that can be set in ColdFusion Administrator are listed below. To modify these settings, open ColdFusion Administrator and click on the Mail menu. Mail Server Settings
 









Mail Server - the default mail server Verify Mail Server Connection - when checked, verifies that ColdFusion can connect to the mail server when the "Submit Changes" button is clicked. Backup mail servers (Enterprise edition only) - a comma-delimited list of backup servers to try if the default mail server is inaccessible. Maintain Connection to Mail Server (Enterprise edition only) - when checked, mail server connections are kept open after sending a mail message, which improves performance when delivering multiple messages. Connection Timeout - the number of seconds to wait for a response from the mail server. Server Port - usually 25.

Mail Spooling Settings








Spool Interval - number of seconds mail server waits to process spooled mail. Mail Delivery Threads (Enterprise Edition only) - the maximum number of simultaneous threads used to deliver spooled mail. Spool mail messages for delivery - when checked, mail messages are sent to the mail spooler for delivery. When unchecked, ColdFusion tries to deliver the message right away. Generally, you will want this checked. Maximum number of messages spooled to memory (Enterprise Edition only) - the maximum number of messages ColdFusion MX should spool to memory before switching to disk spooling. Memory spooling is only available in the Enterprise Edition.

Mail Logging Settings




Error Log Severity - the severity of SMTP errors ColdFusion should log to the mail.log log. Log all mail messages sent by ColdFusion - when checked, all messages are logged to the mailsent.log log (To, From, and Subject fields only).

Mail Character Set Settings Default CFMail Charset - the character set used by the <cfmail> tag. UTF-8 for most languages. Using <cfmail> Emails are sent with the <cfmail> tag. The table below shows some of <cfmail>'s most common attributes. <cfmail> Attributes Attribute to from subject Description Required. Recipient email address. Required. Sender email address. Required. Email subject.

<cfmail> Attributes Attribute cc bcc replyto type Description Email addresses to be copied. Email addresses to be blind copied. Address to receive replies. HTML. If omitted, the email will be sent as plain text.

username SMTP username password SMTP password query server timeout Query to use when sending multiple emails. SMTP server address. Overrides setting in ColdFusion Administrator. Seconds to wait before timing out. Overrides setting in ColdFusion Administrator.

Here is an example of a simple <cfmail> tag. Code Sample: Email/Demos/SimpleMail.cfm <cfmail to="ndunn@webucator.com" from="info@runnershome.com" subject="Hello There">Hello there! Thanks for visiting. Best regards, RunnersHome</cfmail> Code Explanation Expressions in pound signs within <cfmail> tags are evaluated. <cfoutput> tags are not required and will likely cause an error or undesired results. For this file to work, you must have a valid mail server set up in ColdFusion Administrator. Alternatively, you can configure the <cfmail> tag to select

the server using the server, username, and password attributes. Your mail server may also require a valid from email. Sending a Confirmation Email The code sample below shows how to send a confirmation email with <cfmail>. Code Sample: Email/Demos/Register.cfm ---- Code Omitted ---<cfif FORM.password EQ FORM.password2> <cfquery name="emailcheck" datasource="#APPLICATION.datasource#"> SELECT * FROM Users WHERE email='#FORM.email#' </cfquery> <cfif emailcheck.RecordCount EQ 0> <cfquery datasource="#APPLICATION.datasource#"> INSERT INTO Users (firstname, lastname, email, password) VALUES ('#FORM.firstname#', '#FORM.lastname#', '#FORM.email#', '#FORM.password#') </cfquery> <cfset SESSION.firstname = FORM.firstname> <cfset SESSION.lastname = FORM.lastname> <cfset SESSION.userid = emailcheck.userid> <cfmail to="#FORM.email#" from="runners@runnershome.com" subject="Successful Registration"> Congratulations! You have successfully registered for Runners Home! </cfmail> Thanks for registering! <cfelse> <p>It appears you have already registered.</p> </cfif> <cfelse> <p class="errors"><b>Your passwords do not match. Please <a href= "Register.cfm">try again</a>.</p> </cfif>

---- Code Omitted ---Sending Email as HTML Email can be sent in plain text format or in HTML format. The default is plain text format. Setting the type attribute of the <cfmail> tag can be set to "html" (only possible value) indicates that the message is in HTML format. You can then use HTML tags within the body of the message. HTMLenabled email clients will render the message as an HTML page. Using <cfmailpart> Of course, you do not always know what type of email client the mail recipient will have. By nesting <cfmailpart> tags within <cfmail>, you can provide alternative email messages in plain text and HTML format. <cfmailpart> Attributes Attribute type Description Required. Options are text, plain, and html. text and plain both specify text/plain format. html specifies HTML format. Used for plain text emails to specify the number of characters per line. If omitted, text will not wrap.

wraptext

charset Character encoding. Code Sample: Email/Demos/Register-2.cfm ---- Code Omitted ---<cfmail to="#FORM.email#" from="runners@runnershome.com" subject="Successful Registration"> <cfmailpart type="text" wraptext="72"> Congratulations! You have successfully registered for Runners Home! </cfmailpart> <cfmailpart type="html">

<b>Congratulations!</b> You have successfully registered for Runners Home!<br> <img src="http://www.astroleague.org/al/regional/congratsfireworks.gif"> </cfmailpart> </cfmail> ---- Code Omitted ---Exercise: Sending a Password by Email Duration: 15 to 25 minutes. In this exercise, you will create a Password Reminder page that allows users to have their passwords sent to them by email. 1. Open Email/Exercises/PasswordReminder.cfm in your editor. 2. Write code that looks for the user's password in the users table. 3. If the password is found, email it to the user and return an appropriate message to the browser. Send the message in both HTML and plain text format. 4. If the password is not found (i.e, there is no such email address in the users table), return a message saying no such user exists. 5. You will need to specify a valid mail server. 6. When you are done, try it out in your browser. Code Sample: Email/Exercises/PasswordReminder.cfm <cfparam name="FORM.email" default=""> <html> <head> <title>Password Reminder</title> </head> <body> <cfif NOT isDefined("FORM.submitted")> <h2>Password Reminder</h2> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> </cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Email:</td>

<td> <input type="text" name="email" value="<cfoutput>#FORM.Email#</cfoutput>" size="30"> </td> </tr> <tr> <td colspan="2" align="right"> <input type="submit" value="Remind Me"> </td> </tr> </table> </form> <cfelse> <!--Write code that looks for the user's password in the Users table. If the password is found, email it to the user. If the password is not found (i.e, there is no such email address in the Users table), return a message saying no such user exists. ---> </cfif> </body> </html> Where is the solution? Attaching Files Files can be attached using the <cfmailparam> tag. The syntax is as follows: <cfmailparam file = "#ExpandPath("Logs/RunningLog.txt")#" type="image/gif"> There is no limit to the number of <cfmailparam> tags that can be included within a <cfmail> tag. Sending Email with ColdFusion Conclusion In this lesson of the ColdFusion tutorial, you learned to administer and send email using ColdFusion.

Managing Data In this lesson of the ColdFusion tutorial, you will learn... 1. 2. 3. 4. 5. To display data retrieved from a database. To insert data into a database. To update existing data in a database. To delete data from a database. To manage site permissions through assigning users to roles.

Creating a User Administration Page In this section, you will learn how to create a user administration page that will display all of the users' information within a table structure. The page will consist of multiple forms that allow you to edit user information, delete users and add new users. The page will look like this: Displaying Users The page is divided into two parts: 1. A section for adding a new user. 2. A section for displaying, editing and deleting existing users. This first demo shows how to perform the query and display the results as form fields. Note the use of the query attribute in the <cfoutput> tag. This instructs ColdFusion to treat the <cfoutput> like a <cfloop>, looping through each record returned by the query. Code Sample: ManagingData/Demos/Admin.cfm <cfquery name="getUsers" datasource="#APPLICATION.datasource#"> SELECT userid, firstname, lastname, email, role FROM Users </cfquery>

<html> <head> <title>Admin Page</title> <style> .admin {color:#990000} .user {color:#000000} </style> </head> <body> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> </cfoutput> <table cellpadding="3" cellspacing="0" id="maintable" width="700"> <tr> <th>First Name</th> <th>Last Name</th> <th>Email</th> <th>Role</th> <th colspan="2">Action</th> </tr> <tr> <td><input type="text" name="firstname" size="15"></td> <td><input type="text" name="lastname" size="15"></td> <td><input type="text" name="email" size="30"></td> <td> <select name="role"> <option value="admin" class="admin">Admin</option> <option value="user" selected class="user">User</option> </select> </td> <td colspan="2" align="center" width="120"> <input name="add" type="submit" value="Add User" style="font-size:xx-small"> </td> </tr> <tr><td colspan="6"><hr></td></tr>

</table> </form> <cfoutput query="getUsers"> <form method="post" style="margin:0px" action="#CGI.SCRIPT_NAME#"> <table width="700"> <tr> <td> <input type="text" name="firstname" value="#firstname#" size="15" class="#role#"> </td> <td> <input type="text" name="lastname" value="#lastname#" size="15" class="#role#"> </td> <td> <input type="text" name="email" value="#email#" size="30" class="#role#"> </td> <td> <cfif role EQ "admin"> <cfset adminselected = "selected"> <cfset userselected = ""> <cfelse> <cfset userselected = "selected"> <cfset adminselected = ""> </cfif> <select name="role"> <option value="admin" #adminselected# class="admin">Admin</option> <option value="user" #userselected# class="user">User</option> </select> </td> <td width="120"> <input type="submit" value="EDIT" style="fontsize:xx-small" class="#role#"> <input type="submit" value="DELETE" style="fontsize:xx-small" class="#role#"> </td> </tr> </table>

</form> </cfoutput> <cfinclude template="Includes/Footer.cfm"> </body> </html> Adding Users We will now add code to try to insert users when the Add User button is pressed. As we'll be processing edits and deletes with this same page, we must detect not only that a form has been submitted, but which form it was. We will want to email the new user an auto-generated password, so we will include Includes/Functions.cfm, which contains generatePassword() and sendPassword() functions. Code Sample: ManagingData/Demos/Admin2.cfm <cfinclude template="Includes/Functions.cfm"> <cfif isDefined("FORM.inserting")> <cfset password=GeneratePassword(10)> <cfset sendPassword(FORM.email,password)> <cfquery datasource="#APPLICATION.datasource#"> INSERT INTO Users (email, password, firstname, lastname, role) VALUES ('#FORM.email#', '#password#','#FORM.firstname#','#FORM.lastname#','#FO RM.role#') </cfquery> </cfif> ---- Code Omitted ---Editing and Deleting Users Now we will add the code to edit and delete users. Note that we need to be able to determine which submit button was pushed. We do this by giving the submit buttons names. The name-value pair of the button that is pressed will be sent to the server. Code Sample: ManagingData/Demos/Admin3.cfm <cfinclude template="Includes/Functions.cfm">

<cfif isDefined("FORM.inserting")> <cfset password=GeneratePassword(10)> <cfset sendPassword(FORM.email,password)> <cfquery datasource="#APPLICATION.datasource#"> INSERT INTO Users (email, password, firstname, lastname, role) VALUES ('#FORM.email#', '#password#','#FORM.firstname#','#FORM.lastname#','#FO RM.role#') </cfquery> <cfelseif isDefined("FORM.editing")> <cfquery datasource="#APPLICATION.datasource#"> UPDATE Users SET firstname='#FORM.firstname#', lastname='#FORM.lastname#', email='#FORM.email#', role='#FORM.role#' WHERE userid=#FORM.userid# </cfquery> <cfelseif isDefined("FORM.deleting")> <cfquery datasource="#APPLICATION.datasource#"> DELETE FROM Users WHERE userid=#FORM.userid# </cfquery> </cfif> ---- Code Omitted ---Controlling Access to the Page As we only want users who are admins to be able to view this page, we need to check the user's role before displaying the page. We do this by checking SESSION.role. Note that we also had to add code to Login.cfm to set the SESSION.role variable as shown in the files below. Code Sample: ManagingData/Demos/Login.cfm <cfif isDefined("FORM.submitted")> <cfquery name="logincheck" datasource="#APPLICATION.datasource#"> SELECT firstname, lastname, userid, role

FROM Users WHERE email='#FORM.email#' AND password='#FORM.password#' </cfquery> <cfif logincheck.RecordCount EQ 1> <cfset SESSION.firstname = logincheck.firstname> <cfset SESSION.lastname = logincheck.lastname> <cfset SESSION.userid = logincheck.userid> <cfset SESSION.role = logincheck.role> <cfif isDefined("FORM.rememberme")> <cfcookie name="loggedin" value="#logincheck.userid#" expires="7"> </cfif> <cflocation url="index.cfm" addtoken="no"> <cfelse> <cfset badlogin=true> </cfif> </cfif> <cfparam name="FORM.email" default=""> <html> <head> <title>Login Page</title> </head> <body> <h2>Log in</h2> <cfif isDefined("badlogin")> <p class="errors"><b>That is not the correct email and password. Please <a href="Login.cfm">try again</a>.</p> </cfif> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> </cfoutput> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Email:</td> <td><input type="text" name="email" value="<cfoutput>#FORM.email#</cfoutput>" size="40"></td> </tr> <tr>

<td>Password:</td> <td> <input type="password" name="password" size="14"> </td> </tr> <tr> <td colspan="2"> <input type="checkbox" name="rememberme"> Remember Me </td> </tr> <tr> <td align="right" colspan="2"> <input type="submit" value="Log in"> </td> </tr> </table> </form> <cfinclude template="Includes/Footer.cfm"> </body> </html> Aborting the Page for Unpermitted Visitors We can abort the execution of a page when users who are not permitted try to access it with the <cfabort> tag. Code Sample: ManagingData/Demos/Admin4.cfm <cfif NOT isDefined("SESSION.role") or SESSION.role NEQ "admin"> You do not have permissions to view this page. <cfabort> </cfif> ---- Code Omitted ---Providing Convenient Access to the Admin Page To make it convenient to access the admin page, we added a link to the footer that only appears when an "admin" user is logged in.

Code Sample: ManagingData/Demos/Includes/Footer.cfm <br><hr width="350" align="right"> <div align="right" id="copyright">&copy; 2007 Runners Home. All rights reserved. <a href="index.cfm">Home</a> | <a href="Races.cfm">Races</a> | <a href="Resources.cfm">Resources</a> | <cfif isDefined("SESSION.userid")> <a href="Logout.cfm">Log out</a> <cfelse> <a href="Login.cfm">Log in</a> </cfif> <cfif isDefined("SESSION.role") AND SESSION.role EQ "admin"> | <a href="Admin4.cfm">Admin</a> </cfif> </div> To see how the applicatoin now works:






Open ManagingData/Demos/index.cfm and log in as George Washington (gwashington@whitehouse.gov / password). You should have a link on the footer to the Admin page. Click the link. You should be allowed access. Click the Logout link on the footer. You should be logged out and returned to the home page. Log in again as John Quincy Adams (jqadams@whitehouse.gov / password). The Admin link should not be present.

Important Note The admin page has a very serious problem. It allows an admin to delete himself or to change his status from admin to user. This could create a situation in which there were no admins left to administer the users. In production code, you would want to take measures to prevent this from happening. Allowing Users to Add and Edit Data

Displaying Data In this section, you will learn how to display records from a database and control read/write access to individual records based on a user's role and his relationship to the record. The following rules will apply: 1. All users will be able to see active races. 2. All logged-in users will be able to add races. 3. "User" users will only be able to edit and delete races that they added themselves. 4. "Admin" users will be able to see, edit, and delete all races. The interface will look like this when a "User" user logs in: This first demo displays all the races, but they are not yet sortable. It also allows all users to edit all data. Code Sample: ManagingData/Demos/Races.cfm <cfquery name="getraces" datasource="#APPLICATION.datasource#"> SELECT raceid, racename, racetime, distance, city, state, active, u.userid, firstname, lastname, email FROM Users u, Races r WHERE u.userid = r.userid AND active=1 </cfquery> <html> <head> <title>Running Races</title> </head> <body> <table border="1" cellpadding="3" cellspacing="0" id="maintable"> <tr> <th>Race</th> <th>Location</th> <th>Distance</th> <th>Date & Time</th> <th>Added by</th> <th>Edit/Delete</th> </tr>

<cfoutput query="getraces"> <tr> <td>#racename#</td> <td>#city#, #state#</td> <td>#distance#</td> <td>#DateFormat(racetime, "ddd, mmm d, yyyy")# at #TimeFormat(racetime,"h:mm tt")#</td> <td><a href="mailto:#email#">#firstname# #lastname#</a></td> <td> <form method="post" action="RaceEdit.cfm" style="margin-bottom:0px"> <input type="hidden" name="raceid" value="#raceid#"> <input type="submit" value="EDIT" style="fontsize:xx-small"> <input type="submit" name="deleting" value="DELETE" style="font-size:xx-small"> </form> </td> </tr> </cfoutput> </table> <cfinclude template="Includes/Footer.cfm"> </body> </html> Controlling Access to Individual Records We will add code that prevents users from editing other people's records. We will also add code to allow any user who is logged in to add a race. Code Sample: ManagingData/Demos/Races2.cfm <cfif isDefined("SESSION.userid")> <cfset userid = SESSION.userid> <cfelse> <cfset userid =0> </cfif> <cfquery name="getraces" datasource="#APPLICATION.datasource#"> SELECT raceid, racename, racetime, distance, city, state, active, u.userid, firstname, lastname, email

FROM Users u, Races r WHERE u.userid = r.userid AND active=1 </cfquery> <html> <head> <title>Running Races</title> </head> <body> <cfif userid NEQ 0> <a href="RaceAdd.cfm">Add a race</a> </cfif> <table border="1" cellpadding="3" cellspacing="0" id="maintable"> <tr> <th>Race</th> <th>Location</th> <th>Distance</th> <th>Date & Time</th> <th>Added by</th> <th>Edit/Delete</th> </tr> <cfoutput query="getraces"> <tr> <td>#racename#</td> <td>#city#, #state#</td> <td>#distance#</td> <td>#DateFormat(racetime, "ddd, mmm d, yyyy")# at #TimeFormat(racetime,"h:mm tt")#</td> <td><a href="mailto:#email#">#firstname# #lastname#</a></td> <cfif VARIABLES.userid EQ getraces.userid OR (isDefined("SESSION.role") AND SESSION.role EQ "admin")> <td> <form method="post" action="RaceEdit.cfm" style="margin-bottom:0px"> <input type="hidden" name="raceid" value="#raceid#"> <input type="submit" value="EDIT" style="fontsize:xx-small">

<input type="submit" name="deleting" value="DELETE" style="font-size:xx-small"> </form> </td> <cfelse> <td>PROTECTED</td> </cfif> </tr> </cfoutput> </table> <cfinclude template="Includes/Footer.cfm"> </body> </html> Code Explanation "User" users now get a "PROTECTED" cell for records that they don't own. Admins are able to edit all records. Sorting Data We will now make the table headings into links that, when clicked on, will change the order in which the records appear. Code Sample: ManagingData/Demos/Races3.cfm ---- Code Omitted ---<table border="1" cellpadding="3" cellspacing="0" id="maintable"> <cfoutput> <tr> <th> <a href="#CGI.SCRIPT_NAME#?orderfield=racename">Race</a> </th> <th> <a href="#CGI.SCRIPT_NAME#?orderfield=city">Location</a> </th> <th> <a href="#CGI.SCRIPT_NAME#?orderfield=distance">Distance< /a>

</th> <th> <a href="#CGI.SCRIPT_NAME#?orderfield=racetime">Date & Time</a> </th> <th> <a href="#CGI.SCRIPT_NAME#?orderfield=firstname">Added by</a> </th> <th>Edit/Delete</th> </tr> </cfoutput> ---- Code Omitted ---That completes the page for displaying data. In the next sections, we will learn how to add, edit and delete races. Adding Data using Stored Procedures A stored procedure is a database object that holds a callable query, allowing parameters to be passed in. Benefits of Stored Procedures The major benefits of stored procedures.






Performance. Because their execution plan does not have to be determined at runtime, stored procedures typically run faster than straight queries. Security. Stored procedures provide a means for accomplishing a specific task and do not provide the same kind of direct access to tables as straight queries do. Simplicity and Reusability. Complex queries can be saved as stored procedures, so developers do not have to write the same difficult query over and over.

Calling Stored Procedures with ColdFusion In ColdFusion, stored procedures can be called directly within a <cfquery> tag using the database-specific syntax for executing them. They can also be called using the <cfstoredproc> tag. <cfstoredproc> and <cfprocparam> The <cfstoredproc> tag works much like the <cfquery> tag, except that, rather than a straight query, it contains (or can contain) one or more <cfprocparam> tags for passing parameters to the stored procedure. <cfstoredproc> Attributes Attribute Description

procedure Required. The name of the stored procedure. dataSource Required. The name of the data source. username Overrides username set up in ColdFusion Administrator. password Overrides password set up in ColdFusion Administrator. blockfactor Maximum rows to get at a time from server. debug returnCode Turns debugging display on or off. Sets cfstoredproc.statusCode to the status code returned by the stored procedure. The name of the structure to hold the variables returned by the stored procedure. By default, the structure is stored in cfstoredproc.

result

Using <cfprocparam>, parameters are passed into a stored procedure in the same order as they are set in the stored procedure itself. <cfprocparam> Attributes Attribute Description

<cfprocparam> Attributes Attribute type Description Possible values are in, out, or inout depending on whether a value is being sent to and/or received from the stored procedure. Required if type is in or inout. The variable name that will hold the data returned by the stored procedure. Required if type is in and optional if type is inout. Value passed to stored procedure. Required. The SQL data type (e.g, cf_sql_integer, cf_sql_varchar, etc.) The maximum number of characters of an in or inout value. "0" means no limit. The number of digits after the decimal of a numeric parameter. "0" means no limit. The in or inout parameter passes a null value. The value attribute is ignored.

variable

value

CFSQLType

maxLength

scale

null

The following three examples show different methods for inserting the race: 1. Using <cfquery> with a standard INSERT statement. 2. Using <cfquery> with a call to a stored procedure. 3. Using <cfstoredproc>. Code Sample: ManagingData/Demos/RaceAdd-query.cfm <cfif NOT isDefined("SESSION.userid")> <cflocation url="index.cfm" addtoken="no"> </cfif> <cfparam <cfparam <cfparam <cfparam name="FORM.racehour" default=""> name="FORM.raceminute" default=""> name="FORM.raceday" default=""> name="FORM.racemonth" default="">

<cfparam name="FORM.raceyear" default=""> <cfparam name="FORM.raceampm" default="am"> <cfparam name="FORM.racename" default=""> <cfparam name="FORM.distance" default=""> <cfparam name="FORM.city" default=""> <cfparam name="FORM.state" default=""> <cfparam name="FORM.description" default=""> <html> <head> <title>Add a Race</title> </head> <body> <cfif isDefined("FORM.submitted")> <cfif FORM.raceampm EQ "pm"> <cfset racehour = FORM.racehour+12> <cfelse> <cfset racehour = FORM.racehour> </cfif> <cfset racetime = CreateDateTime(FORM.raceyear, FORM.racemonth, FORM.raceday, racehour, FORM.raceminute, 0)> <cfset racetime = CreateODBCDateTime(racetime)> <!---Use <cfquery> with a standard insert statement--> <cfquery datasource="#APPLICATION.datasource#"> INSERT INTO Races (racename, racetime, distance, city, state, active, userid, description) VALUES('#FORM.racename#',#racetime#,'#FORM.distance#', '#FORM.city#', '#FORM.state#',1,#SESSION.userid#,'#FORM.description#' ) </cfquery> <p class="success">Race Added.<br> <a href="Races.cfm">Return to Race List</a>.</p>

<h1>Add Another Race</h1> <!---Clean up variables---> <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset <cfset FORM.racehour = ""> FORM.raceminute = ""> FORM.raceday = ""> FORM.racemonth = ""> FORM.raceyear = ""> FORM.raceampm = ""> FORM.racename = ""> FORM.distance = ""> FORM.city = ""> FORM.state = ""> FORM.description = "">

<cfelse> <h1>Add Race</h1> </cfif> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> <input type="hidden" name="submitted" value="true"> <table> <tr> <td>Race Name:</td> <td><input name="racename" value="#FORM.racename#" type="text" size="30" maxlength="50"></td> </tr> <tr> <td>City:</td> <td><input name="city" value="#FORM.city#" type="text" size="30" maxlength="50"></td> </tr> <tr> <td>State:</td> <td><input name="state" value="#FORM.state#" type="text" size="3" maxlength="2"></td> </tr> <tr valign="top"> <td>Distance (in miles):</td> <td><input name="distance" value="#FORM.distance#" type="text" size="10" maxlength="20"></td> </tr>

<tr valign="top"> <td>Time:</td> <td> <select name="racehour"> <cfloop index="hour" from="1" to="12" step="1"> <option value="#hour#"<cfif hour EQ FORM.racehour> selected</cfif>>#hour#</option> </cfloop> </select> : <select name="raceminute"> <cfloop index="minute" from="0" to="59" step="15"> <option value="#minute#"<cfif minute EQ FORM.raceminute> selected</cfif>>#NumberFormat(minute,"00")#</option> </cfloop> </select> <input name="raceampm" type="radio" value="am"<cfif FORM.raceampm EQ "am"> checked</cfif>> AM <input name="raceampm" type="radio" value="pm"<cfif FORM.raceampm EQ "pm"> checked</cfif>> PM</td> </tr> <tr valign="top"> <td>Date:</td> <td> <select name="racemonth"> <cfloop index="month" from="1" to="12"> <option value="#month#"<cfif month EQ FORM.racemonth> selected</cfif>>#MonthAsString(month)#</option> </cfloop> </select> <select name="raceday"> <cfloop index="day" from="1" to="31"> <option value="#day#"<cfif day EQ FORM.raceday> selected</cfif>>#day#</option> </cfloop> </select> <select name="raceyear"> <cfloop index="year" from="#Year(Now())#" to="#Year(Now())+3#"> <option value="#year#"<cfif year EQ FORM.raceyear> selected</cfif>>#year#</option>

</cfloop> </select> </td> </tr> <tr valign="top"> <td>Description:</td> <td> <textarea name="description" rows="5" cols="40" wrap="soft">#FORM.description#</textarea> </td> </tr> <tr> <td colspan="2" align="right"><input type="submit" value="Add Race"></td> </tr> </table> </form> </cfoutput> <cfinclude template="Includes/Footer.cfm"> </body> </html> Code Sample: ManagingData/Demos/RaceAdd-sp.cfm ---- Code Omitted ---<!---Use <cfquery> with a call to the spInsertRace stored procedure---> <cfquery datasource="#APPLICATION.datasource#"> exec spInsertRace '#FORM.racename#','#FORM.description#','#FORM.distance #',#racetime#,'#FORM.city#', '#FORM.state#',1,#SESSION.userid# </cfquery> ---- Code Omitted ---Code Sample: ManagingData/Demos/RaceAdd-sp2.cfm ---- Code Omitted ---<!---Use <cfstoredproc> with nested <cfparam>s---> <cfstoredproc procedure="spInsertRace" datasource="#APPLICATION.datasource#">

<cfprocparam type="In" cfsqltype="cf_sql_varchar" value="#FORM.racename#"> <cfprocparam type="In" cfsqltype="cf_sql_varchar" value="#FORM.description#"> <cfprocparam type="In" cfsqltype="cf_sql_varchar" value="#FORM.distance#"> <cfprocparam type="In" cfsqltype="cf_sql_date" value="#racetime#"> <cfprocparam type="In" cfsqltype="cf_sql_varchar" value="#FORM.city#"> <cfprocparam type="In" cfsqltype="cf_sql_varchar" value="#FORM.state#"> <cfprocparam type="In" cfsqltype="cf_sql_bit" value="1"> <cfprocparam type="In" cfsqltype="cf_sql_tinyint" value="1"> </cfstoredproc> ---- Code Omitted ---<cfprocresult> Stored procedures can also return one or more recordsets. The <cfprocresult> tag is used to store these recordsets as ColdFusion queries. It works much like the name attribute of the <cfquery> tag. <cfprocresult> Attributes Attribute name resultSet Description Required. The name of the recordset. A number indicating which recordset to store (only used if multiple recordsets are returned by the stored procedure).

maxRows The maximum number of records to retrieve for that recordset. Editing and Deleting Data The page for editing and deleting data is shown below.

Code Sample: ManagingData/Demos/RaceEdit.cfm <cfif NOT isDefined("SESSION.userid") OR NOT isDefined("FORM.raceid")> <cflocation url="index.cfm" addtoken="no"> </cfif> <html> <head> <title>Edit Race</title> </head> <body> <cfif isDefined("FORM.deleting")> <cfquery datasource="#APPLICATION.datasource#"> DELETE FROM Races WHERE raceid=<cfqueryparam value="#FORM.raceid#" cfsqltype="cf_sql_integer"> </cfquery> <cflocation url="Races3.cfm" addtoken="no"> <cfelseif isDefined("FORM.editing")> <cfif FORM.raceampm EQ "pm"> <cfset racehour = FORM.racehour+12> <cfelse> <cfset racehour = FORM.racehour> </cfif> <cfset racetime = CreateDateTime(FORM.raceyear, FORM.racemonth, FORM.raceday, FORM.racehour, FORM.raceminute, 0)> <cfset racetime = CreateODBCDateTime(racetime)> <cfquery datasource="#APPLICATION.datasource#"> UPDATE Races SET racename = '#FORM.racename#', racetime = #racetime#, distance = '#FORM.distance#', city = '#FORM.city#', state = '#FORM.state#', description = '#FORM.description#', active = #FORM.active# WHERE raceid = <cfqueryparam value="#FORM.raceid#" cfsqltype="cf_sql_integer"> </cfquery>

<p class="success">Race Updated.<br> <a href="Races3.cfm">Return to Race List</a>.</p> <h1>Edit Race</h1> <cfelse> <h1>Edit Race</h1> </cfif> <cfquery name="getraceinfo" datasource="#APPLICATION.datasource#"> SELECT racename, racetime, distance, city, state, description, active FROM Races WHERE raceid=<cfqueryparam value="#FORM.raceid#" cfsqltype="cf_sql_integer"> </cfquery> <cfoutput query="getraceinfo"> <form method="post" action="#CGI.SCRIPT_NAME#"> <input type="hidden" name="editing" value="true"> <input type="hidden" name="raceid" value="#FORM.raceid#"> <table> <tr> <td>Race Name:</td> <td><input name="racename" value="#racename#" type="text" size="30" maxlength="50"></td> </tr> <tr> <td>City:</td> <td><input name="city" value="#city#" type="text" size="30" maxlength="50"></td> </tr> <tr> <td>State:</td> <td><input name="state" value="#state#" type="text" size="3" maxlength="2"></td> </tr> <tr valign="top"> <td>Distance (in miles):</td>

<td><input name="distance" value="#distance#" type="text" size="10" maxlength="20"></td> </tr> <tr valign="top"> <td>Time:</td> <td> <select name="racehour"> <cfloop index="hour" from="1" to="12"> <option value="#hour#"<cfif hour EQ TimeFormat(racetime,"h")> selected</cfif>>#hour#</option> </cfloop> </select> : <select name="raceminute"> <cfloop index="minute" from="0" to="59" step="15"> <option value="#minute#"<cfif minute EQ TimeFormat(racetime,"mm")> selected</cfif>>#NumberFormat(minute,"00")#</option> </cfloop> </select> <input name="raceampm" type="radio" value="am"<cfif TimeFormat(racetime,"tt") EQ "am"> checked</cfif>> AM <input name="raceampm" type="radio" value="pm"<cfif TimeFormat(racetime,"tt") EQ "pm"> checked</cfif>> PM</td> </tr> <tr valign="top"> <td>Date:</td> <td> <select name="racemonth"> <cfloop index="month" from="1" to="12"> <option value="#month#"<cfif month EQ DateFormat(racetime,"m")> selected</cfif>>#MonthAsString(month)#</option> </cfloop> </select> <select name="raceday"> <cfloop index="day" from="1" to="31"> <option value="#day#"<cfif day EQ DateFormat(racetime,"d")> selected</cfif>>#day#</option>

</cfloop> </select> <select name="raceyear"> <cfloop index="year" from="#Year(Now())#" to="#Year(Now())+3#"> <option value="#year#"<cfif year EQ DateFormat(racetime,"yyyy")> selected</cfif>>#year#</option> </cfloop> </select> </td> </tr> <tr valign="top"> <td>Active:</td> <td> <input name="active" type="radio" value="1"<cfif active EQ 1> checked</cfif>> Yes <input name="active" type="radio" value="0"<cfif active EQ 0> checked</cfif>> No</td> </tr> <tr valign="top"> <td>Description:</td> <td> <textarea name="description" rows="5" cols="40" wrap="soft">#description#</textarea> </td> </tr> <tr> <td colspan="2" align="right"><input type="submit" value="Edit Race"></td> </tr> </table> </form> </cfoutput> </body> </html> Code Explanation This is the processing page that is executed when the Edit or Delete button is pressed in the Races table.

The code at the top of the page prevents access to this page for people who are not logged in. It also prevents direct access to this page by checking to make sure that raceid exists in the FORM scope. <cfif NOT isDefined("SESSION.userid") OR NOT isDefined("FORM.raceid")> <cflocation url="index.cfm" addtoken="no"> </cfif> The code then checks to see if the Delete button was clicked. If it was, then it deletes that record from the table and returns to the Races page: <cfif isDefined("FORM.deleting")> <cfquery datasource="#APPLICATION.datasource#"> DELETE FROM Races WHERE raceid=<cfqueryparam value="#FORM.raceid#" cfsqltype="cf_sql_integer"> </cfquery> <cflocation url="Races3.cfm" addtoken="no"> The code then checks if the Edit button on this page was clicked. <cfelseif isDefined("FORM.editing")> <cfif FORM.raceampm EQ "pm"> <cfset racehour = FORM.racehour+12> <cfelse> <cfset racehour = FORM.racehour> </cfif> <cfset racetime = CreateDateTime(FORM.raceyear, FORM.racemonth, FORM.raceday, FORM.racehour, FORM.raceminute, 0)> <cfset racetime = CreateODBCDateTime(racetime)> <cfquery datasource="#APPLICATION.datasource#"> UPDATE Races SET racename = '#FORM.racename#', racetime = #racetime#, distance = '#FORM.distance#', city = '#FORM.city#', state = '#FORM.state#', description = '#FORM.description#',

active = #FORM.active# WHERE raceid = <cfqueryparam value="#FORM.raceid#" cfsqltype="cf_sql_integer"> </cfquery> <p class="success">Race Updated.<br> <a href="Races3.cfm">Return to Race List</a>.</p> <h1>Edit Race</h1> Note that the Edit button on the Races page is not named, so it will not trigger the code above to run. It is triggered by the hidden field in the form on this same page (shown below). <form method="post" action="#CGI.SCRIPT_NAME#"> <input type="hidden" name="editing" value="true"> <input type="hidden" name="raceid" value="#FORM.raceid#"> This example uses the <cfqueryparam> tag when deleting and editing to verify the data type of FORM.raceid. Using <cfqueryparam> in this way is highly recommended as it helps to protect your database from malicious attacks. Exercise: Managing the Resources Table Duration: 30 to 45 minutes. This exercise involves making changes to three pages: ManagingData/Exercises/Resources.cfm, ManagingData/Exercises/ResourceAdd.cfm, and ManagingData/Exercises/ResourceEdit.cfm. The files have comments showing where to edit or add code. 1. Open ManagingData/Exercises/Resources.cfm in your editor. 2. If the user is not logged in, set userid to 0. Otherwise set userid to the user's userid as contained in the Session. 3. Make the table headers into links that, when clicked, sort the table on that field. 4. Open ManagingData/Exercises/ResourceAdd.cfm in your editor. 5. Write a <cfquery> to insert the resource entry into the database. The database fields are resourcename, link, active, userid, and description. 6. Open ManagingData/Exercises/ResourceEdit.cfm in your editor.

7. Write a condition to check if the resource should be deleted or updated and write to queries to handle the appropriate operation. Code Sample: ManagingData/Exercises/Resources.cfm <!--If the user is not logged in, set userid to 0. Otherwise set userid to the user's userid as contained in the SESSION. ---> <cfparam name="URL.orderfield" default="resourceid"> <cfset orderfields = "resourcename, description, firstname"> <cfif NOT ListContains(orderfields,URL.orderfield)> <cfset URL.orderfield = "resourceid"> </cfif> <cfquery name="getresources" datasource="#APPLICATION.datasource#"> SELECT resourceid, resourcename, link, active, description, u.userid, firstname, lastname, email FROM Resources r, Users u WHERE u.userid = r.userid ORDER BY #URL.orderfield# </cfquery> <html> <head> <title>Running Resources</title> </head> <body> <h1>Running Resources</h1> <cfif userid NEQ 0> <a href="ResourceAdd.cfm">Add a resource</a> </cfif> <table border="1" cellpadding="3" cellspacing="0" id="maintable"> <!--Make the table headers into links that, when clicked, sort the table on that field ---> <tr>

<th>Resource</th> <th>Description</th> <th>Added by</th> <th>Edit/Delete</th> </tr> <cfoutput query="getresources"> <tr valign="top"> <td> <a href="#link#">#resourcename#</a> </td> <td>#description#</td> <td> <a href="mailto:#email#">#firstname# #lastname#</a> </td> <cfif Variables.userid EQ getresources.userid OR (isDefined("SESSION.role") AND SESSION.role EQ "admin")> <td> <form method="post" action="ResourceEdit.cfm" style="margin-bottom:0px"> <input type="hidden" name="resourceid" value="#resourceid#"> <input type="submit" value="EDIT" style="fontsize:xx-small"> <input type="submit" name="deleting" value="DELETE" style="font-size:xx-small"> </form> </td> <cfelse> <td>PROTECTED</td> </cfif> </tr> </cfoutput> </table> <cfinclude template="Includes/Footer.cfm"> </body> </html> Code Sample: ManagingData/Exercises/ResourceAdd.cfm <cfif NOT isDefined("SESSION.userid")> <cflocation url="index.cfm" addtoken="no"> </cfif>

<cfparam name="FORM.resourcename" default=""> <cfparam name="FORM.link" default=""> <cfparam name="FORM.description" default=""> <html> <head> <title>Add a Resource</title> </head> <body> <cfif isDefined("FORM.submitted")> <!--Write a <cfquery> to insert the resource entry into the database. The database fields are resourcename, link, active, userid, and description. ---> <p class="success">Resource Added.<br> <a href="resources.cfm">Return to Resource List</a>.</p> <h1>Add Another Resource</h1> <cfelse> <h1>Add Resource</h1> </cfif> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> </cfoutput> <input type="hidden" name="submitted" value="true"> <cfoutput><input type="hidden" name="userid" value="#SESSION.userID#"></cfoutput> <table> <tr> <td>Resource Name:</td> <td><input name="resourcename" type="text" size="30"maxlength="50"></td> </tr> <tr> <td>URL:</td> <td><input name="link" type="text" size="30" maxlength="50"></td>

</tr> <tr valign="top"> <td>Description:</td> <td> <textarea name="description" rows="5" cols="40" wrap="soft"></textarea> </td> </tr> <tr> <td colspan="2" align="right"><input type="submit" value="Add Resource"></td> </tr> </table> </form> <cfinclude template="Includes/Footer.cfm"> </body> </html> Code Sample: ManagingData/Exercises/ResourceEdit.cfm <cfif NOT isDefined("SESSION.userid") OR NOT isDefined("FORM.resourceid")> <cflocation url="index.cfm" addtoken="no"> </cfif> <html> <head> <title>Edit Resource</title> </head> <body> <cfif WRITE CONDITION TO CHECK IF RESOURCE SHOULD BE DELETED> <!--Write a <cfquery> to update the resource entry in the database. The database fields are resourcename, link, active, description, and resourceid. ---> <cflocation url="Resources.cfm" addtoken="no"> <cfelseif WRITE CONDITION TO CHECK IF RESOURCE SHOULD BE EDITED> <!---

Write a <cfquery> to update the resource entry in the database. The database fields are resourcename, link, active, description, and resourceid. ---> <p class="success">Resource Updated.<br> <a href="resources.cfm">Return to Resource List</a>.</p> <cfelse> <h1>Update Resource</h1> </cfif> <cfquery name="getresourceinfo" datasource="#APPLICATION.datasource#"> SELECT userid, resourcename, description, link, active FROM Resources WHERE resourceid = <cfqueryparam value="#FORM.resourceid#" cfsqltype="cf_sql_integer"> </cfquery> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> <input type="hidden" name="editing" value="true"> <input type="hidden" name="resourceid" value="#FORM.resourceid#"> <table> <tr> <td>Resource Name:</td> <td><input name="resourcename" value="#getresourceinfo.resourcename#" type="text" size="30" maxlength="50"></td> </tr> <tr> <td>URL:</td> <td><input name="link" value="#getresourceinfo.link#" type="text" size="30" maxlength="50"></td> </tr> <tr> <td>Active:</td> <td>

<input name="active" value="1" type="radio"<cfif getresourceinfo.active EQ 1> checked</cfif>>Yes <input name="active" value="0" type="radio"<cfif getresourceinfo.active EQ 0> checked</cfif>>No </td> </tr> <tr valign="top"> <td>Description:</td> <td> <textarea name="description" rows="5" cols="40" wrap="soft">#getResourceInfo.description#</textarea> </td> </tr> <tr> <td colspan="2" align="right"><input type="submit" value="Update Resource"></td> </tr> </table> </form> </cfoutput> <cfinclude template="Includes/Footer.cfm"> </body> </html> Where is the solution? Managing Data Conclusion In this lesson of the ColdFusion tutorial, you have learned to create data management pages and to control data access based on user roles.

Custom Tags In this lesson of the ColdFusion tutorial, you will learn... 1. To create custom tags. 2. To call custom tags.

In ColdFusion, you can create your own custom tags, which you can reuse throughout an application. One of the huge benefits of this is that more seasoned developers can hide complex code in custom tags, allowing less advanced developers to use those tags without needing to understand or rewrite the code. Custom tag templates can contain any code that any other ColdFusion page can contain. Custom tags are similar to <cfinclude>s in that the code for a custom tag goes on a separate ColdFusion page, which is then called by another page. Calling Custom Tags To call a custom tag, use the following syntax: Syntax <cf_tagname> The cf_ is simply a prefix that tells the ColdFusion Application Server that this is a custom tag. The string that follows the cf_ is the name of the custom tag file minus the file extension. So, in the example above, the file would be called tagname.cfm. Any ColdFusion file can be called as a custom tag. To illustrate, let's assume that we have a page called Foo.cfm, which has nothing more than the word "foo" written on it. Foo.cfm will be our custom tag template. You call it with the <cf_foo> tag, which will simply output the word "foo". The code for the two files is shown below. Code Sample: CustomTags/Demos/Foo.cfm foo Code Sample: CustomTags/Demos/FooCaller.cfm <html> <head> <title><cf_foo></title> </head> <body> <cf_foo> </body> </html>

thisTag Structure The thisTag structure holds data related to the custom tag instance. The table below shows the variables available in the thisTag structure. Variable ExecutionMode HasEndTag GeneratedContent Description The mode of execution: "start", "end", or "inactive" true if the tag is called with an end tag; otherwise, false The content in the body of the tag (i.e, between the open and close custom tag) Contains the attributes of nested custom tags that have been made available through the <cfassociate> tag.

AssocAttribs ExecutionMode

Custom tags can be called with end tags, like so: <cf_foo></cf_foo> or <cf_foo/>. This instructs ColdFusion to call the custom tag twice. Open CustomTags/Demos/FooCallerClosed.cfm in your browser and you will see that the word "foo" is output twice. However, you may not want the code in the tag to be executed twice or you may want the start tag to behave differently from the end tag. This can be handled by checking the ExecutionMode of the ThisTag structure. Take a look at our modified custom tag template. Code Sample: CustomTags/Demos/Foo2.cfm <cfif ThisTag.executionMode EQ "start"> foo <cfelse> bar </cfif> Code Sample: CustomTags/Demos/Foo2Caller.cfm <html> <head> <title><cf_foo2></title> </head> <body>

<cf_foo2/> </body> </html> Code Explanation As you can see, the word "foo" is output when the tag is opened and the word "bar" is output when it is closed. Now that's useful! But let's take a look at an even more interesting example. The following page creates a custom tag which displays an image from a specified directory. Each time the custom tag is called, it returns a different image from the directory. Code Sample: CustomTags/Demos/ImageFlipper.cfm <cfif thisTag.executionMode EQ "start"> <cfdirectory action="list" name="getImages" directory="#ExpandPath(ATTRIBUTES.directory)#"> <cfparam name="SESSION.picture" default="0"> <cfset SESSION.picture = SESSION.picture + 1> <cfif SESSION.picture GT getImages.RecordCount> <cfset SESSION.picture = 1> </cfif> <cfoutput> <img src="#ATTRIBUTES.directory#/#getImages.name[SESSION.pi cture]#" <cfif isDefined("ATTRIBUTES.height")> height="#ATTRIBUTES.height#" </cfif> <cfif isDefined("ATTRIBUTES.width")> width="#ATTRIBUTES.width#" </cfif> <cfif isDefined("ATTRIBUTES.alt")> alt="#ATTRIBUTES.alt#" <cfelse> alt="#getImages.name[SESSION.picture]#" </cfif>> </cfoutput> </cfif>

Code Sample: CustomTags/Demos/Runners.cfm <html> <head> <title>Flipping Image</title> </head> <body> <cf_ImageFlipper directory="Images/Runners" height="205" width="150">Runners</cf_ImageFlipper> </body> </html> Code Explanation In the code above, you'll notice the ATTRIBUTES scope, which contains any variables passed in as attributes as shown below... <cf_ImageFlipper directory="Images/Runners" height="205" width="150"> We check for the existence of these attributes using the isDefined() function. GeneratedContent The GeneratedContent variable, which is only accessible in the "end" mode, holds all content between the open tag and close tag (e.g., between <cf_foo> and </cf_foo>). Its value can be modified in the custom tag. The following files show how a custom tag can be used to comment out the body based on the value of an attribute. Code Sample: CustomTags/Demos/ImageFlipper2.cfm <cfif thisTag.executionMode EQ "start"> <cfdirectory action="list" name="getImages" directory="#ExpandPath(ATTRIBUTES.directory)#"> <cfparam name="SESSION.picture" default="0"> <cfset SESSION.picture = SESSION.picture + 1> <cfif SESSION.picture GT getImages.RecordCount> <cfset SESSION.picture = 1> </cfif> <cfoutput>

<img src="#ATTRIBUTES.directory#/#getImages.name[SESSION.pi cture]#" <cfif isDefined("ATTRIBUTES.height")> height="#ATTRIBUTES.height#" </cfif> <cfif isDefined("ATTRIBUTES.width")> width="#ATTRIBUTES.width#" </cfif> <cfif isDefined("ATTRIBUTES.alt")> alt="#ATTRIBUTES.alt#" <cfelse> alt="#getImages.name[SESSION.picture]#" </cfif>> </cfoutput> <cfelseif thisTag.executionMode EQ "end"> <cfif isDefined("ATTRIBUTES.CommentBody") AND ATTRIBUTES.CommentBody EQ "true"> <cfset thisTag.GeneratedContent = "<!-#thisTag.GeneratedContent#-->"> </cfif> </cfif> Code Sample: CustomTags/Demos/Runners2.cfm <html> <head> <title>Flipping Image</title> </head> <body> <cf_ImageFlipper2 directory="Images/Runners" height="205" width="150" CommentBody="true"> <br/>Runners </cf_ImageFlipper2> </body> </html> Exercise: Using Custom Tags Duration: 20 to 30 minutes. In this exercise, you will create a custom tag that can turn tab-delimited text into an HTML table.

1. Open CustomTags/Exercises/ProfitLoss.txt in your editor. This is a tabdelimited text file showing a simple profit-loss statement. 2. Open CustomTags/Exercises/ProfitLoss.cfm in your editor. This file is created for you. It calls the custom tag cf_Text2Table, which you will be working on. Notice that it has both an open and a close tag and that the body of the tag will contain the text from ProfitLoss.txt. 3. Open CustomTags/Exercises/Text2Table.cfm in your editor. You will write code to turn the body text of the tag (i.e, the text between the open tag and close tag) into an HTML table. Note that you will need to modify the contents of the GeneratedContent variable. 4. To test your solution, open CustomTags/Exercises/ProfitLoss.cfm in your browser. It should look something like this: The Text2Table tag requires an end tag. Add code so that the tag exits (use <cfexit>) with an error message if the call does not include an end tag. The resulting page is shown below: Where is the solution? Storing Custom Tags Custom tags can be stored in any of the following locations. ColdFusion will search for them in the order listed. 1. In the same directory as the calling page - while this is easy for demonstration purposes, it's not very practical, as it means that the custom tag is only available within that directory. 2. In a directory (or subdirectory of a directory) specified in ColdFusion Administrator under Extensions -> Custom Tag Paths. 3. In the cfusion/CustomTags directory or one of its subdirectories. Other Ways of Calling Custom Tags One problem with calling custom tags using the cf_ syntax we have seen thus far is that there no way to deal with name conflicts. ColdFusion simply uses the first file it finds that matches the tag name specified. For example, if you had a file in the current directory called "FetchRecord.cfm" and a custom tag file in the cfusion/CustomTags directory with the same name. You cannot access the custom tag file in the cfusion/CustomTags directory using the cf_FetchRecord syntax. ColdFusion provides two ways to deal with the naming conflict.

CFMODULE The <cfmodule> tag allows you to point directly to the custom tag you want to call. It takes the following syntax: Syntax <cfmodule template="path_to_tag_file" attribute_name="custom_tag_attribute_value"> The custom tag's attributes are passed to the custom tag in the same way as they are using the cf_ syntax. The example below illustrates the use of <cfmodule> to call a custom tag. Code Sample: CustomTags/Demos/Runners-cfmodule.cfm <html> <head> <title>Flipping Image</title> </head> <body> <cfmodule template="ImageFlipper.cfm" directory="Images/Runners" height="205" width="150"> </body> </html> The example above shows how the attributes can be passed in to the custom tags in separate name-value pairs. It is also possible to pass in the attributes as a single structure using <cfmodule>'s attributecollection attribute: Code Sample: CustomTags/Demos/Runners-cfmodule2.cfm <html> <head> <title>Flipping Image</title> </head> <body> <cfset Runner.directory = "Images/Runners"> <cfset Runner.height = "205"> <cfset Runner.width = "150"> <cfmodule template="ImageFlipper.cfm" attributecollection="#Runner#">

</body> </html> Custom tags with close tags can also be called with <cfmodule> as shown in the example below. Code Sample: CustomTags/Demos/ProfitLoss-cfmodule.cfm <html> <head> <title>Profit Loss</title> </head> <body> <h1>Profit Loss</h1> <cfset FilePath = ExpandPath("ProfitLoss.txt")> <cffile action="read" file="#FilePath#" variable="myfile"> <cfmodule template="Text2Table.cfm"><cfoutput>#myfile#</cfoutput ></cfmodule> </body> </html> Finally, with <cfmodule>, custom tags can be specified using dot notation. In this case, the name attribute replaces the template attribute. This is shown in the sample below: Code Sample: CustomTags/Demos/ProfitLoss-cfmodule-name.cfm <html> <head> <title>Profit Loss</title> </head> <body> <h1>Profit Loss</h1> <cfset FilePath = ExpandPath("ProfitLoss.txt")> <cffile action="read" file="#FilePath#" variable="myfile">

<cfmodule name="Strings.Text2Table"><cfoutput>#myfile#</cfoutput ></cfmodule> <!---This calls the Text2Table.cfm custom tag file in the cfusion/CustomTags/Strings directory.---> </body> </html> CFIMPORT The <cfimport> tag is used to import a group of custom tags that can be referenced with a given prefix. The syntax is shown below: Syntax <cfimport prefix="prefix" taglib="path_to_tag_directory"> The prefix attribute is used to specifiy a tag qualifier. A custom tag called "bar" in a library qualified with the "foo" prefix would be called as follows: <foo:bar> The taglib attribute points to the directory holding the custom tags. The directory path can be relative to: 1. the current page location 2. the web root (the page should start with a "/") 3. a directory specified in the Administrator ColdFusion mappings page (the page should start with a "/") Exercise: Using CFIMPORT with Custom Tags Duration: 10 to 20 minutes. In this exercise you will import and use a custom tag library. 1. Create a new directory at the root of your C drive and name it "CustomTags". 2. In the C:/CustomTags directory, create another directory called "Strings". 3. Open CustomTags/Solutions/Text2Table.cfm and save it in the C:/CustomTags/Strings directory you just created.

4. Open ColdFusion Administrator in your browser and add a mapping named "CustomTags" to "C:\CustomTags". 5. Open CustomTags/Exercises/ProfitLoss.cfm and save it as CustomTags/Exercises/ProfitLoss2.cfm. 6. Add the following line of code at the very top of the page: <cfimport prefix="String" taglib="/CustomTags/Strings"> 7. Modify the rest of the page so that it uses the custom tag from the Strings library. 8. To test your solution, open CustomTags/Exercises/ProfitLoss2.cfm in your browser. Where is the solution? Custom Tags Conclusion In this lesson of the ColdFusion tutorial, you have learned how to create and use custom tags, which provide a great mechanism for code reuse. They are also easy for less experienced ColdFusion developers to make use of as they work just like standard ColdFusion tags.

ColdFusion Components In this lesson of the ColdFusion tutorial, you will learn... 1. To create and use ColdFusion Components.

CFC Basics ColdFusion Components (CFCs) are very similar to classes in objectoriented languages. They are used to create structured applications and to "blackbox" complicated processing code.

Like all ColdFusion pages, CFCs are simply text files written in CFML; however, they use the extension ".cfc" rather than ".cfm". They can be stored anywhere in the application's directory structure. Creating CFCs All code within a CFC should be wrapped in a <cfcomponent> tag, which can contain one or more <cffunction> tags. The <cffunction>s are the methods of the component. The only major difference between functions written in CFCs and standard ColdFusion functions is that a CFC function has an access level, which is assigned in the access attribute of the <cffunction> tag. Access Level private package public remote Description only accessible from other methods in the same component only accessible from components in the same directory accessible from any ColdFusion page in the application accessible over the internet and by the Flash Player.

The code below shows an example of a CFC that has similar functionality to the imageflipper custom tag we saw earlier. Code Sample: CFCs/Demos/Images.cfc <cfcomponent> <cffunction name="ImageFlipper" access="public" returntype="string"> <cfargument name="directory" type="string" required="true"> <cfargument name="extensions" type="string" default="jpg,gif"> <cfset aryImages = getFileList(arguments.directory,arguments.extensions)> <cfparam name="SESSION.picture" default="0"> <cfset SESSION.picture = SESSION.picture + 1> <cfif SESSION.picture GT ArrayLen(aryImages)> <cfset SESSION.picture = 1>

</cfif> <cfset imagepath="#arguments.directory#/#aryImages[SESSION.pi cture]#"> <cfreturn imagepath> </cffunction> ---- Code Omitted ---</cfcomponent> Storing CFCs ColdFusion Components can be stored in any of the following locations. ColdFusion will search for them in the order listed. (see footnote) 1. In the same directory as the calling page - while this is easy for demonstration purposes, it's not very practical, as it means that the CFC is only available within that directory. 2. In a directory (or subdirectory of a directory) mapped in ColdFusion Administrator under Server Settings -> Mappings. 3. In a directory (or subdirectory of a directory) specified in ColdFusion Administrator under Extensions -> Custom Tag Paths. 4. In the web root. Invoking CFCs CFC methods are invoked using the <cfinvoke> tag. The tag does one of the following: 1. instantiates the specified component transiently and then invokes the specified method. 2. invokes the specified method on a component that has already been instantiated (e.g, with the <cfobject> tag). Passing Argument Values CFC method arguments can be passed in any of the following ways: 1. Using a nested <cfinvokeargument> tag with name and value attributes: 2. <cfinvoke 3. component = "component_name" 4. method="method_name"

5. returnvariable="return_variable"> 6. <cfinvokeargument name="arg1" value="value"> 7. <cfinvokeargument name="arg2" value="value"> </cfinvoke> 8. Using attribute-value pairs in the <cfinvoke> tag itself: 9. <cfinvoke 10. component = "component_name" 11. method="method_name" 12. returnvariable="return_variable" 13. arg1="value" arg2="value"> 14. Using the argumentCollection attribute with the value set to a structure storing name-value pairs: 15. <cfset args = StructNew()> 16. <cfset args.arg1 = "value"> 17. <cfset args.arg2 = "value"> 18. <cfinvoke 19. component="component_name" 20. method="method_name" 21. argumentCollection="#args#" returnVariable="return_variable"> The following example shows different ways of invoking the ImageFlipper method of the Images CFC: Code Sample: CFCs/Demos/Images.cfm <html> <head> <title>Invoking Component Methods</title> </head> <body> <h1>Invoking Component Methods</h1> <h2>Example 1: Transient Instantiation</h2> <cfinvoke component = "Images" method="ImageFlipper" returnvariable="imgpath"> <cfinvokeargument name="directory" value="Images/Runners"> </cfinvoke> <cfoutput><img src="#imgpath#"/></cfoutput>

<h2>Example 2: Using attributes rather than cfinvokeargument</h2> <cfinvoke component = "Images" method="ImageFlipper" returnvariable="imgpath" directory="Images/Runners"> <cfoutput><img src="#imgpath#"/></cfoutput> <h2>Example 3: Passing the arguments using argumentCollection</h2> <cfset args = StructNew()> <cfset args.directory = "Images/Runners"> <cfset args.extensions = "jpg"> <cfinvoke component="Images" method="ImageFlipper" argumentCollection="#args#" returnVariable="imgpath"> <cfoutput><img src="#imgpath#"/></cfoutput> <h2>Example 4: Separate instantiation and method invocation</h2> <cfset args = StructNew()> <cfset args.directory = "Images/Runners"> <cfset args.extensions = "jpg"> <cfobject name="objImage" component="Images2"> <cfloop index="i" from="1" to="7"> <cfinvoke component="#objImage#" method="ImageFlipper" directory="Images/Runners" extensions = "jpg" returnVariable="imgpath"> <cfoutput><img src="#imgpath#"/></cfoutput> </cfloop>

</body> </html> Transient Instantiation The first example above uses transient instantiation. As the component has not yet been instantiated, <cfinvoke> instantiates the component, calls the method and then destroys the component. This is useful when all you need to do is call one method on the object. The argument is passed using <cfinvokeargument>. The second and third examples are essentially the same, except the second uses attribute-value pairs and the third uses the argumentCollection attribute to pass arguments to the method. Separate Instantiation and Method Invocation The fourth example instantiates the component with the <cfobject> tag and then, using a loop, calles the ImageFlipper method seven times. Because the object is being used more than once, it's better to instantiate it separately. It will be destroyed when the page exits. THIS Scope You may also notice that the component is built a little differently. Instead of using session variables, it holds a picture variable in the THIS scope that stores the number of the picture showing. The ImageFlipper method changes the value of THIS.picture each time it is called. Variables in the THIS scope are properties of the component instance. That is, if another instance of this component were created, it would have its own picture property. Exercise: Writing and Invoking a CFC Method Duration: 15 to 25 minutes. In this exercise, you will write a new method called ImageRand in the images component. 1. Open CFCs/Exercises/Images.cfc in your editor. 2. Write a function called ImageRand that takes two arguments: o directory, which is required o extensions, which has the default value of "jpg, gif"

3. Create an aryImages array of the files specified in the directory. (Hint: use the already-defined getFileList method). 4. Set a variable called picture that gets a random number between 1 and the number of elements in aryImages. 5. Set the imagepath variable to contain the path to the randomly chosen image. 6. Return imagepath. 7. To test your solution, open CFCs/Exercises/RandImage.cfm in your browser, which calls the ImageRand method you just created. You should see a random image from the Images/Runners directory every time you refresh the page. Where is the solution? Extending ColdFusion Components ColdFusion components can inherit from other ColdFusion components by means of the extends attribute. The example below shows a ThumbNails component that extends the Images component. Code Sample: CFCs/Demos/ThumbNails.cfc <cfcomponent extends="Images"> <cffunction name="CreateImageTable" access="public" returntype="string"> <cfargument name="directory" type="string" required="true"> <cfargument name="columns" type="numeric" default="2"> <cfargument name="extensions" type="string" default="jpg,gif"> <cfset aryImages = getFileList(arguments.directory,arguments.extensions)> <cfset rows = Ceiling(ArrayLen(aryImages)/columns)> <cfset picture = 1> <cfsavecontent variable="ReturnTable"> <table border="1"> <cfloop index="i" from="1" to="#rows#"> <tr> <cfloop index="j" from="1" to="#columns#"> <td>

<cfoutput><img src="#arguments.directory#/#aryImages[picture]#"></cfo utput> </td> <cfset picture = picture + 1> </cfloop> </tr> </cfloop> </table> </cfsavecontent> <cfreturn ReturnTable> </cffunction> </cfcomponent> The ThumbNails component has a single method that displays all the images in the specified directory as a table. The following example makes use of that method and of a method in the parent Images component. Code Sample: CFCs/Demos/ThumbNails.cfm <html> <head> <title>Thumbnails</title> </head> <body> <h1>Thumbnails</h1> <cfobject name="objThumbNail" component="ThumbNails"> <cfinvoke component = "#objThumbNail#" method="CreateImageTable" returnvariable="ImgTable"> <cfinvokeargument name="directory" value="Images/Runners"> <cfinvokeargument name="columns" value="3"> </cfinvoke> <cfoutput>#ImgTable#</cfoutput> <hr /> <cfinvoke component = "#objThumbNail#" method="ImageFlipper" returnvariable="imgpath"> <cfinvokeargument name="directory" value="Images/Runners"> </cfinvoke> <cfoutput><img src="#imgpath#"/></cfoutput> </body>

</html> Code Explanation Although the examples above aren't very interesting in terms of functionality, they should give you the idea of how CFCs can be extended. Application.cfc There is a special reserved Application.cfc component that works much like the Application.cfm and OnRequestEnd.cfm files, but uses an objectoriented approach, which allows it to be extended in a way that the Application.cfm file cannot. Like Application.cfm, the Application.cfc component is called before each page request. Its THIS scope is used to set the same application properties that can be set with <cfapplication>'s attributes: Variable name applicationTimeout Name of application. Lifespan of application variables. Default is set in Administrator. Description

clientManagement Enable client variables. Default is set in Administrator. clientStorage Where to store client variables. Default is set in Administrator. Where to store login variables (in Cookies or Session). Default is "Cookie".

loginStorage

sessionManagement Enable session management. Default is "No". sessionTimeout setClientCookies setDomainCookies Lifespan of session variables. Default is set in Administrator. Enable cookies. Default is "Yes". Sets CFID and CFTOKEN for a domain. Default is "No".

Variable scriptProtect

Description Protect variables from cross-site scripting attacks. Default is set in Administrator.

The Application.cfc component has eight pre-named methods, none of which is required. They are described below. Application.cfc Methods onApplicationStart The onApplicationStart method is invoked when the first page of the application is requested. It can be used to:
    

test database connections. verify the existence of key files. test the mail server. set application variables. log the time the application started.

onApplicationEnd The onApplicationEnd method is invoked when the application times out. It can be used to log the time the application ended. onRequestStart The onRequestStart method is invoked at the start of every page request. It can be used to:
  

authenticate the user. output a standard header. (see footnote) include common function libraries.

onRequest The onRequest method is invoked immediately after the onRequestStart method. It takes one argument: the path to the target page. The method can be used to:


filter content.

 

output a standard header. output a standard footer.

onRequestEnd The onRequestEnd method is invoked at the end of every page request. It can be used to output a standard footer. onSessionStart The onSessionStart method is invoked when a user's session starts. It can be used to:
 

set session variables. log the time the session started.

onSessionEnd The onSessionEnd method is invoked when the session ends. It can be used to log the time the session ended. onError The onError method is invoked when an exception is not caught by a try/catch block. It can be used to:
  

Log errors. Display friendly error messages. Email the site administrator.

We cover the onError method in greater detail in the Handling Errors. Application.cfc Benefits Why do we need Application.cfc? What was wrong with Application.cfm? Application.cfm certainly is useful, but it has some major shortcomings, which Application.cfc addresses. Application.cfm Shortcomings Only one Application.cfm page per request Each time a page runs, ColdFusion looks for an Application.cfm page (see footnote) in the current directory and then in each parent directory up to

the web root until it finds one. As soon as it finds an Application.cfm page, it runs it and stops looking. The problem with this system is that it makes it difficult to treat any single directory differently from the rest of the application. To do so, you have to create an entirely new Application.cfm file for that directory, even though much of the code will be the same as in the other Application.cfm files in the application. If you decide to make an application-wide change, you will have to make the change in each Application.cfm file. ColdFusion actually searches for Application.cfc files in the same way it searches for Application.cfm files and it stops as soon as it finds one. The big difference is that one Application.cfc can extend another. This means that we don't have to repeat all the same code every time we want a slight change in functionality. No easy way to log sessions Application.cfm does not provide any mechanism for flagging the start and end of sessions. Application.cfc's onSessionStart and onSessionEnd methods are invoked when a session starts and ends, so the developer can easily log information about the visit. Application code runs every time a page is requested All the code in Application.cfm runs every time a page is requested. This makes it a less than ideal place for performing expensive tests like verifying a database connection. We can get around this by writing code to check if the database has already been verified: <cfif NOT isDefined("APPLICATION.DatabaseVerified")> <cftry> <cfquery name="VerifyDB" datasource="Animals"> SELECT AnimalType FROM tblAnimals </cfquery> <cflock timeout="5" throwontimeout="No" type="EXCLUSIVE" scope="SESSION"> <cfset APPLICATION.DatabaseVerified = true> </cflock> <cfcatch type="database"> <cflog file="Animals" type="error" text="Message: #cfcatch.message# Detail: #cfcatch.detail#

Native Error: #cfcatch.NativeErrorCode#"> </cfcatch> </cftry> </cfif> Application.cfc's onApplicationStart method makes this simpler as the method is only invoked one time: when the application is started. This means we don't have to check to see if the connection has already been verified and we don't have to lock application variables. It also allows us to take more serious action, like stopping the application from starting. The method might be written like this: <cffunction name="onApplicationStart" output="true"> <cftry> <cfquery name="VerifyDB" datasource="Animals"> SELECT AnimalType FROM tblAnimals </cfquery> <cflog file="Animals" type="Information" text="Application Started"> <cfcatch type="database"> <cfoutput>Datasource "Animals" not available.</cfoutput> <cflog file="Animals" type="error" text="Message: #cfcatch.message# Detail: #cfcatch.detail# Native Error: #cfcatch.NativeErrorCode#"> <cfreturn false> </cfcatch> </cftry> </cffunction> Extending Application.cfc - An Example (see footnote) Imagine a site for dogs, cats and mice. All certified animals (dogs, cats and mice) can access the main part of the site, but there is a part of the site reserved for cats and another part reserved for dogs. The directory structure looks like this: In this demo, we will indicate who the user is by passing "who=dog" or "who=cat" along the querystring. We want the application to act as follows:



Animals/index.cfm and other pages under the Animals directory, but not under the Cats or Dogs directories. o If URL.who is undefined or does not equal "dog", "cat" or "mouse": If URL.who equals "dog", "cat" or "mouse": Animals/Dogs/index.cfm and other pages under the Dogs directory.
o o o



If URL.who is undefined or does not equal "dog":



If URL.who equals "dog" : Animals/Cats/index.cfm and other pages under the Cats directory.
o o

If URL.who is undefined or does not equal "cat": If URL.who equals "cat" :

The index.cfm pages all look the same, except for the words "animal", "dog", and "cat". The code for Animals/index.cfm is shown below, followed by the code for its Application.cfc file. Code Sample: CFCs/Demos/Animals/index.cfm <html> <head> <title>You're an animal!</title> </head> <body> You're an animal! </body> </html> Code Sample: CFCs/Demos/Animals/Application.cfc <cfcomponent> <cfset THIS.name="Animals"> <cfset THIS.sessionManagement="true"> <cfset THIS.clientManagement="true"> <cfset THIS.setClientCookies="true"> <cfset THIS.applicationTimeout=CreateTimeSpan(2,0,0,0)> <cfset THIS.sessionTimeout=CreateTimeSpan(0,0,20,0)> <cffunction name="onApplicationStart"> <!--- POSSIBLE USES:

Test database connection Test mail server Set application variables (do not have to lock code) Log the start of the application ---> <cfset APPLICATION.animals = "dog,cat,mouse"> </cffunction> <cffunction name="onApplicationEnd"> <!--- POSSIBLE USES: Log the end of the application ---> </cffunction> <cffunction name="onRequestStart"> <!--- POSSIBLE USES: Authentication code Output header ---> <cfif NOT isDefined("URL.who") OR NOT ListContains(APPLICATION.animals,URL.who)> You must be a certified animal to visit this page. <cfabort> </cfif> </cffunction> <cffunction name="onRequest"> <!--- POSSIBLE USES: Filter content Output header Output footer ---> <cfargument name = "targetPage" type="String" required="true"/> <cfsavecontent variable="content"> <cfinclude template="#ARGUMENTS.targetPage#"> </cfsavecontent> <cfoutput> #replace(content, "badword", "%&!@", "all")# </cfoutput> </cffunction> <cffunction name="onRequestEnd">

<cfargument type="String" name = "targetTemplate" required="true"/> <!--- POSSIBLE USES: Output footer ---> <hr/> &copy; Animals Incorporated </cffunction> <cffunction name="onSessionStart"> <!--- POSSIBLE USES: Set session variables Log the start of the session ---> <cflock timeout="5" throwontimeout="No" type="EXCLUSIVE" scope="SESSION"> <cfset APPLICATION.sessions = APPLICATION.sessions + 1> </cflock> <cflock timeout="5" throwontimeout="No" type="EXCLUSIVE" scope="SESSION"> <cfset APPLICATION.currentSessions = APPLICATION.currentSessions + 1> </cflock> </cffunction> <cffunction name="onSessionEnd"> <cfargument name = "SessionScope" required="true"/> <!--- POSSIBLE USES: Log the end of the session ---> <cflock timeout="5" throwontimeout="No" type="EXCLUSIVE" scope="SESSION"> <cfset APPLICATION.currentSessions = APPLICATION.currentSessions - 1> </cflock> </cffunction> <cffunction name="onError"> <cfargument name="Exception" required="true"/> <cfargument type="String" name = "EventName" required="true"/>

<!--- POSSIBLE USES: Log errors Display error Email site administrator ---> </cffunction> </cfcomponent> Code Explanation This Application.cfc file includes code to set the THIS scope variables and it also includes all eight reserved methods. For our purposes, the interesting methods to look at are onApplicationStart, onRequestStart, and onRequestEnd. onApplicationStart sets APPLICATION.animals, which determines which animals are allowed on the site. onRequestStart checks to make sure that the visitor is an allowed animal. onRequestEnd outputs a standard footer. Now let's look at Dogs/Application.cfc. Code Sample: CFCs/Demos/Animals/Dogs/Application.cfc <cfcomponent extends="Animals.Application" output="yes"> <cffunction name="onRequestStart" output="yes"> <cfif NOT isDefined("URL.who") OR URL.who NEQ "dog"> You must be a dog to visit this page. <cfabort> </cfif> </cffunction> </cfcomponent> Code Explanation Things to notice:




The component extends Animals.Application. (For this to work, you'll need to create a mapping in Administrator to the CFCs/Demos/Animals.) This means that all the code in Animals/Application.cfc will run unless something is overridden. Only the onRequestStart method, which authenticates the user, is overridden.

CFCs/Demos/Animals/Cats/Application.cfc works exactly the same way. Exercise: Extending Application.cfc Duration: 20 to 30 minutes. In this exercise, you will create an Application.cfc file that outputs a header on every page. In a subdirectory, you will create another Application.cfc file that changes the header. 1. Open CFCs/Exercises/Animals/Application.cfc and review the onRequestStart method. You will see that it now outputs a simple navigation bar. 2. Write code so that pages in the Dogs, Cats, and Mice directories get their own navigation bars, which each have a single link back to Animals/index.cfm. Change the footer on pages in the Mice directory to include the text "Mice are cool!". Where is the solution? ColdFusion Components Conclusion In this lesson of the ColdFusion tutorial, you have learned how to create and use ColdFusion Components, which allow for an object-oriented style of programming in ColdFusion as they support inheritance and type safety. You have also learned how to use Application.cfc to make your application development more modular. Footnotes 1. The ColdFusion MX 7 documentation at http://livedocs.macromedia.com/coldfusion/7/htmldocs/wwhelp/wwhim pl/common/html/wwhelp.htm?context=ColdFusion_Documentation&fi le=00001022.htm states that the directories are searched in a different order, but in our testing we found it to be incorrect. 2. We do not recommend you use the Application.cfc functions to output content, as that content is difficult to modify from page to page. 3. Actually, it looks for an Application.cfc first and only looks for the Application.cfm page if it doesn't find any Application.cfc files. 4. Note that this example involves outputting content using the Application.cfc functions. We use this for illustrative purposes only. In

practice, we do not recommend you use these functions to output content to the page.

Handling Errors In this lesson of the ColdFusion tutorial, you will learn... 1. 2. 3. 4. To debug and fix compiler errors To create custom error pages To use structured error handling with cftry/cfcatch blocks To work with <cflock> to handle race conditions

ColdFusion categorizes errors into three buckets: 1. Exceptions - errors that stop normal processing. 2. Missing template errors - "page not found" errors. 3. Form field data validation errors - errors resulting from built-in ColdFusion server-side form validation using <cfform> or hidden fields. This lesson addresses the first two categories. Handling Exceptions There are two types of exceptions in ColdFusion: compiler exceptions and runtime exceptions. Compiler Exceptions Compiler exceptions occur when a ColdFusion page cannot run at all because of some error in the code. For example, a compiler exception could be caused by any of the following:

   

missing angle bracket an extraneous pound sign (#) a missing required attribute an attribute that doesn't belong

The following page will not compile: Code Sample: Errors/Demos/CompilerException.cfm <cfif></cfif> Code Explanation Part of the resulting error page is shown below: The suggestions are generally not very helpful; however, in most cases the cause of a compiler exception is not too difficult to identify. Sometimes on pages with a lot of code, it can be difficult to locate the problem, especially if it is caused by a missing or extra close tag in code with a lot of nested tags. To illustrate, take a look at the following page. Code Sample: Errors/Demos/CompilerException2.cfm <cfset a = "dog"> <cfset b = "cat"> <cfset c = "mouse"> <cfset d = "horse"> <cfset e = "cow"> <cfset f = "pig"> <cfset g = "donkey"> <cfif true> <cfoutput>#a#</cfoutput> <cfif true> <cfif true> <cfoutput>#b#</cfoutput> </cfif> <cfoutput>#c#</cfoutput> <cfif true> <cfif true> <cfoutput>#d#</cfoutput> </cfif> <cfif true> <cfoutput>#e#</cfoutput> </cfif>

</cfif> <cfoutput>#f#</cfoutput> </cfif> </cfif> <cfoutput>#g#</cfoutput> </cfif> Code Explanation For simplicity, we have used "true" where a condition would be, but imagine that these conditions might be true or false under different circumstances. Part of the resulting error page is shown below: So, you're missing a start <cfif>. How do you identify which one? Or perhaps the real problem is that you have one too many close </cfif> tags. One problem with this type of error is that you can get rid of the exception, but leave yourself with bad logic. For example, you could simply remove the last </cfif>. The exception goes away and the page displays: "dog cat mouse horse cow pig donkey". But, suppose that you want the output to read: "dog cat mouse pig donkey". This kind of problem can be tricky to solve and unfortunately there is no simple answer, but we have a few recommendations that should make your coding life easier. 1. Indent your code. This seems like a no brainer, but we see a lot of code written by experienced developers that is impossible to read simply because it isn't laid out nicely. Dreamweaver has a great "Apply Source Formatting" feature (under the Commands menu) to help lay out the code, but we recommend indenting as you write as it will help you avoid these types of errors in the first place. 2. Nest your code sensibly. For example, don't use multiple <cfoutput> tags when one will do the trick. 3. Use comments. Everybody recommends them, but they are used too infrequently. Comments will help you identify problems in your code and will allow you to focus on the solution. Even small comments can be helpful. We have made some changes to our animals page:

Code Sample: Errors/Demos/CompilerException2-formatted.cfm <cfset a = "dog"> <cfset b = "cat"> <cfset c = "mouse"> <cfset d = "horse"> <cfset e = "cow"> <cfset f = "pig"> <cfset g = "donkey"> <cfoutput> <cfif true> #a# <!---dog---> <cfif true> <cfif true> #b#<!---cat---> <cfif true> #c#<!---mouse---> <cfif true> #d#<!---horse---> </cfif> <cfif true> #e#<!---cow---> </cfif> </cfif> #f#<!---pig---> </cfif> </cfif> #g#<!---donkey---> </cfif> </cfif> </cfoutput> Code Explanation The error hasn't been fixed, but now it is a lot easier to identify the problem. We know that we want the output to read "dog cat mouse pig donkey". We do not want "horse" and "cow" to be output. There are at least a couple of ways we can solve this. One is shown below: Code Sample: Errors/Demos/CompilerException2-fixed.cfm <cfset a = "dog"> <cfset b = "cat"> <cfset c = "mouse">

<cfset <cfset <cfset <cfset

d e f g

= = = =

"horse"> "cow"> "pig"> "donkey">

<cfoutput> <cfif true> #a# <!---dog---> <cfif true> <cfif true> #b#<!---cat---> <cfif true> #c#<!---mouse---> <cfif false> <cfif true> #d#<!---horse---> </cfif> <cfif true> #e#<!---cow---> </cfif> </cfif> #f#<!---pig---> </cfif> </cfif> #g#<!---donkey---> </cfif> </cfif> </cfoutput> Exercise: Fixing Compiler Exceptions Duration: 10 to 15 minutes. In this exercise, you will fix a compiler exception similar to the one we just saw. Your goal is to get the output to read "You have fixed the error!" 1. Open Errors/Exercises/CompilerException.cfm in your editor and save it as CompilerException2.cfm. 2. Fix it. If you get messed up, you can recopy the original Errors/Exercises/CompilerException.cfm. 3. To test your solution, open Errors/Exercises/CompilerException2.cfm in your browser.

Where is the solution? Runtime Exceptions Runtime exceptions are errors that are caught at the time the code is executed. They can be handled at the site level, at the application level, and at the page level. In this section, we will look at handling runtime exceptions at the site and application levels. In the next section we will look at pagelevel structured error handling. Missing Template Handler Page In ColdFusion Administrator, under Server Settings -> Settings, you can specify a missing template handler. Usually, you'll use this to provide a more friendly error. You may want to include your navigation bars on the template to make it easier for the users to find what they are looking for. Site-wide Error Handler Page In ColdFusion Administrator, under Server Settings > Settings, you can specify a site-wide error handler. This page can contain CFML, but you should nest anything potentially dangerous in try/catch blocks, which we will discuss shortly. The following example logs the error, returns an error to the screen (probably with too much information), and emails the site administrator an error dump. Note that the site-wide error handler will be used for compiler exceptions as well. Code Sample: Errors/Demos/SiteWideErrorPage.cfm <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Oops!</title> <style type="text/css"> .Label {margin-top:10px; font-weight:bold; width:200px;} .Details {margin-left:10px; font-style:italic; width:500px;} </style> </head> <body> <h1>Oops!</h1> <p>Sorry, we have experienced and error.</p>

<cftry> <!---LOG ERROR---> <cflog text="#ERROR.Diagnostics#, #ERROR.HTTPReferer#, #ERROR.Template##ERROR.QueryString#" log="application" type="Error"> <!---OUTPUT ERROR INFORMATION - may not want to do this on a live application---> <cfoutput> <div class="Label">Time of Error:</div> <div class="Details">#DateFormat(ERROR.DateTime)# at #TimeFormat(ERROR.DateTime)#</div> <div class="Label">Diagnostic Information:</div> <div class="Details">#ERROR.Diagnostics#</div> <div class="Label">Referrer:</div> <div class="Details">#ERROR.HTTPReferer#</div> <div class="Label">Template Causing Error:</div> <div class="Details">#ERROR.Template#?#ERROR.QueryString#</ div> </cfoutput> <cfcatch> No information available. </cfcatch> </cftry> <!---SEND EMAIL TO SITE ADMINISTRATOR---> <cftry> <cfmail to="admin@webucator.com" subject="#ERROR.Template# error" type="html" from="errors@webucator.com"> <cfdump var="#ERROR#"> </cfmail> <hr/> <p>The site administrator has been informed.</p> <cfcatch type="any"> <cftry> <hr/> <cfoutput>

<p>Please email the site administrator at <a href="mailto:admin@webucator.com">admin@webucator.com< /a>.</p> </cfoutput> <cfcatch type="any"></cfcatch> </cftry> </cfcatch> </cftry> </body> </html> Code Explanation Run Errors/Demos/SiteWideErrorDemo.cfm, which tries to read an undefined variable, in your browser and you will get the following result:

The logged error (in cfusion/cfusion-ear/cfusion-war/WEBINF/cfusion/logs/application.log) will read something like: "Error","jrpp14","08/12/06","15:26:51","WEBUCATORCFM","Element FOO is undefined in VARIABLES. <br>The error occurred on line 6., , /Courseware/CFM201/ClassFiles/Errors/Demos/SiteWideErr orDemo.cfm" Handling Errors with <cferror> The <cferror> tag, which generally goes in the Application.cfm file, is used to indicate custom error templates for different types of errors. The type is specified with <cferror>'s type attribute. Possible values are: 1. validation - has to do with server-side form validation, which we are not covering here. 2. exception - displayed when runtime exceptions occur. You can use different templates for different types of exceptions. 3. request - serves as a backup if errors occur in exception error templates. You cannot use CFML tags in this template, but you may output error variables by enclosing them in pound signs (e.g, #ERROR.Template#).

The following example shows a simple Application.cfm file that makes use of <cferror>. It is followed by examples of an exception error template and a request error template. Code Sample: Errors/Demos2/Application.cfm <cfapplication sessionmanagement="yes" clientmanagement="yes" name="ErrorDemos"> <cferror type="exception" template="ErrorException.cfm" mailto="admin@webucator.com"> <cferror type="request" template="ErrorRequest.cfm" mailto="admin@webucator.com"> <cferror type="validation" template="ErrorValidation.cfm" mailto="admin@webucator.com"> Code Sample: Errors/Demos2/ErrorException.cfm <html> <head> <title>Exception</title> </head> <body> <h1>Exception Error!</h1> <cfoutput> <table border="1"> <tr> <th>Name</th> <th>Value</th> </tr> <tr> <td>Error.Browser</td> <td>#Error.Browser#</td> </tr> <tr> <td>Error.DateTime</td> <td>#Error.DateTime#</td> </tr> <tr> <td>Error.Diagnostics</td> <td>#Error.Diagnostics#</td>

</tr> <tr> <td>Error.GeneratedContent</td> <td>#Error.GeneratedContent#</td> </tr> <tr> <td>Error.HTTPReferer</td> <td>#Error.HTTPReferer#</td> </tr> <tr> <td>Error.MailTo</td> <td>#Error.MailTo#</td> </tr> <tr> <td>Error.QueryString</td> <td>#Error.QueryString#</td> </tr> <tr> <td>Error.Template</td> <td>#Error.Template#</td> </tr> <tr> <td>Error.RemoteAddress</td> <td>#Error.RemoteAddress#</td> </tr> <tr> <td>Error.Type</td> <td>#Error.Type#</td> </tr> <tr> <td>Error.RootCause</td> <td>#Error.RootCause#</td> </tr> <tr> <td>Error.Message</td> <td>#Error.Message#</td> </tr> </table> </cfoutput> </body> </html>

Code Sample: Errors/Demos2/ErrorRequest.cfm <html> <head> <title>Request Error</title> </head> <body> <h1>Request Error!</h1> <table border="1"> <tr> <th>Name</th> <th>Value</th> </tr> <tr> <td>Error.Browser</td> <td>#Error.Browser#</td> </tr> <tr> <td>Error.DateTime</td> <td>#Error.DateTime#</td> </tr> <tr> <td>Error.Diagnostics</td> <td>#Error.Diagnostics#</td> </tr> <tr> <td>Error.GeneratedContent</td> <td>#Error.GeneratedContent#</td> </tr> <tr> <td>Error.HTTPReferer</td> <td>#Error.HTTPReferer#</td> </tr> <tr> <td>Error.MailTo</td> <td>#Error.MailTo#</td> </tr> <tr> <td>Error.QueryString</td> <td>#Error.QueryString#</td> </tr>

<tr> <td>Error.Template</td> <td>#Error.Template#</td> </tr> <tr> <td>Error.RemoteAddress</td> <td>#Error.RemoteAddress#</td> </tr> </table> </body> </html> Code Explanation To generate an exception error, open any of the following files: Code Sample: Errors/Demos2/CauseException1.cfm <cfinclude template="Missing.cfm"> Code Sample: Errors/Demos2/CauseException2.cfm <cf_foo> Code Sample: Errors/Demos2/CauseException3.cfm <cfquery datasource="NoSuchDS"></cfquery> To generate a request error, run any of the files above after doing one of the following: 1. Comment out the exception error template in Errors/Demos2/Application.cfm. 2. Make the ColdFusion code in Errors/Demos2/ErrorException.cfm invalid. Error Variables Exception and request errors share the following variables: Exception and Request Error Variables Variable ERROR.browser The user's browser. Description

Exception and Request Error Variables Variable ERROR.dateTime ERROR.diagnostics ERROR.generatedContent ERROR.HTTPReferer ERROR.mailTo ERROR.queryString ERROR.remoteAddress ERROR.template Description The date and time of the error. Detailed diagnostic information. Any content generated by the request before the error occurred. The referring page, if any. The email address of the administrator as set in the <cferror> tag. Query string of the request, if any. The IP address if the user's machine. The page being executed when the error occurred.

In addition, exception errors have these additional variables: Exception-only Error Variables Variable ERROR.message Description Message associated with the exception.

ERROR.rootCause A structure containing the data returned by <cfcatch>. ERROR.tagContext ERROR.type An array of structures containing information for each tag that is currently open. The exception type.

Handling Errors with the onError Method The onError method of the Application.cfc component works much like the <cferror> tag. The method gets two arguments:

1. An exception structure. 2. The name of the Application.cfc method in which the error occurred. This will be blank unless the error occurred in some of the code in the Application.cfc file. The method is generally used to log errors and to display a friendly error message. It can also be used to email information about the error to the site administrator. A sample is shown below. Code Sample: Errors/Demos3/Application.cfc <cfcomponent> <cfset THIS.name = "ErrorDemos"> <cffunction name="onRequestStart"> <!---Uncomment the tag below to cause an exception in this file---> <!---<cf_foo>---> </cffunction> <cffunction name="onError" output="true"> <cfargument name="Except" required="true"/> <cfargument name="EventName" type="String" required="true"/> <!---Log Errors---> <cfset Message = Except.message> <cfif Len(EventName)> <cfset Message = Message & " The error occurred in " & EventName & "."> <cfelse> <cfset Message = Message & " The error occurred in " & CGI.SCRIPT_NAME & "."> </cfif> <cflog file="#THIS.Name#" type="error" text="Message: #Message#"> <!--- Throw validation errors to ColdFusion for handling. ---> <cfif Find("coldfusion.filter.FormValidationException", ARGUMENTS.Except.StackTrace)> <cfthrow object="#except#"> <cfelse>

<!--- You can replace this cfoutput tag a friendlier error message. ---> <cfoutput> <h1>Error Event: #EventName#</h1> <h2>Error details:</h2> <cfdump var="#except#"> </cfoutput> </cfif> </cffunction> </cfcomponent> Code Explanation If an uncaught exception occurs, the onError method is invoked. It does the following: 1. Logs the error. If the exception occurred in an Application.cfc method, it reports the method name. Otherwise, it reports the path to the requested page. It uses the application name (THIS.name) as the name of the log file, so that it can be found easily. 2. Determines if the error is a form validation exception, in which case, it turns the error over to ColdFusion to handle. If it is not, then it outputs an error message. cftry/cfcatch So far, we have been looking at how to handle uncaught exceptions. It's much better if you can anticipate the exception, catch it, and respond. We do this with try/catch blocks. The syntax is shown below: Syntax <cftry> <!---Code that might cause a problem---> <cfcatch type="Exception_Type"> <!---Error message---> </cfcatch> </cftry> The major benefit of using try/catch blocks is that you can continue processing the page in the event that an error occurs. For example, you might have code that inserts a record into a database. Such code can fail if the database server is down (or for a number of other reasons). If the insert

fails, you could catch the error and report it by email to the site administrator. However, the email code could also fail, so you might put that in a try/catch block as well. The code might look like this: Code Sample: Errors/Demos/TakeTwo.cfm <cftry> <cfquery datasource="dsn"> --Insert record </cfquery> Record inserted. <cfcatch type="database"> <cftry> <cfmail to="admin@webucator.com" from="errors@webucator.com" subject="Database error" type="html"> Database error. Record could not be inserted. <cfdump var="#cfcatch#"> </cfmail> Sorry, we could not insert the record. The site administrator has been informed. <cfcatch type="any"> Sorry, we could not insert the record. Please email the site administrator at admin@webucator.com. </cfcatch> </cftry> </cfcatch> </cftry> Exception Types The table below shows the types of exceptions that can occur. These values can be used for the type attribute of the <cfcatch> tag. Exception Types Exception Type Any Description All exceptions. Useful if you're not sure what type of exception could occur.

Exception Types Exception Type Description Custom exceptions thrown by <cfthrow> that either do not specify the type of exception or specify "Application" as the type. Invalid expressions, such as trying to concatenate with a plus sign (+). Occur when a cflock times out. Errors where files specified by the <cfinclude>, <cfmodule>, and <cferror> tags cannot be found. Errors that occur in code dealing with objects.

Application

Expression Lock MissingInclude Object

SearchEngine Errors that occur in code dealing with the Verity search engine. Security Template Errors that occur in security code. Errors that occur in ColdFusion syntax. These errors are usually caught at compile time.

Locking Code to Prevent Errors ColdFusion is a multi-threaded application, which means that it can process many requests at the same time. In some case, two requests may be trying to access the same memory space or the same file. Any code that lends itself to this possibility should be locked. To understand why, you need to understand the concept of a race condition. Race Conditions Race conditions occur when multiple requests attempt to modify the same data at the same moment. In Macromedia ColdFusion MX 7 Web Application Construction Kit, the authors use the following example: (see footnote)

<cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> In this one line of code, APPLICATION.hitCount is being read and modified. Although this happens very quickly, it is possible that two simultaneous requests read the value at the exact same time before either of them has the chance to modify it. Hence, they each read the same value (e.g, 100). They both then add 1 to the value and come up with the same number and set the new hitCount to 101. But since there were actually two separate hits, the new hitCount should be 102. Although hit counts are not usually mission critical, this does illustrate the problem. The solution is to wrap the code in an exclusive lock: <cflock type="Exclusive" scope="application" timeout="10"> <cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> </cflock> This forces single-threaded access to this code, which will prevent a race condition from occurring. We used an Exclusive lock, because the memory variable is being modified. In cases where the variable is only being read, you could use a ReadOnly lock: <cflock type="ReadOnly" scope="application" timeout="10"> <cfoutput>Page hits: #APPLICATION.hitCount#</cfoutput> </cflock> Named Locks The examples above used scoped locks, which lock the whole application scope until the code inside the lock has been processed. While this is certainly safe, it can be overkill. To see how, take a look at the following code: //In the onRequestStart method of Application.cfc <cflock type="Exclusive" scope="application" timeout="10"> <cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> </cflock>

//In the onSessionStart method of Application.cfc <cflock type="Exclusive" scope="application" timeout="10"> <cfset APPLICATION.sessionCount = APPLICATION.sessionCount + 1> </cflock> While it's possible that these two pieces of code might be accessed at the exact same time by multiple users, this won't cause any conflict. Yet the way it is written, each request will have to cue up behind the next. Using named locks provides more granularity: //In the onRequestStart method of Application.cfc <cflock type="Exclusive" name="HitCountLock" timeout="10"> <cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> </cflock> //In the onSessionStart method of Application.cfc <cflock type="Exclusive" name="SessionCountLock" timeout="10"> <cfset APPLICATION.sessionCount = APPLICATION.sessionCount + 1> </cflock> This way, requests only get cued up behind locks of the same name. A couple of warnings:




Be very careful to spell your lock names the same! Typos are very dangerous. Lock names are server-wide, so if you are running multiple applications on the same server, you will want to include the application's name in the lock name (e.g, name="#APPLICATION.applicationName#SessionCountLock").

Handling Errors Conclusion In this lesson of the ColdFusion tutorial, you have learned to debug and fix compiler errors, to create custom error pages, to use try/catch blocks to catch runtime exceptions, and to work with <cflock> to handle race conditions.

Footnotes 1. Macromedia ColdFusion MX 7 Web Application Construction Kit, Ben Forta & Raymond Camden, p. 554

ColdFusion and XML In this lesson of the ColdFusion tutorial, you will learn... 1. To work with ColdFusion's XML functions 2. To process RSS feeds with ColdFusion 3. To use WDDX to transfer data across the web XML Functions ColdFusion provides many XML functions for dealing with XML documents. This section covers those functions and their functionality. Creating New XML Documents There are several ways to create new XML documents, which ColdFusion treats as structures. The root element of an XML document can be accessed as follows: xmlDoc.xmlRoot; or by the name of the root element: xmlDoc.rootName, where the root element is <rootName/>. XmlParse() The XmlParse() function is used to convert an XML string into an XML document object, which makes the XML searchable through other ColdFusion XML functions. The function takes the following parameters: XmlParse() Parameters Parameter Description

XmlParse() Parameters Parameter Description Required. Can be any of the following: xmlText
 

An XML string A absolute path or URL to an XML file

Optional boolean. If true, the object's properties (elements and attributes) will be case sensitive. The default is false. When an caseSensitive object is case sensitive, you must use bracket notation to reference elements and attributes. Optional. Can be either of the following: validator
 

A string containing a DTD or XML schema An absolute path or URL to a DTD or XML schema

The following sample shows how it is used. Code Sample: XML/Demos/XmlParse.cfm <html> <head> <title>XmlParse()</title> </head> <body> <h1>XmlParse()</h1> <cfset xmlGeorge=XMLParse(ExpandPath("George.xml"))> <cfoutput> <h2>#xmlGeorge.Name.XmlAttributes.Title# #xmlGeorge.Name.FirstName# #xmlGeorge.Name.LastName#</h2> </cfoutput> <cfdump var="#xmlGeorge#"> </body> </html> Code Explanation In a browser, the page looks like this:

As you can see, the XML document's properties can be accessed like a ColdFusion structure. Elements can be accessed as direct properties of their parent element. Attributes must be accessed through the XmlAttributes structure. (see footnote) XmlNew(), XmlElemNew(), xmlRoot, XmlChildren, XmlAttributes New XML documents can also be built from scratch using the XmlNew() function. Elements are added with the XmlElemNew() function, which requires the XML document object of which the element will be a part and the name of the new element. (see footnote) The root element should be assigned to the xmlRoot property of the XML document. Elements and attributes are positioned in the document with XmlChildren and XmlAttributes. The following document shows how to build an XML document from scratch. Code Sample: XML/Demos/XmlNew.cfm <!---Create the XML document---> <cfset docGeorge=XmlNew()> <!---Set the root element---> <cfset docGeorge.xmlRoot = XmlElemNew(docGeorge,"Name")> <!---Set an attribute of the root element---> <cfset docGeorge.xmlRoot.XmlAttributes.Title = "President"> <!---Create the FirstName and LastName elements---> <cfset elemFirstName = XmlElemNew(docGeorge,"FirstName")> <cfset elemLastName = XmlElemNew(docGeorge,"LastName")> <!---Set the text values of the FirstName and LastName elements---> <cfset elemFirstName.XmlText = "George"> <cfset elemLastName.XmlText = "Washington"> <!--Append the FirstName and LastName elements to the root element

Notice this can be done using xmlRoot or the name of the root element ---> <cfset docGeorge.xmlRoot.XmlChildren[1] = elemFirstName> <cfset docGeorge.Name.XmlChildren[2] = elemLastName> <!---Convert the XML document to a string---> <cfset xmlString = ToString(docGeorge)> <!---Set the content to text/xml, reset the buffer, and output the XML string---> <cfcontent type="text/xml" reset="yes"><cfoutput>#xmlString#</cfoutput> Code Explanation In a browser, the page looks like this: The <cfxml> Tag The <cfxml> tag can be used to create an XML document object directly in XML. It tends to be easier than using XmlNew() if the XML structure is not determined programatically. The following example shows how to use <cfxml>. Code Sample: XML/Demos/cfxml.cfm <cfxml variable="docGeorge"> <?xml version="1.0" encoding="iso-8859-1"?> <Name xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xsi:noNamespaceSchemaLocation="Name.xsd" Title="President"> <FirstName>George</FirstName> <LastName>Washington</LastName> </Name> </cfxml> <!---Convert the XML document to a string---> <cfset xmlString = ToString(docGeorge)> <!---Set the content to text/xml, reset the buffer, and output the XML string--->

<cfcontent type="text/xml" reset="yes"><cfoutput>#xmlString#</cfoutput> XmlTransform() The XmlTransform() function is used to transform XML documents against XSLT documents. It returns a string containing the result of the transformation. The function takes the following parameters: XmlTransform() Parameters Parameter Description Required. Can be either of the following: xml
 

An XML string An XML document object

Required. Can be either of the following: xsl
 

An XSLT string An absolute path or URL to the XSLT

Optional. A structure containing name-value pairs that map to the parameters parameters defined in the XSLT. The properties of the structure must be added via the StructInsert() function. To illustrate, we will use the following XML and XSLT documents: Code Sample: XML/Demos/George.xml <?xml version="1.0" encoding="iso-8859-1"?> <Name xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xsi:noNamespaceSchemaLocation="Name.xsd" Title="President"> <FirstName>George</FirstName> <LastName>Washington</LastName> </Name> Code Sample: XML/Demos/Name.xsl <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:param name="bgcolor" select="'white'"/> <xsl:output method="xml" version="1.0" encoding="UTF8" indent="yes"/> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="Name/@Title"/> <xsl:text> </xsl:text> <xsl:value-of select="Name/FirstName"/> <xsl:text> </xsl:text> <xsl:value-of select="Name/LastName"/> </title> </head> <body bgcolor="{$bgcolor}"> <h1> <xsl:value-of select="Name/@Title"/> <xsl:text> </xsl:text> <xsl:value-of select="Name/FirstName"/> <xsl:text> </xsl:text> <xsl:value-of select="Name/LastName"/></h1> </body> </html> </xsl:template> </xsl:stylesheet> Notice that Name.xsl can accept a bgcolor parameter, which it uses to set the background color of the page. The following ColdFusion file will transform George.xml against Name.xsl. Code Sample: XML/Demos/XmlTransform.cfm <cffile action="read" variable="strXML" file="#ExpandPath('George.xml')#"> <cfset PathToXslt = ExpandPath("Name.xsl")> <cfset params = StructNew()> <cfset StructInsert(params,"bgcolor","silver")> <cfoutput>#XmlTransform(strXML,PathToXslt,params)#</cf output> Code Explanation

Notice that we have to read the XML file into a string with <cffile> before passing it to XmlTransform(), which cannot accept a path to the XML file as the first argument. The resulting HTML code will look like this: The page will display as follows: XmlFormat() The XmlFormat() function is used to escape special XML characters so that they can be displayed in a browser. For example, the < character becomes &lt;. The following example shows how we could display the code resulting from the transformation we just did in the browser window. Code Sample: XML/Demos/XmlFormat.cfm <cffile action="read" variable="strXML" file="#ExpandPath('George.xml')#"> <cfset PathToXslt = ExpandPath("Name.xsl")> <cfset params = StructNew()> <cfset StructInsert(params,"bgcolor","silver")> <cfset result = XmlTransform(strXML,PathToXslt,params)> <cfset formattedXml = XmlFormat(result)> <html> <head> <title>XmlFormat()</title> </head> <body> <h1>XmlFormat()</h1> <cfoutput> <pre>#formattedXml#</pre> </cfoutput> </body> </html> Code Explanation The resulting page is shown below:

Searching XML Documents XML documents can be searched using the XmlSearch() function. Specific elements can be located using the XmlChildPos() function. XmlSearch() The XmlSearch() function uses an XPath expression to search an XML document object. The function takes two arguments: the document object to search and the XPath expression. It returns an array of XML nodes that match the expression. The code samples below show how the function is used. The first sample is the XML document being searched. The second sample is the ColdFusion file that uses the XmlSearch() function. Code Sample: XML/Demos/Beatles.xml <?xml version="1.0"?> <beatles> <beatle link="http://www.paulmccartney.com"> <name> <firstname>Paul</firstname> <lastname>McCartney</lastname> </name> </beatle> <beatle link="http://www.johnlennon.com"> <name> <firstname>John</firstname> <lastname>Lennon</lastname> </name> </beatle> <beatle link="http://www.georgeharrison.com"> <name> <firstname>George</firstname> <lastname>Harrison</lastname> </name> </beatle> <beatle link="http://www.ringostarr.com"> <name> <firstname>Ringo</firstname> <lastname>Starr</lastname> </name> </beatle> </beatles>

Code Sample: XML/Demos/XmlSearch.cfm <html> <head> <title>XmlSearch()</title> </head> <body> <h1>XmlSearch()</h1> <cfset xmlBeatles=XMLParse(ExpandPath("Beatles.xml"))> <cfoutput> <table border="1" cellpadding="2" cellspacing="0"> <tr> <th>XPath</th> <th>Result</th> </tr> <cfset arrXPs = ArrayNew(1)> <cfset ArrayAppend(arrXPs,"/beatles")> <cfset ArrayAppend(arrXPs,"/beatles/beatle/@link")> <cfset ArrayAppend(arrXPs,"/beatles//firstname")> <cfset ArrayAppend(arrXPs,"/beatles//firstname/text()")> <cfloop index="i" from="1" to="#ArrayLen(arrXPs)#"> <cfset xp = arrXPs[i]> <cfset arrResult = XmlSearch(xmlBeatles,xp)> <tr valign="top"> <td>#xp#</td> <td> <ol> <cfloop index="i" from="1" to="#ArrayLen(arrResult)#"> <li> #XmlGetNodeType(arrResult[i])#: <cftry> #arrResult[i].XmlText# <cfcatch type="any">#arrResult[i]#</cfcatch> </cftry> </li> </cfloop> </ol> </td> </tr> </cfloop>

</table> </cfoutput> </body> </html> Code Explanation The code loops through a set of XPaths, uses each one to search the XML document and displays the results as a list. If the resulting node has an XmlText property (e.g, an element), that property is displayed. If it does not (e.g, an attribute), the node itself is displayed. The output is shown below: XmlChildPos() The XmlChildPos() function finds the position of a node within its parent element. This can be used for adding or removing elements to the XML DOM. The code samples below show how the function is used. The first sample is the XML document being searched. The second sample is the ColdFusion file that uses the XmlChildPos() function. Code Sample: XML/Demos/RockStars.xml <?xml version="1.0" encoding="iso-8859-1"?> <rockstars> <beatle link="http://www.paulmccartney.com"> <name> <firstname>Paul</firstname> <lastname>McCartney</lastname> </name> </beatle> <beatle link="http://www.johnlennon.com"> <name> <firstname>John</firstname> <lastname>Lennon</lastname> </name> </beatle> <stone link="http://www.mickjagger.com" > <name> <firstname>Mick</firstname> <lastname>Jagger</lastname> </name> </stone> <stone link="http://www.keithrichards.com" >

<name> <firstname>Keith</firstname> <lastname>Richards</lastname> </name> </stone> <beatle link="http://www.webucator.com"> <name> <firstname>Nat</firstname> <lastname>Dunn</lastname> </name> </beatle> </rockstars> Code Sample: XML/Demos/XmlChildPos.cfm <cfset xmlRockStars=XMLParse(ExpandPath("RockStars.xml"))> <cfset arrRockStars=xmlRockStars.rockstars.XmlChildren> <html> <head> <title>XmlChildPos()</title> </head> <body> <h1>XmlChildPos()</h1> <cfoutput> <ol> <cfloop index="i" from="1" to="#ArrayLen(arrRockStars)#"> <li>#arrRockStars[i]#</li> </cfloop> </ol> <cfset notARockStarPos = XmlChildPos(xmlRockStars.rockstars,"beatle",3)> <div style="color:red; font-size:larger;"> The person at position #notARockStarPos# is not a rock star and will be deleted. </div> <cfset ArrayDeleteAt(arrRockStars,notARockStarPos)>

<ol> <cfloop index="i" from="1" to="#ArrayLen(arrRockStars)#"> <li>#arrRockStars[i]#</li> </cfloop> </ol> </cfoutput> </body> </html> Code Explanation The document displays all the XML children of the rockstars element. The XmlChildPos() function finds the third beatle element and identifies it as the fifth element within rockstars and uses that information to delete the element from the rockstars.XmlChildren array. It then redisplays the XML children of the rockstars element. Notice that the non-rockstar is missing: Validating XML Documents ColdFusion provides functions for checking if an XML document is well formed or valid against an XML schema or DTD. There are additional functions for checking different parts of an XML document. These functions are explained below. IsXML() The IsXML() function determines whether the passed-in string is wellformed XML. It returns true if it is and false if it is not. The following sample shows how it is used. Code Sample: XML/Demos/IsXML.cfm <cfparam name="FORM.XmlText" default="<Name> <FirstName>George</FirstName> <LastName>Washington</LastName> </Name>"> <html> <head> <title>IsXML()</title> </head> <body> <h1>XML Test</h1>

<cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> <textarea name="XmlText" cols="60" rows="10">#FORM.XmlText#</textarea> <br><input name="SubmitButton" type="submit" value="Check XML"> <cfif isDefined("FORM.SubmitButton")> <cfif IsXML(FORM.XmlText)> <span style="margin-left:200px;color:green">XML is well formed.</span> <cfelse> <span style="margin-left:200px;color:red">XML is not well formed.</span> </cfif> </cfif> </form> </cfoutput> </body> </html> Code Explanation The page simply displays a form with a textarea for holding XML. When the form is submitted, the page uses the IsXML() function to check if the text is well-formed XML and displays a message with the results. When the page is first opened, it looks like this: When the form is submitted with well-formed XML, the page displays as follows (notice the little message of success in the bottom right corner): When the form is submitted with poorly-formed XML, the page displays as follows: (see footnote) XmlValidate() The XmlValidate() function is used to validate an XML document against a DTD or XML schema. The function takes the following parameters: XmlValidate() Parameters Parameter Description

XmlValidate() Parameters Parameter Description Required. Can be any of the following: xmlDoc
  

An XML string An absolute path or URL to an XML file An XML document object

Optional. Can be either of the following: validator
 

A string containing a DTD or XML schema An absolute path or URL to a DTD or XML schema

The function returns a structure containing the following fields:
  



Status - A boolean indicating if validation was successful. Errors - An array of errors. FatalErrors - An array of fatal errors, usually indicating that the XML document is not well formed. Warnings - An array of warnings.

To illustrate, we will use the following simple XML schema: Code Sample: XML/Demos/Name.xsd <?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Name"> <xs:complexType> <xs:sequence> <xs:element name="FirstName" type="xs:string"/> <xs:element name="LastName" type="xs:string"/> </xs:sequence> <xs:attribute name="Title" type="xs:string" use="optional"/> </xs:complexType> </xs:element> </xs:schema> Code Explanation

The schema requires the document element Name to have two children, FirstName and LastName, and an optional attribute, Title. ColdFusion uses the Xerces parser to validate XML documents. To validate correctly when the validator parameter is being used, the parser requires the xmlns:xsi and xsi:noNamespaceSchemaLocation attributes in the document element of the XML being validated. To illustrate, we will use a demo similar to the IsXML() demo. Code Sample: XML/Demos/XmlValidate.cfm <cfparam name="FORM.XmlText" default="<Name xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation=''> <FirstName>George</FirstName> <LastName>Washington</LastName> </Name>"> <html> <head> <title>XmlValidate()</title> </head> <body> <h1>XML Validation Test</h1> <cfoutput> <form method="post" action="#CGI.SCRIPT_NAME#"> <textarea name="XmlText" cols="60" rows="10">#FORM.XmlText#</textarea> <br><input name="SubmitButton" type="submit" value="Validate XML"> <cfif isDefined("FORM.SubmitButton")> <cfset structResults = XmlValidate(FORM.XmlText,ExpandPath("Name.xsd"))> <cfif structResults.status> <span style="margin-left:200px;color:green">XML is valid.</span> <cfelse> <span style="margin-left:200px;color:red">XML is not valid.</span> <div style="margin-top:10px"> <cfif ArrayLen(structResults.Errors)> <b>Errors:</b> <ol style="margin-top:0px">

<cfloop index="i" from="1" to="#ArrayLen(structResults.Errors)#"> <li>#structResults.errors[i]#</li> </cfloop> </ol> </cfif> <cfif ArrayLen(structResults.FatalErrors)> <b>Fatal Errors:</b> <ol style="margin-top:0px"> <cfloop index="i" from="1" to="#ArrayLen(structResults.FatalErrors)#"> <li>#structResults.fatalErrors[i]#</li> </cfloop> </ol> </cfif> <cfif ArrayLen(structResults.Warnings)> <b>Warnings:</b> <ol style="margin-top:0px"> <cfloop index="i" from="1" to="#ArrayLen(structResults.Warnings)#"> <li>#structResults.warnings[i]#</li> </cfloop> </ol> </cfif> </div> </cfif> </cfif> </form> </cfoutput> </body> </html> Code Explanation When the page is first opened, it looks like this: When the form is submitted with valid XML, the page displays as follows (notice the little message of success in the bottom right corner): When the form is submitted with invalid XML, the page displays as follows:

The XML has several problems:






The errors are problems with the validity of the XML document according to the schema. In this case, the title attribute should be spelled with a capital "T" as "Title" and no MiddleName element is allowed. The fatal errors are problems with the well-formedness of the XML document. In this case, the LastName element's close tag should have a capital "N". There are no warnings, which are generally difficult to produce in a validating parser.

IsXmlDoc(), IsXmlRoot(), IsXmlElem(), IsXmlAttribute(), IsXmlNode()










The IsXmlDoc() function returns true if the passed-in parameter is an XML document object. The IsXmlRoot() function returns true if the passed-in parameter is the root of an XML document object. The IsXmlElem() function returns true if the passed-in parameter is an XML element. The IsXmlAttribute() function returns true if the passed-in parameter is an XML attribute. The IsXmlNode() function returns true if the passed-in parameter is an XML node.

The following file shows how these functions work. Code Sample: XML/Demos/XmlParts.cfm <cfset xmlGeorge=XMLParse(ExpandPath("George.xml"))> <html> <head> <title>XML Parts</title> <style type="text/css"> .YES {background-color:#00cc00; font-weight:bold;} .NO {color:#ff0000;} </style> </head> <body> <h1>XML Parts</h1> <table border="1" cellpadding="2" cellspacing="0"> <tr> <th>#</th>

<th>XML Part</th> <th>IsXmlDoc()</th> <th>IsXmlRoot()</th> <th>IsXmlElem()</th> <th>IsXmlAttribute()</th> <th>IsXmlNode()</th> </tr> <cfoutput> <cfset part = xmlGeorge> <tr> <td>1</td> <td>xmlGeorge</td> <td class="#IsXmlDoc(part)#">#IsXmlDoc(part)#</td> <td class="#IsXmlRoot(part)#">#IsXmlRoot(part)#</td> <td class="#IsXmlElem(part)#">#IsXmlElem(part)#</td> <td class="#IsXmlAttribute(part)#">#IsXmlAttribute(part)#< /td> <td class="#IsXmlNode(part)#">#IsXmlNode(part)#</td> </tr> <cfset part = xmlGeorge.Name> <tr> <td>2</td> <td>xmlGeorge.Name</td> <td class="#IsXmlDoc(part)#">#IsXmlDoc(part)#</td> <td class="#IsXmlRoot(part)#">#IsXmlRoot(part)#</td> <td class="#IsXmlElem(part)#">#IsXmlElem(part)#</td> <td class="#IsXmlAttribute(part)#">#IsXmlAttribute(part)#< /td> <td class="#IsXmlNode(part)#">#IsXmlNode(part)#</td> </tr> <cfset part = xmlGeorge.Name.XmlAttributes.Title> <tr> <td>3</td> <td>xmlGeorge.Name.XmlAttributes.Title</td> <td class="#IsXmlDoc(part)#">#IsXmlDoc(part)#</td> <td class="#IsXmlRoot(part)#">#IsXmlRoot(part)#</td> <td class="#IsXmlElem(part)#">#IsXmlElem(part)#</td> <td class="#IsXmlAttribute(part)#">#IsXmlAttribute(part)#< /td>

<td class="#IsXmlNode(part)#">#IsXmlNode(part)#</td> </tr> <cfset part = xmlGeorge.Name.FirstName> <tr> <td>4</td> <td>xmlGeorge.Name.FirstName</td> <td class="#IsXmlDoc(part)#">#IsXmlDoc(part)#</td> <td class="#IsXmlRoot(part)#">#IsXmlRoot(part)#</td> <td class="#IsXmlElem(part)#">#IsXmlElem(part)#</td> <td class="#IsXmlAttribute(part)#">#IsXmlAttribute(part)#< /td> <td class="#IsXmlNode(part)#">#IsXmlNode(part)#</td> </tr> <cfset part = xmlGeorge.Name.XmlChildren[1]> <tr> <td>5</td> <td>xmlGeorge.Name.XmlChildren[1]</td> <td class="#IsXmlDoc(part)#">#IsXmlDoc(part)#</td> <td class="#IsXmlRoot(part)#">#IsXmlRoot(part)#</td> <td class="#IsXmlElem(part)#">#IsXmlElem(part)#</td> <td class="#IsXmlAttribute(part)#">#IsXmlAttribute(part)#< /td> <td class="#IsXmlNode(part)#">#IsXmlNode(part)#</td> </tr> <cfset part = xmlGeorge.Name.FirstName.XmlText> <tr> <td>6</td> <td>xmlGeorge.Name.FirstName.XmlText</td> <td class="#IsXmlDoc(part)#">#IsXmlDoc(part)#</td> <td class="#IsXmlRoot(part)#">#IsXmlRoot(part)#</td> <td class="#IsXmlElem(part)#">#IsXmlElem(part)#</td> <td class="#IsXmlAttribute(part)#">#IsXmlAttribute(part)#< /td> <td class="#IsXmlNode(part)#">#IsXmlNode(part)#</td> </tr> </table> </cfoutput> </body> </html>

Code Explanation The page will display as follows: This is pretty straightforward. The only surprises are the way attributes and text nodes are handled. According to row 3, the Title attribute is not an XML attribute, nor is it an XML node. And according to row 6, the text inside of the FirstName element is not an XML node. The Title attribute is, of course, an XML attribute, and all attributes and text in an XML document are XML nodes; however, ColdFusion treats attributes as properties of an element, not as nodes, and it treats text as plain text, rather than as an XML text node. XmlNodes Array Every element has an XmlNodes array, which contains the element's element nodes, text nodes, comment nodes, and CDATA section nodes. It does not contain an element's attribute nodes. To access attributes, you must use the XmlAttributes structure. XmlGetNodeType() The XmlGetNodeType() function is used to determine the passed-in parameter's node type. The following two examples show how the function works. Code Sample: XML/Demos/GetNodeType.cfm <cfset xmlGeorge=XMLParse(ExpandPath("George.xml"))> <html> <head> <title>XmlGetNodeType()</title> </head> <body> <h1>XmlGetNodeType()</h1> <cfoutput> <ol> <cfset part = xmlGeorge> <li> xmlGeorge: <cftry> #XmlGetNodeType(part)#

<cfcatch type="any"><span style="color:red">#cfcatch.Message#</span></cfcatch> </cftry> </li> <cfset part = xmlGeorge.Name> <li> xmlGeorge.Name: <cftry> #XmlGetNodeType(part)# <cfcatch type="any"><span style="color:red">#cfcatch.Message#</span></cfcatch> </cftry> </li> <cfset part = xmlGeorge.Name.XmlAttributes.Title> <li> xmlGeorge.Name.XmlAttributes.Title: <cftry> #XmlGetNodeType(part)# <cfcatch type="any"><span style="color:red">#cfcatch.Message#</span></cfcatch> </cftry> </li> <cfset part = xmlGeorge.Name.FirstName> <li> xmlGeorge.Name.FirstName: <cftry> #XmlGetNodeType(part)# <cfcatch type="any"><span style="color:red">#cfcatch.Message#</span></cfcatch> </cftry> </li> <cfset part = xmlGeorge.Name.XmlChildren[1]> <li> xmlGeorge.Name.XmlChildren[1]: <cftry> #XmlGetNodeType(part)# <cfcatch type="any"><span style="color:red">#cfcatch.Message#</span></cfcatch>

</cftry> </li> <cfset part = xmlGeorge.Name.FirstName.XmlText> <li> xmlGeorge.Name.FirstName.XmlText: <cftry> #XmlGetNodeType(part)# <cfcatch type="any"><span style="color:red">#cfcatch.Message#</span></cfcatch> </cftry> </li> </ol> </cfoutput> </body> </html> Code Explanation The page display is shown below: Code Sample: XML/Demos/XmlNodes.cfm <cfset xmlGeorge=XMLParse(ExpandPath("George2.xml"))> <cfset nameNodes = xmlGeorge.Name.XmlNodes> <html> <head> <title>XmlNodes()</title> </head> <body> <h1>XmlNodes()</h1> <ol> <cfoutput> <cfloop index="i" from="1" to="#ArrayLen(nameNodes)#"> <li>#XmlGetNodeType(nameNodes[i])# #IsXmlNode(nameNodes[i])#</li> </cfloop> </cfoutput> </ol> </body> </html> Code Explanation

The page display is shown below: You'll notice that it matters how the element is accessed. When text nodes are accessed through dot notation (e.g, xmlGeorge.Name.FirstName.XmlText), they are not treated as XML nodes. However, when they are accessed through the XmlNodes array, they are. The same is true for comment and CDATA section nodes. To be safe, you should always test a possible node with the IsXmlNode() function before treating it as an XML node. Processing RSS Feeds RSS stands for Really Simple Syndication. It is an XML format for making news items or other bits of content readily available. There are different versions of RSS, but all of them contain <item> elements for holding the different news items. These <item> elements at their simplest have the following structure: Syntax <item> <title>Item Title</title> <link>http://www.webucator.com/link/to/full/article</l ink> <description>Item description. Sometimes contains escaped HTML characters.</description> </item> There are other elements that the <item> element can contain, but for our purposes, we'll keep it simple (see footnote) and assume that an RSS document is made up of one or more <item> tags structured as shown above. We will ignore all other elements. There are several ways to process RSS feeds. Through code samples, we will see how to do it using XmlSearch() and XmlTransform() with XSLT. The output of the pages will look like this: Processing RSS Feeds with XmlSearch() Code Sample: XML/Demos/RssFeed.cfm <html> <head>

<title>RSS Feed</title> </head> <body> <cfparam name="FORM.rssURL" default="http://rss.news.yahoo.com/rss/topstories"> <cfoutput> <form method="post"> <input type="text" size="50" name="rssURL" value="#FORM.rssURL#"> <input type="submit" value="Get Feed"> </form> <cfset rss = XMLParse(FORM.rssURL)> <cfset items = XMLSearch(rss, "//*[localname()='item']")> <cfloop index="i" from="1" to="#ArrayLen(items)#"> <cfset title = XMLSearch(rss, "//*[localname()='item'][#i#]/*[local-name()='title']")> <cfset title = title[1].xmlText> <cfset description = XMLSearch(items[i], "//*[localname()='item'][#i#]/*[local-name()='description']")> <cfset description = description[1].xmlText> <cfset link = XMLSearch(items[i], "//*[localname()='item'][#i#]/*[local-name()='link']")> <cfset link = link[1].xmlText> <div style="width:600px"> <h2 style="clear:both;padding-top:10px; bordertop:1px solid blue;">#title#</h2> <div style="border:1px solid black; padding:4px; width:90%; margin:10px;">#description#</div> <div style="float:right; width:20px; marginbottom:10px; "> <a href="#link#">Read</a> </div> </div> </cfloop>

</cfoutput> </body> </html> Code Explanation This file parses an RSS feed from a URL and creates an XML document object: <cfset rss = XMLParse(FORM.rssURL)> It then creates an items array containing all nodes that have the local name "item". By using the local name, we avoid the headache of namespaces. You can read this XPath as "any element descending from the root whose local name is 'item'." <cfset items = XMLSearch(rss, "//*[localname()='item']")> It then loops through the items array grabbing the title, description and link nodes, assigning their XmlText to variables, and outputting the results in a div. <cfloop index="i" from="1" to="#ArrayLen(items)#"> <cfset title = XMLSearch(rss, "//*[localname()='item'][#i#]/*[local-name()='title']")> <cfset title = title[1].xmlText> <cfset description = XMLSearch(items[i], "//*[localname()='item'][#i#]/*[local-name()='description']")> <cfset description = description[1].xmlText> <cfset link = XMLSearch(items[i], "//*[localname()='item'][#i#]/*[local-name()='link']")> <cfset link = link[1].xmlText> <div style="width:600px"> <h2 style="clear:both;padding-top:10px; bordertop:1px solid blue;">#title#</h2> <div style="border:1px solid black; padding:4px; width:90%; margin:10px;">#description#</div> <div style="float:right; width:20px; marginbottom:10px; "> <a href="#link#">Read</a>

</div> </div> </cfloop> Processing RSS Feeds with XmlTransform() The XmlTransform() function can be used in conjunction with an XSLT to process RSS feeds. The file below shows a very basic XSLT that will transform an RSS feed to output HTML very much like we saw in the last example. Code Sample: XML/Demos/rss.xsl <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dc="http://purl.org/dc/elements/1.1/" version="1.0"> <xsl:output method="xml" indent="yes" omit-xmldeclaration="yes"/> <xsl:template match="/"> <xsl:apply-templates select="//*[localname()='item']"/> </xsl:template> <xsl:template match="//*[local-name()='item']"> <div style="width:600px"> <xsl:apply-templates select="*[local-name()='title' or local-name()='description']"/> </div> </xsl:template> <xsl:template match="*[local-name()='title']"> <h2 style="clear:both;padding-top:10px; bordertop:1px solid blue;"> <xsl:value-of select="."/> </h2> </xsl:template> <xsl:template match="*[local-name()='description']"> <div style="border:1px solid black; padding:4px; width:90%; margin:10px;"> <xsl:apply-templates /> </div> <div style="float:right; width:20px; marginbottom:10px; "> <a href="{parent::*[local-name()='link']}">Read</a>

</div> </xsl:template> </xsl:stylesheet> Code Sample: XML/Demos/RssFeed-xslt.cfm <html> <head> <title>RSS Feed</title> </head> <body> <cfparam name="FORM.rssURL" default="http://rss.news.yahoo.com/rss/topstories"> <cfoutput> <form method="post"> <input type="text" size="50" name="rssURL" value="#FORM.rssURL#"> <input type="submit" value="Get Feed"> </form> <cfset rss = XMLParse(FORM.rssURL)> <cfset result = XmlTransform(rss,ExpandPath("rss.xsl"))> <cfset resultFixed = Replace(result,"&gt;",">","ALL")> <cfset resultFixed = Replace(resultFixed,"&lt;","<","ALL")> #resultFixed# </cfoutput> </body> </html> Code Explanation Again we start by parsing an RSS feed from a URL to create an XML document object: <cfset rss = XMLParse(FORM.rssURL)> We then transform the document against our XSLT: <cfset result = XmlTransform(rss,ExpandPath("rss.xsl"))>

Because some RSS feeds include escaped HTML characters in the description, we use the Replace() function to unescape these characters. <cfset resultFixed = Replace(result,"&gt;",">","ALL")> <cfset resultFixed = Replace(resultFixed,"&lt;","<","ALL")> Finally, we output the result: #resultFixed# ColdFusion and WDDX WDDX stands for Web Distributed Data Exchange. It is an XML format for exchanging data between programming languages and across the web. Although it is not a standard format, there are WDDX implementations for JavaScript, PHP, Perl, ASP, and Java as well as ColdFusion. The following diagram shows how WDDX works. <cfwddx> The <cfwddx> tag is used to to convert ColdFusion structures to WDDX and WDDX to ColdFusion structures. It can also convert ColdFusion structures and WDDX strings to JavaScript objects. The tag's attributes are shown below: <cfwddx> Attributes Parameter Description Required. Possible values are: action
   

cfml2wddx: converts CFML to WDDX wddx2cfml: converts WDDX to CFML cfml2js: converts CFML to JavaScript wddx2js: converts WDDX to JavaScript

input output

Required. The value to convert. Required if action is "wddx2cfml". Variable name of output object. If action is not "wddx2cfml" and output is omitted,

<cfwddx> Attributes Parameter Description the result is output as text. Required when converting to JavaScript. Variable name of topLevelVariable JavaScript WddxRecordset object, which can be manipulated with functions made available by the wddx.js file. useTimeZoneInfo Optional. Default is "yes". Attempts to convert time between server and client. Optional. Default is "no". Applicable when converting from WDDX. If "yes", WDDX is validated before conversion and an error is thrown if it is invalid.

validate

The wddx.js File The wddx.js file, which is stored in CFIDE/scripts/wddx.js, makes it easy to convert JavaScript objects to WDDX by creating a new object of the WddxSerializer class and calling its serialize() method. (see footnote) Below we will build a simple application for creating and saving a list of resources to share among websites. 1. First, we will build the original list as a ColdFusion structure, which we will make available as WDDX for consumption. 2. Next, we will show how to consume the WDDX produced in step 1 with another ColdFusion page. 3. Then we will show how to consume the WDDX produced in step 1 with JavaScript and how to modify it, save the content in a form field, and submit it to a ColdFusion page for processing. 4. Then we will show how to save the modified WDDX content in a file. 5. Then we will show how to read the WDDX from the file and make it available for consumption. 6. Finally, we show how to consume that WDDX content with JavaScript. This is the same as step 3, except that it gets the WDDX from a different source.

Code Sample: XML/Demos/WddxLinks.cfm <cfcontent type="text/xml"> <cfset objWddx = StructNew()> <cfset objWddx.Links = ArrayNew(1)> <cfset i = ArrayLen(objWddx.Links)+1> <cfset objWddx.Links[i] = StructNew()> <cfset objWddx.Links[i].Title = "Webucator"> <cfset objWddx.Links[i].URL = "http://www.webucator.com"> <cfset objWddx.Links[i].Description = "Webucator provides customized onsite technical training."> <cfset objWddx.Links[i].Categories = ArrayNew(1)> <cfset ArrayAppend(objWddx.Links[i].Categories,"Training")> <cfset ArrayAppend(objWddx.Links[i].Categories,"IT")> <cfset ArrayAppend(objWddx.Links[i].Categories,"Computers")> <cfset i = ArrayLen(objWddx.Links)+1> <cfset objWddx.Links[i] = StructNew()> <cfset objWddx.Links[i].Title = "Adobe"> <cfset objWddx.Links[i].URL = "http://www.adobe.com"> <cfset objWddx.Links[i].Description = "Adobe develops computer software products and technologies that enable users to express and use information across all print and electronic media."> <cfset objWddx.Links[i].Categories = ArrayNew(1)> <cfset ArrayAppend(objWddx.Links[i].Categories,"Software")> <cfset ArrayAppend(objWddx.Links[i].Categories,"IT")> <cfset ArrayAppend(objWddx.Links[i].Categories,"Computers")> <cfset i = ArrayLen(objWddx.Links)+1> <cfset objWddx.Links[i] = StructNew()> <cfset objWddx.Links[i].Title = "Microsoft"> <cfset objWddx.Links[i].URL = "http://www.microsoft.com"> <cfset objWddx.Links[i].Description = "Microsoft offers a wide range of software, services, and

Internet technologies for personal and business computing."> <cfset objWddx.Links[i].Categories = ArrayNew(1)> <cfset ArrayAppend(objWddx.Links[i].Categories,"Software")> <cfset ArrayAppend(objWddx.Links[i].Categories,"IT")> <cfset ArrayAppend(objWddx.Links[i].Categories,"Computers")> <cfset i = ArrayLen(objWddx.Links)+1> <cfset objWddx.Links[i] = StructNew()> <cfset objWddx.Links[i].Title = "Google"> <cfset objWddx.Links[i].URL = "http://www.google.com"> <cfset objWddx.Links[i].Description = "Google is the leading search engine."> <cfset objWddx.Links[i].Categories = ArrayNew(1)> <cfset ArrayAppend(objWddx.Links[i].Categories,"Search")> <cfset ArrayAppend(objWddx.Links[i].Categories,"IT")> <cfwddx action="cfml2wddx" input="#objWddx#"> Code Explanation This file simply creates a ColdFusion structure holding the following information about web resources:
   

Link Title (string) Link URL (string) Link Description (string) Link Categories (array)

It then converts the structure to WDDX and outputs it to the browser using the <cfwddx> tag. Code Sample: XML/Demos/WddxConsume.cfm <cfset thisPage = "#CGI.SERVER_NAME##CGI.SCRIPT_NAME#"> <cfset arrFileParts = ListToArray(CGI.SCRIPT_NAME,"/")> <cfset ArrayDeleteAt(arrFileParts,ArrayLen(arrFileParts))> <cfset path = ArrayToList(arrFileParts,"/")>

<cfset pathToWddx = "http://#CGI.SERVER_NAME#:#CGI.SERVER_PORT#/#path#/Wdd xLinks.cfm"> <cfhttp method="get" url="#pathToWddx#"> <cfwddx action="wddx2cfml" input="#cfhttp.FileContent#" output="objWddx" validate="yes"> <html> <head> <title>Links Page</title> </head> <body> <cfset arrLinks = objWddx.links> <cfset linkCat = "Software"> <ul> <cfoutput> <cfloop from="1" to="#ArrayLen(arrLinks)#" index="i"> <cfset strCats = ArrayToList(arrLinks[i].Categories)> <cfif ListFind(strCats,linkCat)> <li><a href="#arrLinks[i].URL#">#arrLinks[i].Title#</a> #arrLinks[i].Description#</li> </cfif> </cfloop> </cfoutput> </ul> </body> </html> Code Explanation This file uses the <cfhttp> tag to read in the WddxConsume.cfm file. It then uses the <cfwddx> tag to convert the file's content to a ColdFusion structure and loops through the output producing links that are included in the "Software" category. Code Sample: XML/Demos/WddxConsume-js.cfm <cfset thisPage = "#CGI.SERVER_NAME##CGI.SCRIPT_NAME#"> <cfset arrFileParts = ListToArray(CGI.SCRIPT_NAME,"/")>

<cfset ArrayDeleteAt(arrFileParts,ArrayLen(arrFileParts))> <cfset path = ArrayToList(arrFileParts,"/")> <cfset pathToWddx = "http://#CGI.SERVER_NAME#:#CGI.SERVER_PORT#/#path#/Wdd xLinks.cfm"> <cfhttp method="get" url="#pathToWddx#"> <html> <head> <title>Links Page</title> <script language="javascript" src="wddx.js"></script> <script language="javascript"> <cfwddx action="wddx2js" input="#cfhttp.FileContent#" toplevelvariable="objLinks"> var serializer = new WddxSerializer(); function addLink(FORM) { var arrLen = objLinks.links.length; objLinks.links[arrLen] = new Object(); objLinks.links[arrLen]["title"] = FORM.title.value; objLinks.links[arrLen]["description"] = FORM.description.value; objLinks.links[arrLen]["url"] = FORM.url.value; objLinks.links[arrLen]["categories"] = FORM.categories.value.split(","); document.forms[0].strWddx.value=serializer.serialize(o bjLinks); addListItem(objLinks.links[arrLen]["title"]); } function addListItem(TEXT) { var li = document.createElement("li"); var liText = document.createTextNode(TEXT); list.appendChild(li); li.appendChild(liText); } var list;

window.onload = function() { document.forms[0].title.focus(); document.forms[0].strWddx.value=serializer.serialize(o bjLinks); list=document.getElementById("listLinks"); for (i in objLinks.links) { addListItem(objLinks.links[i].title); } } </script> </head> <body> <form method="post" action="SaveLinks.cfm"> <input type="hidden" name="strWddx" value=""> <table> <tr> <td>Title:</td> <td><input type="text" size="50" name="title"></td> </tr> <tr> <td>URL:</td> <td><input type="text" size="50" name="url"></td> </tr> <tr valign="top"> <td>Description:</td> <td><textarea name="description" cols="50" rows="3"></textarea></td> </tr> <tr valign="top"> <td>Categories:</td> <td> Must be comma-delimited<br> <textarea name="categories" cols="50" rows="2"></textarea> </td> </tr> <tr> <td colspan="2" align="right"> <input type="button" value="Add Link" onClick="addLink(this.form)">

<input type="submit" value="Save Links"> </td> </tr> </table> </form> <h1>Links</h1> <ul id="listLinks"></ul> </body> </html> Code Explanation This file also uses the <cfhttp> tag to read in the WddxConsume.cfm file. Inside HTML <script> tags, it uses the <cfwddx> tag to create the JavaScript necessary to represent the WDDX as a JavaScript object. When the page finishes loading, JavaScript is used to create list items showing the titles of the different links. An HTML form is provided for adding new links. When the "Add Link" button is clicked, a new link is added to the JavaScript object and the WDDX is stored in the hidden field, strWddx. When the "Save Links" submit button is pressed, the form is submitted to the SaveLinks.cfm page. Code Sample: XML/Demos/SaveLinks.cfm <html> <head> <title>WDDX: Save Links</title> </head> <body> <cftry> <cffile action="write" output="#FORM.strWddx#" file="#ExpandPath('links.xml')#" nameconflict="overwrite"> Links saved. <cfcatch type="any"> Could not save links: <cfoutput>#cfcatch.Message#</cfoutput> </cfcatch> </cftry> </body> </html> Code Explanation This file simply uses <cffile> to save the WDDX string to a file (links.xml).

Code Sample: XML/Demos/WddxLinksFromFile.cfm <cftry> <cfcontent type="text/xml"> <cffile action="read" file="#ExpandPath('links.xml')#" variable="strWddx"> <cfoutput>#strWddx#</cfoutput> <cfcatch type="any"> <cfcontent type="text/html"> <cfoutput>#cfcatch.Message#</cfoutput> </cfcatch> </cftry> Code Explanation This file uses <cffile> to read in links.xml and outputs the result. Code Sample: XML/Demos/WddxConsume-js2.cfm <cfset thisPage = "#CGI.SERVER_NAME##CGI.SCRIPT_NAME#"> <cfset arrFileParts = ListToArray(CGI.SCRIPT_NAME,"/")> <cfset ArrayDeleteAt(arrFileParts,ArrayLen(arrFileParts))> <cfset path = ArrayToList(arrFileParts,"/")> <cfset pathToWddx = "http://#CGI.SERVER_NAME#:#CGI.SERVER_PORT#/#path#/Wdd xLinksFromFile.cfm"> <cfhttp method="get" url="#pathToWddx#"> ---- Code Omitted ---Code Explanation This file is almost the same as WddxConsume-js.cfm. It just reads from WddxLinksFromFile.cfm rather than WddxLinks.cfm. WDDX vs. Web Services Like WDDX, web services provide a mechanism for different applications to share data. Web services, however, do much more than that. They provide a mechanism for sharing objects, making their methods invokable over distributed systems. The scope of WDDX is much smaller. WDDX simply

provides a way of sharing data between program languages. It provides no built-in way for one system to invoke processes on another system. ColdFusion and XML Conclusion In this lesson of the ColdFusion tutorial, you have learned to use ColdFusion's XML functions, to process RSS feeds, and to use WDDX to transfer data across the web. Footnotes 1. If you need to turn an XML document back into a string, use the ToString() function. 2. It also takes an optional namespace argument as the second parameter. 3. This XML is poorly formed because it has more than one document element. 4. For a good tutorial on RSS, see http://www.w3schools.com/rss. 5. The wddx.js file also provides functionality for creating JavaScript objects that act like ColdFusion query objects.


				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:2365
posted:9/27/2009
language:English
pages:222