You need to enable session or client management, then check for the existence of the session or client variable. Actually, I believe client management needs to be on anyway to set cfid and cftoken.
<cfif not IsDefined("client.RollCount")> <cfset client.rollcount = 1> <cfcookie name="mytestcookie" value="On" expires="1"> <cfset variables.ID = "cfid=" & client.cfid & "&cftoken=" & client.cftoken> <cfelse> <cfif IsDefined('cookie.MyTestCookie')> <cfset variables.ID = ""> <cfelse> <cfset variables.ID = "cfid=" & client.cfid & "&cftoken=" & client.cftoken> <cfif client.RollCount IS 1> <cfset client.RollCount = 2> <cfset variables.NoCookies = 1> </cfif> </cfif> </cfif>
You will need to add ?#variables.id# to the end of your links. Upon entering the site the program will try to set a cookie, set client.rollcount (or session.rollcount if you'd rather use that) to 1 and the cfid and cftoken will be appended to any links. Upon the first click, client.rollcount will be 1 so now the program will test to see if cookie.mytestcookie exists. If it does, we can pass the cfid and cftoken the rest of the way to keep the session alive and cookies aren't needed. We can also now look for the existence of variables.nocookies and give the user a message like "You have cookies disabled. While this will work fine, if you leave our site for any reason, the items in your shopping cart will be removed". Then we set client.rollcount to 2 so variables.nocookies isn't set on subsequent clicks and they only get that message once.
In each cookie you can store 4 Kb of data. Enough for most cookie uses, but not enough to store the contents of a shopping cart or something.
You can encrypt the contents of a cookie with the Encrypt() function.
Just remember you CAN'T check for the existence of a cookie on a page below where you've set it. Even if cookies are turned off, once you set it it will exist for that instant as far as ColdFusion is concerned. Just like a local variable except it won't be stored on the users computer.
Session variables are stored in a structure, so you can use StructDelete
<cfset variables.Result = StructDelete(session, "YourSessionVarName")>
Before we talk about client and session variables, we need to briefly discuss the HTTP protocol and cookies. That will help clarify how ColdFusion tracks users.
The HTTP protocol consists of a request-response pair. When a browser requests a webpage from a server, the server's response is to find the webpage on its hard drive and then send the file back to the browser. When the browser parses (reads) the webpage and finds there are references to 5 images, it then makes 5 subsequent requests to the server, one for each image. Those 5 requests are then fulfilled by the server finding the images on its hard drive and sending them back to the browser. The browser then uses the 6 files to display the webpage on the monitor.
All of this happens very quickly and helps to hide the fact that displaying one webpage can be the result of a large number of HTTP request-response pairs.
Each request that a browser makes to a server is said to be "stateless". That is to say, the server doesn't track a series of requests from a particular browser. Each request is independant of every other request. As long as the server has the IP address of the computer making each individual request, it has sufficient information to send the necessary file (resource) on its way.
Certain websites, however, have a requirement to track users - sites with shopping baskets, for example. If the information about the products in a user's basket is stored on the server, that server needs to somehow track users to ensure that baskets don't get lost or mixed up.
How, then, do we address the fundemental statelessness of the HTTP protocol? The answer is with cookies and a pair of values called cfid and cftoken.
A cookie is the name given to a simple string of text. Firstly, let's clarify how cookies work.
Setting a cookie in a browser works as follows. The browser requests a file from a server. The server's response is to send back the file, along with a command to "set" a cookie in the browser. If the browser is configured to accept cookies, the cookie is said to be "set".
Every time the browser makes a subsequent request from the same server, the cookie is returned to the server along with each request.
For example, if we set a cookie in a browser that had a value NAME equal to FRED, every time the browser asked for another file from the same server, along with the request would come the information that name=FRED.
If there were several browsers all making requests from one server, so long as they are all returning different NAMEs, we could use the cookie's NAME value to identify which browser each request had come from.
ColdFusion utilises this behaviour to first differentiate and then track users. However, instead of using a cookie with a value called NAME, it uses two values called cfid and cftoken.
The first time a browser makes a request for a page from a ColdFusion server, ColdFusion creates a new cfid-cftoken pair. cfid is an incremental number and cftoken is a random 8 digit number. If the last cfid-cftoken pair created was 100-12345678, the next pair might be 101-87654321. Taken together, each cfid-cftoken pair is unique.
When ColdFusion sends the first page back to the browser, it also attempts to set a cookie that contains our newly created cfid-cftoken values. If the cookie is successfully set, every subsequent request from that browser will be accompanied by the cookie containing cfid=101*cftoken=87654321.
As long as ColdFusion then receives the cfid and cftoken values along with every request (which is how cookies are designed to operate), it can differentiate requests from different browsers.
Client variables are values stored on the server and associated with a particular browser. They are typically stored in the server's registry (a part of Windows where values are stored). Client variables are designed to track users over a long period of time.
If a user visited a site and was asked to enter their name in an online form, and this code was on the following page
<cfset client.Name = form.Name>
whatever they typed in the Name form field would then be stored in the registry as a client variable called "Name". Along with Name would be the cfid-cftoken pair of the browser with which they are associated.
The user then left our site and didn't return for several weeks. When they did return, the first time they request a page from the server, the cfid-cftoken pair were also sent. ColdFusion sees that along with the request for a page is a value for cfid and cftoken. If then looks to see if the cfid-cftoken pair it has been sent with the request match any of the pairs stored in the registry.
It does! And along with the cfid-cftoken value in the registry is another value called Name which was set during the last visit. So, if ColdFusion is asked to display client.Name on the page , it can retreive the Name value for that client from the registry and output it.
When a visitor has left our site, we have no way of knowing if they will ever return. As our site receieves more visitors over time, the number of client variables stored on the server will increase. To prevent the registry from overflowing, we need to have some mechanism for deleting unused variables.
In ColdFusion, we determine a length of time after which we consider it safe to delete a user's variables. ColdFusion keeps a record of the last time a user visited a site and client variables are typically kept for 90 days. ColdFusion regularly goes through the registry and looks for client variables that belong to a user that hasn't visited in the last 90 days and deletes them.
In this way, we don't tell ColdFusion when to delete a user's client variables. We determine a length of inactivity after which it can be considered safe to delete their client variables.
If Fred visited our site and his name was set as a client variable, he may return to our site several times over the following month. On each subsequent visit, his client variable Name would still exist in the registry, tied to his cfid-cftoken pair.
Our ColdFusion server is told to delete client variables for users that haven't accessed the site in the last 3 months (90 days). If Fred visited our site 91 days after his last visit, his client variables would have been deleted and for Fred, client.Name would no long exist on that server.
Session variables work in much the same way. However, instead of being stored in the server's registry, they are held in the server's memory. They are designed for holding transient values, only lasting for the length of one visit (one session) to our site.
As they are intended to be used for holding values that are impermanent and that can be safely discarded after a visit, they are ideally suited to storing the contents of a shopping basket, for example.
As memory is precious on our server, session variables are typically deleted after only 20 minutes of inactivity.
Knowing what fickle things customers are, we are going to ask them to enter their current favourite colour using another online form. On the subsequent page, we have this code
<cfset session.Colour = form.Colour>
Whatever they typed in to the form field named Colour will now be held in the server's memory as a variable called Colour, together with that user's cfid-cftoken pair.
On another page on the site, we display their favourite colour using the code
<cfoutput> <p>Today, your favourite colour is #session.Colour#</p> </cfoutput>
When the browser requests this page, it also sends the cfid-cftoken pair previously set as a cookie. ColdFusion looks to see if there is a match in memory for the cfid-cftoken value is has been sent. Again, there is, and along with those values is a variable called Colour, set to Red for this particular user. The page will then tell the customer that their favourite colour is Red.
Our user's phone then rings and whilst they are distracted for a while, their browser doesn't request any pages from our site. After 20 minutes of inactivity, the server then deletes any session variables tied to that user. If they put the phone down after 21 minutes and refresh the page, session.Colour wouldn't exist on the server. We would have to code the page so that they didn't see an error message telling them that session.Colour was not defined.
Session and client variables help the developer by removing the burden of having to track users. ColdFusion has a mechanism that does this automatically.
There are, as you might expect, a few caveats.
To use client and session variables, you must have an Application.cfm file, usually placed in the root folder of your website. In it, typically on one of the first few lines, you must give your application a name, using the cfapplication tag.
You must then enable client and session variables, optionally telling ColdFusion their life-span. It is also possible to store client variables in locations other than the registry. You can alternatively set them as cookies in the users's browser or store them in a database on the server.
<cfapplication name="EasyStore" clientmanagement="yes" setclientcookies="yes" clientstorage="registry" sessionmanagement="yes" sessiontimeout="#CreateTimeSpan(0,1,0,0)#">
We've specified an application name. We've also explicitly told it to enable client management (ie to store client variables on the server), to try and set the cfid-cftoken values as a cookie in the browser and that client variables should be stored in the registry.
We also explicitly enabled session management (ie to store session variables in memory on the server) and that we want ColdFusion to delete session variables tied to clients after they haven't requested a page from our site for 1 hour.
If our site is visited by a browser that has been configured to not accept cookies, when ColdFusion tries to set a cookie when returning the first page, it will fail. If we were to rely solely on the browser sending its cfid-cftoken pair with every subsequent page request, we wouldn't be able to maintain state for that user.
Translated into the real world, if this user added an item to their basket, on the next page their basket would be empty.
ColdFusion, though, doesn't mind how it gets to see a browser's cfid and cftoken values. Normally, it would receive them in a cookie. To cater for browsers that have cookies disabled we have to ensure that that cfid-cftoken pair is sent along with each request by another means.
We can append cfid and cftoken values to URLs as a query string or embed them in forms as hidden fields. If the URL for the second page is coded on the first page like this
<a href="page2.cfm?cfid=#session.cfid#&cftoken=#session.cftoken#">Page 2</a>
when the request is sent by the browser for page2.cfm, it will also send with it's cfid-cftoken pair. ColdFusion can then marry up requests for pages with client and session variables tied to that user.
Alternatively, if the user submits a form to view page2.cfm, we could pass back the cfid and cftoken values like this:
<form action="page2.cfm#Session.URLToken#" method="post"> <input type="text" name="Colour"> <input type="submit"> </form>
When the form is submitted, the browser's cfid and cftoken values would be visible to ColdFusion as URL variables.
Using client and session variables is made very simple by ColdFusion. However, if you wish to take advantage of them and still enable users with cookie-disabled browsers to visit your site, you must recode your site to provide an alternative means of passing pack the cfid and cftoken values to the server.
To store information unique to a user in memory until needed at a later point in the application without having to pass the information from page to page with every click.
It executes in much the same way as application.cfm, except it is included after your template has been processed, whereas the Application.cfm is included before your template.
ColdFusion traverses up the directory tree to find the Application.cfm. ColdFusion looks for OnRequestEnd.cfm in the directory where it found the Application.cfm , but does not do any further searching. If it didn't find the Application.cfm, it won't look for OnRequestEnd.cfm.
Cookies are normally saved to the client's hard drive in a text file. To ensure that a user's session ends when they close their browser, save the cfid and cftoken values as per-session cookies instead. Per-session cookies aren't written as a text file to the users's computer; they are stored in memory and are deleted when the browser is closed. If a new browser window is opened and they revisit the same ColdFusion web site, the cfid and cftoken values that previously identified them no longer exist, causing ColdFusion to create a new session for that user.
<cfapplication name="myapp" sessionmanagement="Yes" setclientcookies="No"> <cfif not IsDefined("cookie.cfid")> <cflock scope="session" type="readonly" timeout="5"> <cfcookie name="cfid" value="#session.cfid#"> <cfcookie name="cftoken" value="#session.cftoken#"> </cflock> </cfif>
ColdFusion 4.0 has a bug in that it identifies the session not by the combination of cfid and cftoken, but, just by cfid alone. In case you indeed checked all settings and cfapplication tags, I'd go for an update to at least 4.01.
Alternatively, the user could have cookies turned off and send a URL to another user with his cfid and cftoken values in the query string.
There haven't been any conclusive rigorous tests about automatic read locking versus programmatic locking. Some people here have done some informal tests which indicate no significant difference, but to the best of my knowledge, none of those tests have involved significant numbers of concurrent users.
Also, to the best of my knowledge, no one outside of Allaire knows exactly how the automatic read locking works. It could be less than optimal, but it could be just right for all I know. You'd need to know the internals of the ColdFusion server engine to answer this question.
However, in the situation you're in, you don't have much choice - automatic read locking is far better than the alternative, which is just to let the unlocked code run as-is.
Whether this would be better or worse then single-threading sessions depends. Using single-threaded session obviates the need for any cflock tags around any session variables. On the other hand, it does this by allowing only one request for a given user to execute at a time. This might be problematic with framesets or multiple windows. Given a choice, I'd go with the automatic read locking, probably.