Using Cookies to Remember Preferences


Cookies are simple variables that can be stored on a client machine. Basically, the server asks the browser to remember a variable with such-and-such a name and such-and-such a value. The browser returns the variable to the server as it requests successive pages from that same server. In other words, after the server sets the value on the browser, the browser continues to remind the server about it as the user moves from page to page. The net effect is that each site essentially has a small portion of the browser's memory in which to store little bits of information.

NOTE

Cookies first appeared in early versions of Netscape Navigator and have since been adopted by nearly all browser software. As of this writing, the original specification document for cookies is still available at http://www.netscape.com/newsref/std/cookie_spec.html. It is interesting to read, if only because it underscores how important Netscape's early innovations have become to today's Web. No substantive changes have been made to the cookies since.


Introducing the COOKIE Scope

Cookies aren't something specific to ColdFusion. Any server-side scripting programming environment can set them, and they can even be set by client-side languages such as JavaScript. Depending on the language, the actual code necessary to set or retrieve a cookie varies a bit, of course. The best implementations keep coders from having to understand the details of the actual communication between the browser and server. It's best if the coder can just concentrate on the task at hand.

In ColdFusion, the notion of cookies is exposed to you via the simple, elegant COOKIE scope. Similar to the APPLICATION scope you learned about in the previous chapter, the COOKIE scope is automatically maintained by ColdFusion. Setting a variable within the COOKIE scope instructs the browser to remember the cookie. Referring to a variable within the COOKIE scope returns the value of the cookie on the browser's machine.

For instance, the following line asks the user's browser to remember a cookie variable called MyMessage. The value of the cookie is "Hello, World!":

 <cfset COOKIE.myMessage = "Hello, World!"> 

From that point on, you could output the value of #COOKIE.myMessage# in your CFML code, between <cfoutput> tags. The "Hello, World" message would be output in place of the variable.

A Simple Cookie Exercise

This simple exercise will illustrate what happens when you use cookies. First, temporarily change your browser's preferences so that you will receive notice whenever a cookie is being set.

To be notified when a cookie is set on your browser, follow these guidelines:

  • If you are using Mozilla, select Edit; then Preferences. Select the Privacy and Security node and then the Cookies item. Under Cookie Lifetime Policy, select Ask for each cookie.

  • If you are using Internet Explorer (version 5 or later), select Internet Options from the Tools menu, and then select the Security tab. Make sure the appropriate zone is selected; then select Custom Level and check the Prompt options for both Allow Cookies That Are Stored on Your Computer and Allow Per-Session Cookies.

  • If you are using some other browser or version, the steps you take might be slightly different, but you should have a way to turn on some type of notification when cookies are set.

Now use your browser to visit the CookieSet.cfm template shown in Listing 20.1. You should see a prompt similar to the one shown in Figure 20.1. The prompt might look different depending on browser and version, but it generally will show you the name and value of the cookie being set. (Note that you can even refuse to allow the cookie to be set.) Go ahead and let the browser store the cookie by clicking OK.

Figure 20.1. Click OK to let the browser store the cookie.


If you now visit the CookieShow.cfm template shown in Listing 20.2, you will see the message you started your visit at:, followed by the exact time you visited the code in Listing 20.1. Click your browser's Reload button a few times, so you can see that the value doesn't change. The value persists between page requests. If you go back to Listing 20.1, the cookie will be reset to a new value.

Close your browser, reopen it, and visit the CookieShow.cfm template again. You will see an error message from ColdFusion, telling you that the COOKIE.TimeVisitStart variable doesn't exist. By default, cookies expire when the browser is closed. Therefore, the variable is no longer passed to the server with each page request and is unknown to ColdFusion.

Listing 20.1. CookieSet.cfmSetting a Cookie
 <!---  Filename: CookieSet.cfm  Created by: Nate Weiss (NMW)  Purpose: Sets a cookie to remember time of this page request ---> <html> <head><title>Cookie Demonstration</title></head> <body> <!--- Set a cookie to remember the time right now ---> <cfset COOKIE.TimeVisitStart = timeFormat(now(), "h:mm:ss tt")> The cookie has been set. </body> </html> 

