Some of the new, cool, hip sites out there have done something rather neat with their login system. If you take a look at Technorati, notice that when you click the Sign In link, a modal window pops up. This lets you login no matter where you in the site. No need to go to another page (thank goodness, since Technorati is slow) and be sent back (hopefully) when done. Can ColdFusion 8 do this? Of course! Let's look at an example.
First off - imagine we have an existing site. One that uses a custom tag for layout (details may be found here). All of our pages use the custom tag. For example:1 <cf_layout>
2
3 <p>
4 Welcome to our Web 2.0 site.
5 </p>
6
7 </cf_layout>
(As just a quick warning, I'll be sharing the complete code for all of this at the end of the blog entry, and via a download link.) Our layout custom tag creates a simple navigation table on top:
2
3 <p>
4 Welcome to our Web 2.0 site.
5 </p>
6
7 </cf_layout>
1 <a href="index.cfm">Home</a> / <a href="about.cfm">About Us</a> / <a href="buy.cfm">Buy Us</a>
The first thing I want to do is add a Login link to the menu:
1 / <a href="javaScript:doLogin()">Login</a>
The link above runs the JavaScript function, doLogin, so let's go there next.
1 function doLogin() {
2 ColdFusion.Window.create('loginwindow','Login','login.cfm',{center:true,modal:true});
3 }
This function simply uses the ColdFusion/Ajax API to create a window. I named it loginwindow, gave it a title, pointed it to a login.cfm page, and passed a few attributes. I could have styled it as well, made it a specific size, etc. But you get the idea. Now let's look at login.cfm:
2 ColdFusion.Window.create('loginwindow','Login','login.cfm',{center:true,modal:true});
3 }
1 <form action="login.cfm" onSubmit="handleLogin();return false;" id="loginform">
2 <table>
3 <tr>
4 <td>Username:</td>
5 <td><input type="text" name="username"></td>
6 </tr>
7 <tr>
8 <td>Password:</td>
9 <td><input type="password" name="password"></td>
10 </tr>
11 <tr>
12 <td> </td>
13 <td><input type="submit" name="login" value="Login"></td>
14 </tr>
15 </table>
16 </form>
This is a fairly simple form - but note that I specifically block the form submission. I call yet another JavaScript function, handleLogin. So let's take a look at that:
2 <table>
3 <tr>
4 <td>Username:</td>
5 <td><input type="text" name="username"></td>
6 </tr>
7 <tr>
8 <td>Password:</td>
9 <td><input type="password" name="password"></td>
10 </tr>
11 <tr>
12 <td> </td>
13 <td><input type="submit" name="login" value="Login"></td>
14 </tr>
15 </table>
16 </form>
1 function handleLogin() {
2 ColdFusion.Ajax.submitForm('loginform','processlogin.cfm',handleResponse);
3 }
Once again we have a use of the ColdFusion/Ajax API. The submitForm function will do exactly as it sounds - send an entire form block to a page. My last argument, handleResponse, is the function that will be called with the result. Let's look at processlogin.cfm first:
2 ColdFusion.Ajax.submitForm('loginform','processlogin.cfm',handleResponse);
3 }
1 <cfsetting enablecfoutputonly="true">
2 <cfparam name="form.username" default="">
3 <cfparam name="form.password" default="">
4
5 <cfif structKeyExists(form, "login")>
6 <cfif form.username is "paris" and form.password is "hilton">
7 <cfset session.loggedin = true>
8 <cfoutput>good</cfoutput>
9 <cfelse>
10 <cfoutput>bad</cfoutput>
11 </cfif>
12 </cfif>
Nothing too complex here. Normally you would call a CFC to handle the authentication. I've never used a hard coded username and password in production. Really. Now notice that I output two strings: good and bad. Remember how I submitForm named a function to run with the result? Let's look at that now:
2 <cfparam name="form.username" default="">
3 <cfparam name="form.password" default="">
4
5 <cfif structKeyExists(form, "login")>
6 <cfif form.username is "paris" and form.password is "hilton">
7 <cfset session.loggedin = true>
8 <cfoutput>good</cfoutput>
9 <cfelse>
10 <cfoutput>bad</cfoutput>
11 </cfif>
12 </cfif>
1 function handleResponse(s) {
2 if(s == "good") {
3 //first hide the window
4 ColdFusion.Window.hide('loginwindow');
5 //rewrite out login
6 var loginspan = document.getElementById('loginstatus');
7 var newcontent = "<a href='index.cfm?logout=1'>Logout</a>";
8 loginspan.innerHTML = newcontent;
9 } else {
10 alert('Your login didn\'t work. Try paris/hilton');
11 }
12 }
The function was passed the response, so all I need to do is check the value. If it is good, I hide the window (again, using the ColdFusion/Ajax API. Now I do something fancy. It wouldn't make sense to keep the login link on the page. I wrapped my login link in a span:
2 if(s == "good") {
3 //first hide the window
4 ColdFusion.Window.hide('loginwindow');
5 //rewrite out login
6 var loginspan = document.getElementById('loginstatus');
7 var newcontent = "<a href='index.cfm?logout=1'>Logout</a>";
8 loginspan.innerHTML = newcontent;
9 } else {
10 alert('Your login didn\'t work. Try paris/hilton');
11 }
12 }
1 <span id="loginstatus"><a href="javaScript:doLogin()">Login</a></span>
This lets me get the span and change the HTML inside. Pretty simple, right?
To see an online demo of this, go here: http://www.coldfusionjedi.com/demos/login/index.cfm
Obviously someone could do this a lot prettier then I did - but notice how nice it works. You can login from any page, and when done, you are still on the page you were looking at. (I do something like this for the contact form at CFBloggers.org.) Also note how little JavaScript was involved. The most complex bit was handling the result of the login, and even that was just about 10 lines of code.
Enjoy. I've attached the code to this blog entry.
Comment 1 written by Steve Sequenzia on 29 November 2007, at 2:22 PM
Thanks for the great info.
Comment 2 written by Will Swain on 29 November 2007, at 2:34 PM
Thanks for all the hard work.
Comment 3 written by Raymond Camden on 29 November 2007, at 2:36 PM
Comment 4 written by Will Swain on 29 November 2007, at 2:40 PM
Anyway, I meant thanks for all the hard work generally. I find your blog an invaluable resource. Keep up the good work.
Comment 5 written by Dave on 29 November 2007, at 3:03 PM
Comment 6 written by Troy Allen on 29 November 2007, at 3:17 PM
BTW...I think you have a Typo on your "Buy Us" page:
<quote>Our you incredibly rich?</quote>
<grin>
Troy
Comment 7 written by Shane Zehnder on 29 November 2007, at 4:06 PM
Comment 8 written by Will Swain on 29 November 2007, at 4:23 PM
Comment 9 written by Raymond Camden on 29 November 2007, at 4:30 PM
From Ben: Official line is all current IE, FF, Opera and Safari. Safari has issues with FCK though.
The previous quote is in regard to supported browsers.
For JS turned off - there is no official way to handle stuff.
For my demo - you could easily make the login link go to a traditional form. Look at my blog entry on Spry and HTML Panels and how they handle graceful degradation. That style of link would work just fine here.
Comment 10 written by Will Swain on 29 November 2007, at 4:36 PM
Other than that, I suppose it's an issue of 'know your user' which determines how much trouble to go to.
Comment 11 written by Scott Bennett on 29 November 2007, at 5:41 PM
I have found that there are not too many people that disable JavaScript these days. Here are some statistics from the last 30 days on the most popular site I work on:
JavaScript enabled:
Yes - 309,475 visitors - 99.44%
No - 1,756 visitors - 0.56%
And a lot of those people that didn't have JavaScript enabled were people browsing on their blackberry's and such.
But then again, at our current conversion rate and average order value, those 1,756 people per month could represent about $12,000 in sales so that would make it well worth my effort to make sure they could browse my site easily without javascript, even though they should be paying attention to their driving and not shopping online with their phone =)
Comment 12 written by H Jaber on 29 November 2007, at 6:20 PM
Comment 13 written by Raymond Camden on 29 November 2007, at 7:52 PM
Comment 14 written by Nkosi on 29 November 2007, at 9:26 PM
Comment 15 written by Andrew on 30 November 2007, at 7:14 AM
Comment 16 written by Raymond Camden on 30 November 2007, at 8:27 AM
Comment 17 written by H Jaber on 1 December 2007, at 1:31 PM
Maybe I misunderstood the post in that case. I was actually thinking of this example "Tooltips With Interactive Content" towards the bottom of this page:
http://labs.adobe.com/technologies/spry/samples/to...
I imagine that your previous posts on posting data from spry using the "post" method can be combined with that sample and create a similar effect.
Comment 18 written by Raymond Camden on 3 December 2007, at 11:25 AM
Comment 19 written by cha on 18 January 2008, at 12:40 AM
Comment 20 written by Raymond Camden on 18 January 2008, at 6:22 AM
Comment 21 written by DML on 28 February 2008, at 11:50 AM
Comment 22 written by Cliff on 18 May 2008, at 5:25 PM
Thank you, this is just what I was looking for!
I am not a CF developer, just a big fan. This was very helpful.
If I am going to check this against a database with hashed passwords, where do you propose I add that? To the processlogin.cfm file?
Also, I'd like this to be modal, so that if someone goes to a page they don't have access to, they won't see the content until they log in.
I have two other customization questions.
If the user enters an incorrect user name or password, it leaves the data in the fields. Can you suggest a way to clear the fields after the notice?
Also, can you propose a way to include a "forgot password" link in the login.cfm page that would close the cfwindow and redirect them to the forgot password page?
Many thanks, Cliff
Comment 23 written by Aaron on 23 November 2008, at 2:04 PM
This looks great and appears to be clear as mud until I try to run it in CF8. I have a js alert in begining of my handleResponse function which tells me the value of "s" (either good or bad passed from the processlogin.cfm). Trouble is, although I can see the value is being passed correctly (i.e s=good) it never logs me in as the js function does not seem to be treating "s" as a string...or something?
Any ideas please?
Comment 24 written by Raymond Camden on 23 November 2008, at 8:42 PM
Or are you saying the result of s "looks" to be good, but the stuff in the s=="good" portion isn't running? If so, I'd consider using Firebug and debugging the result. You can use console.log(s) to see the value.
Comment 25 written by Saul on 22 March 2009, at 6:46 AM
I was tinkering with things and trying to learn more, so I have modified your script so that if the login fails, it runs a cfajaxproxy instance to a cfc to count the number of failed logins and returns the "x failed logins" to the login window and updates a div. This works beautifully, but the code only runs on a form submit event.
So what I was wanting to do was also run the code just after the coldfusion.window.create code as well, but this fails, I think because I would need to run it after some sort of ready event. Is there such a thing available when using the inbuilt coldfusion.window.create function?
Comment 26 written by Raymond Camden on 22 March 2009, at 1:58 PM
Comment 27 written by Jeff on 17 November 2009, at 1:00 PM
Comment 28 written by Patrick on 19 November 2009, at 10:52 PM
Comment 29 written by Raymond Camden on 21 November 2009, at 8:45 AM
Comment 30 written by Patrick on 21 November 2009, at 10:32 AM
Comment 31 written by Raymond Camden on 21 November 2009, at 10:42 AM
Comment 32 written by dan fredericks on 31 December 2009, at 10:27 AM
I am looking into the FW/1 Framework and was wondering how I might incorporate this into the framework? I can't seem to get the good to return to the function, so I can't log in. Any ideas?
I am really new to frameworks, and from what I have read (on your blog and others), this framework might be the best to use to start understanding frameworks.
thanks for the help
Dan
Comment 33 written by Raymond Camden on 31 December 2009, at 11:03 AM
Does that make sense?
Comment 34 written by Raymond Camden on 31 December 2009, at 11:15 AM
Comment 35 written by Dan Fredericks on 31 December 2009, at 11:45 AM
In my views folder I have a login folder with the login.cfm and processlogin.cfm pages. on the default.cfm page I run the cfwindow tag that will pop open the window with the source of the cfwindow tag being: index.cfm?action=login.login.
on the login.cfm page I have my fields for id and pass. I submit using your js call to the function that does the submitForm. I have this and all my javascript calls on my default.cfm page in my layouts folder.
This is where I have my issue. It runs the submitform (i think), and is suppose to call handleResponse js when it is done (this also on the default.cfm page in layout folder). It seems to call the function, however the variable of good is set on the processlogin.cfm page and I don't really know how it can be used in the response js.
I am having a hard time getting my head around controllers, services, layout, etc. and how data passes. Also, I have not done a lot of cfscripting, and alot of the code you all are writing now is in cfscript...it is taking me a while to convert that over in my head.
Anyway, thanks first for blogging about this framework, would not have known about it if not. Also thanks for any help you can give me...
The more example code from anyone using this framework would be great for not only me, but for anyone else that wants to check out FW/1.
Thanks again.
Dan
Comment 36 written by Raymond Camden on 31 December 2009, at 12:40 PM
Comment 37 written by dan fredericks on 4 January 2010, at 6:37 AM
Thanks for all the information.
Comment 38 written by Thompson Edolo on 16 January 2010, at 8:02 AM
[Add Comment] [Subscribe to Comments]