Listing 20.2. CookieShow.cfmDisplaying a Cookie's Value
 <!---  Filename: CookieShow.cfm  Created by: Nate Weiss (NMW)  Please Note Displays the value of the TimeVisitStart cookie,  which gets set by CookieSet.cfm ---> <html> <head><title>Cookie Demonstration</title></head> <body> <cfoutput>  You started your visit at:  #COOKIE.TimeVisitStart#<br> </cfoutput> </body> </html> 

Using Cookies

You can easily build on the last example to make it a more useful in the real world. For instance, you wouldn't want the Time Started value to be reset every time the user visited the first page; you probably want the value to be recorded only the first time. So it would make sense to first test for the cookie's existence and only set the cookie if it doesn't already exist. It would also make sense to remember the full date/time value of the user's first visit, rather than just the time.

So, instead of

 <cfset COOKIE.TimeVisitStart = timeFormat(now(), "h:mm:ss tt")> 

you could use

 <cfif not isDefined("COOKIE.VisitStart")>  <cfset COOKIE.VisitStart = now()> </cfif> 

In fact, the isDefined test and the <cfset> tag can be replaced with a single <cfparam> tag:

 <cfparam name="COOKIE.VisitStart" type="date" default="#now()#"> 

This <cfparam> tag can be placed in your Application.cfc file so it is encountered before each page request is processed. You can now be assured that ColdFusion will set the cookie the first time the user hits your application, no matter what page they start on, and that you will never get a parameter doesn't exist error message, because the cookie is guaranteed to always be defined. As discussed previously, the cookie will be reset if the user closes and reopens her browser.

If you need a quick reminder on the difference between <cfset> and <cfparam>, see Chapter 8, "Using ColdFusion," and Chapter 9, "CFML Basics."


You could then output the time elapsed in your application by outputting the difference between the cookie's value and the current time. You could put this code wherever you wanted in your application, perhaps as part of some type of header or footer message. For instance, the following code would display the number of minutes that the user has been using the application:

 <cfoutput>  Minutes Elapsed: #dateDiff("n", COOKIE.VisitStart, now())# </cfoutput> 

The next two listings bring these lines together. Listing 20.3 is an Application.cfc file that includes the <cfparam> tag shown previously. Listing 20.4 is a file called ShowTimeElapsed.cfm, which can be used to display the elapsed time in any of the current application's pages by using <cfinclude>. You also can visit Listing 20.4 on its ownFigure 20.2 shows what the results would look like.

Figure 20.2. Cookies can be used to track users, preferences, or, in this case, elapsed times.


Be sure to save Listing 20.3 as Application.cfc, not Application1.cfc.

Listing 20.3. Application1.cfcDefining a Cookie Variable in Application.cfc
 <!---  Filename: Application.cfc  Created by: Raymond Camden (ray@camdenfamily.com)  Handles application events. ---> <cfcomponent output="false">   <cffunction name="onRequestStart" output="false" returnType="void">     <cfparam name="COOKIE.VisitStart" type="date" default="#now()#">   </cffunction> </cfcomponent> 

Listing 20.4. ShowTimeElapsed.cfmPerforming Calculations Based on Cookies
 <!---  Filename: ShowTimeElapsed.cfm  Created by: Nate Weiss (NMW)  Please Note Can be <CFINCLUDED> in any page in your application ---> <!--- Find number of seconds passed since visit started ---> <!--- (difference between cookie value and current time) ---> <cfset secsSinceStart = dateDiff("s", COOKIE.VisitStart, now())> <!--- Break it down into numbers of minutes and seconds ---> <cfset minutesElapsed = int(secsSinceStart / 60)> <cfset secondsElapsed = secsSinceStart MOD 60> <!--- Display the minutes/seconds elapsed ---> <cfoutput>  Minutes Elapsed:  #minutesElapsed#:#numberFormat(secondsElapsed, "00")# </cfoutput> 

NOTE

What is the meaning of output="false" and returnType="void" in the methods in listing 20.3? These are optional arguments that help define how CFC methods run. By using output=false, we limit the white space generated by the methods. Using returnType=void simply means that the method doesn't return any data. Again, these are optional attributes, but it's good practice to use them.


Because COOKIE.VisitStart is always a ColdFusion date/time value, getting the raw number of seconds since the visit started is easyyou use the dateDiff function. If the difference in seconds between the cookie value and the present moment (the value returned by the now function) is 206, you know that 206 seconds have passed since the cookie was set.

Because most people are more comfortable seeing time expressed in minutes and seconds, Listing 20.4 does some simple math on the raw number of seconds elapsed. First, it calculates the number of whole minutes that have elapsed, by dividing SecsSinceStart by 60 and rounding down to the nearest integer. Next, it calculates the number of seconds to display after the number of minutes by finding the modulus (which is the remainder left when SecsSinceStart is divided by 60).

See Appendix C, "ColdFusion Function Reference," for explanations of the DateDiff, Int, and Now functions.


Gaining More Control with <cfcookie>

You already have learned how to set cookies using the <cfset> tag and the special COOKIE scope (Listings 20.120.3). Using that technique, setting cookies is as simple as setting normal variables. However, sometimes you will want more control over how cookies get set.

Introducing the <cfcookie> Tag

To provide you with that additional control, ColdFusion provides the <cfcookie> tag, which is an alternative syntax for setting cookie variables. Once set, you can access or display the cookies as you have learned so far, by referring to them in the special COOKIE scope.

Table 20.2 introduces the attributes available when using <cfcookie>.

Table 20.2. <cfcookie> Tag Syntax

ATTRIBUTE

PURPOSE

NAME

Required. The name of the cookie variable. If you use NAME="VisitStart", the cookie will thereafter become known as COOKIE.VisitStart.

VALUE

Optional. The value of the cookie. To set the cookie's value to the current date and time, use VALUE="#Now()#".

EXPIRES

Optional. When the cookie should expire. You can provide any of the following:

A specific expiration date (such as 3/18/2002) or a date/time value.

The number of days you want the cookie to exist before expiring, such as 10 or 90.

The word NEVER, which is a shortcut for setting the expiration date far into the future, so it effectively never expires.

The word NOW, which is a shortcut for setting the expiration date in the recent past, so it is already considered expired. This is how you delete a cookie.

If you don't specify an EXPIRES attribute, the cookie will do what it does normally, which is to expire when the user closes the browser. See "Controlling Cookie Expiration," later in this chapter.

DOMAIN

Optional. You can use this attribute to share the cookie with other servers within your own Internet domain. By default, the cookie is visible only to the server that set it. See "Controlling How Cookies Are Shared," later in this chapter.

PATH

Optional. You can use this attribute to specify which pages on your server should be able to use this cookie. By default, the cookie can be accessed by all pages on the server once set. See "Controlling How Cookies Are Shared," later in this chapter.

SECURE

Optional. You can use this attribute to specify whether the cookie should be sent back to the server if a secure connection is being used. The default is No. See "Controlling How Cookies Are Shared," later in this chapter.


Controlling Cookie Expiration

The most common reason for using <cfcookie> instead of a simple <cfset> is to control how long the cookie will exist before it expires. For instance, looking back at the Application.cfc file shown in Listing 20.3, what if you didn't want the Elapsed Time counter to start over each time the user closed her browser?

Say you wanted the elapsed time to keep counting for up to a week. You would replace the <cfparam> line in Listing 20.3 with the following:

 <!--- If no "VisitStart" cookie exists, create it ---> <cfif not isDefined("COOKIE.VisitStart")>  <cfcookie  name="VisitStart"  value="#now()#"  expires="7"> </cfif> 

Controlling How Cookies Are Shared

Netscape's original cookie specification defines three additional concepts that haven't been discussed yet. All three have to do with giving you more granular control over which pages your cookies are visible to:

  • A domain can be specified as each cookie is set. The basic idea is that a cookie should always be visible only to the server that set the cookie originally. This is to protect users' privacy. However, if a company is running several Web servers, it is considered fair that a cookie set on one server be visible to the others. Specifying a domain for a cookie makes it visible to all servers within that domain. An example of this could be a server named www.foo.com that wants to share a cookie with the server store.foo.com, which is in the same domain.

  • A path can be specified as each cookie is set. This enables you to control whether the cookie should be visible to the entire Web server (or Web servers), or just part. For instance, if a cookie will be used only by the pages within the ows folder in the Web server's root, it might make sense for the browser to not return the cookie to any other pages, even those on the same server. The path could be set to /ows, which would ensure that the cookie is visible only to the pages within the ows folder. This way, two applications on the same server can each set cookies with the same name without overwriting one another, as long as the applications use different paths when setting the cookies.

  • A cookie can be marked as secure. This means that it should be returned to the server only when a secure connection is being used (that is, if the page's URL starts with https:// instead of http://). If the browser is asked to visit an ordinary (nonsecure) page on the server, the cookie isn't sent and thus isn't visible to the server. This doesn't mean that the cookie will be stored on the user's computer in a more secure fashion; it just means that it won't be transmitted back to the server unless SSL encryption is being used.

As a ColdFusion developer, you have access to these three concepts by way of the domain, path, and secure attributes of the <cfcookie> tag. As Table 20.2 showed, all three attributes are optional.

Let's say you have three servers, named one.orangewhip.com, two.orangewhip.com, and three.orangewhip.com. To set a cookie that would be shared among the three servers, take the portion of the domain names they share, including the first dot. The following code would set a cookie visible to all three servers (and any other servers whose host names end in .orangewhip.com):

  <!--- Share cookie over our whole domain --->  <cfcookie  name="VisitStart"  value="#now()#"  domain=".orangewhip.com"> 

The next example uses the path attribute to share the cookie among all pages that have a /ows at the beginning of the path portion of their URLs (the part after the host name). For instance, the following would set a cookie that would be visible to a page with a path of /ows/Home.cfm and /ows/store/checkout.cfm, but not /owintra/login.cfm:

 <!--- Only share cookie within ows folder ---> <cfcookie name="VisitStart" value="#now()#" path="/ows"> 

Finally, this last example uses the secure attribute to tell the browser to make the cookie visible only to pages that are at secure (https://) URLs. In addition, the cookie will expire in 30 days and will be shared among the servers in the orangewhip.com domain, but only within the /ows portion of each server:

 <!--- This cookie is shared but confidential ---> <cfcookie name="VisitStart" value="#Now()#" expires="30" domain=".orangewhip.com" path="/ows" secure="Yes"> 

NOTE

You can specify that you want to share cookies only within a particular subdomain. For instance, domain=".intranet.orangewhip.com" shares the cookie within all servers that have .intranet.orangewhip.com at the end of their host names. However, there must always be a leading dot at the beginning of the domain attribute.

You can't share cookies based on IP addresses. To share cookies between servers, the servers must have Internet domain names.

The domain attribute is commonly misunderstood. Sometimes people assume that you can use it to specify other domains to share the cookies with. But domain can be used only to specify whether to share the cookies with other servers in the same domain.


Sharing Cookies with Other Applications

Because cookies aren't a ColdFusion-specific feature, cookies set with, say, Active Server Pages are visible in ColdFusion's COOKIE scope, and cookies set with <cfcookie> are visible to other applications, such as PHP, Perl, or JavaServer Pages. The browser doesn't know which language is powering which pages. All it cares about is whether the requirements for the domain, path, security, and expiration have been met. If so, it makes the cookie available to the server.

TIP

If you find that cookies set in another language aren't visible to ColdFusion, the problem might be the path part of the cookie. For instance, whereas ColdFusion sets the path to / by default so that the cookie is visible to all pages on the server, JavaScript sets the path to match that of the current page by default. Try setting the path part of the cookie to / so that it will behave more like one set with ColdFusion. The syntax to do this varies from language to language.


Cookie Limitation

There are some pretty serious restrictions on what you can store in cookies, mostly established by the original specification:

  • Only simple strings can be stored. Because dates and numbers can be expressed as strings, you can store them as cookies. But no ColdFusion-specific data types, such as arrays and structures, can be specified as the value for a cookie.

  • A maximum of 20 cookies can be set within any one domain. This prevents cookies from eventually taking up a lot of hard drive space. Browsers might or might not choose to enforce this limit.

  • A name can be only 4 Kbytes long. The name of the cookie is considered part of its length.

  • The browser isn't obligated to store more than 300 cookies. (That is 300 total, counting all cookies set by all the world's servers.) The browser can delete the least recently used cookie when the 300-cookie limit has been reached. That said, many modern browsers choose not to enforce this limit.



Macromedia Coldfusion MX 7 Web Application Construction Kit
Macromedia Coldfusion MX 7 Web Application Construction Kit
ISBN: 321223675
EAN: N/A
Year: 2006
Pages: 282

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net