17.1. Lazy RegistrationAccount, Authentication, Customisation, Customization, Incremental, Login, Password, Personalisation, Personalization, Profiling, Registration, User, Verification Figure 17-1. Lazy Registration17.1.1. Goal StoryIt's Saturday afternoon, and Stuart is busy planning the evening's activities. He visits a band listings web site, where he clicks on a map to zoom into his local area. Even though he has never visited the site before, a profile, which he can see on the top of the site is already being constructed. At this stage, the profile guesses at his location based on his actions so far. As he browses some of the jazz bands, the profile starts to show an increasing preference for jazz bands, and some of the ads reflect that. Since the jazz thing is a one-time idea, he goes into his profile and tweaks some of those genre preferences but leaves the location alone since the system's guess was correct. Finally, he decides to make a booking, at which point he establishes a password for future access to the same profile including his address, which is posted back to the profile. 17.1.2. ProblemHow can the user customize the site while deferring formal registration? 17.1.3. Forces
17.1.4. SolutionAccumulate bits of information on the user as they interact while deferring formal registration. As soon as the user visits the web site, a user account with auto-generated ID is immediately created for her and set in a cookie that will remain in the browser. It doesn't matter if she never return; unused IDs can be cleared after a few months. As the user interacts with the application, the account accumulates data. In many cases, the data is explicitly contributed by the user, and it's advisable to expose this kind of information so that the user can actually populate it. In this way, the initial profile may be seen as a structure with lots of holes. Some holes are eventually filled out automatically and others by the user himself. The user is also free to correct any of the filled-in data at any time (Figure 17-2). Figure 17-2. User ProfileTwo particularly notable "holes" are a unique user identifier and a password. It is this combination of attributes that allows the user to access the profile from another machine or a different browser. They will also preserve the profile in the case that cookies are deleted from the user's browser. So, while this pattern is generally about gradual accumulation of profile data, there remains a significant milestone in the user/application relationshipthe moment at which user ID and password are established. Do the user ID and password have to be provided simultaneously? No. Even that can be incremental as long as you make the email address the unique identifier. In fact, this is pretty common nowadays. Email is usually required anyway, and it's unique, so why not make it the user ID? In the context of Lazy Registration, though, there's an additional benefit, as the email might be accumulated in the natural flow of eventsthe site might add the user to an announcements list, for example. In some cases, the email might even be verified during this process. Sceptics may wonder why a user would want to actively work with her profile. The answer was formulated in a web usability pattern called "Carrot and a Stick" (http://jerry.cs.uiuc.edu/~plop/plop99/proceedings/Kane/perzel_kane.pdf):
Thus, users will only enter information if there is a perceived benefit to them. There is plenty of evidence that this occurswitness the social bookmarking phenomenon, where thousands of users make public their personal links. By exposing their profiles, many of those users are hoping the system will point them in the direction of related resources they have not yet heard of. Some web sites have used this pattern for years, so what does it have to do with Ajax? Lazy Registration aims for a smooth approach in which the barrier is low for each new user contribution. For instance, you sign up for a web site's mailing list, and your email is automatically added to your profile and shown on the side of the page. With Ajax, there's no need to break the flow. No more "just go over there for a few minutes, then come back here, and if you're lucky, you might be looking at something similar to what you can see now." That's a big win for web sites aiming to drop the barrier of registration, and it's great for users, too. It's standard practice for web sites to collect data about users. The aim of this pattern is to empower them to contribute to this. Instead of covertly building up a corpus of data on a user, you invite him to add value to his own experience by contributing and maintaining the data himself.[*]
Several technologies are involved in Lazy Registration:
17.1.5. Decisions17.1.5.1. What kind of data will the profile contain?Usability and functionality concerns will drive decisions on what data is accumulated. By envisioning how users will interact with the web site, you can decide what kind of data must be there to support the interaction. For example:
In addition, consider that some users, such as employees working on an intranet web site, use certain Ajax Apps all day long. For that reason, the profile might also contain preferences similar to those on conventional desktop applications. Many options will be application-specific, but a few generic examples include:
One issue that arises with Lazy Registration is the clearing of data. What if a user visits once and never comes back? You probably don't want to keep that data sitting there forever. Typically, you will probably have a script running daily to delete (or archive) the records of users whose last login was, say, three months ago. 17.1.5.2. How can the profile be accumulated?You might know what data you need, but are users willing to give it to you? This comes back to the carrot-and-stick argument: you need to provide users a service that will make it worthwhile for them to provide that data. In addition, you need to communicate the benefit, and you must be able to assure them that the data will be safe and secure. The least imaginative way to gain user data is to pay them for it, or, more deviously, pay others for it. Giving away a T-shirt in exchange for data was fine during the dot-com boom, but hopefully you can do better than that. Give the user a service they really need. For example:
17.1.5.3. How much data should be stored in cookies?How much you store in cookies depends on your general approach to the Ajax implementation: is the application browser-centric or server-centric? A browser-centric choice would be to pack as much as possible into the browser's local state so as to optimize performance, while running a full-fledged JavaScript application with a little server-side synchronization. A server-centric approach would rely only on data held server-side, with the browser accessing additional data on a need-to-know basis using XMLHttpRequest Calls. One special concern is the security of cookies. If users access the application from a public PC, there's the risk of unauthorized access. In this case, it's especially advisable not to store sensitive information in the browser and to offer the possibility of cleaning cookies at the end of the session. (For instance, call the option "I'm on a public terminal.") 17.1.6. Real-World Examples17.1.6.1. MemeflowSteve Lacey's MemeFlow (http://memeflow.com) is a portal with RSS-backed Portlets. Its use of Lazy Registration is characteristic of several other portals (Figure 17-3). You can immediately build up a collection of your favorite feeds, and when you provide your username and password later on, those feeds will remain. Figure 17-3. Memeflow17.1.6.2. BlummyAlexander Kirk's Blummy (http://blummy.com) is a bookmarklet manager (Figure 17-4). You can start adding bookmarklets to a personal "Blummy" container straightaway (this has a unique URL). When you register, you'll get a URL with your own name, but the old URL remains valid, so you can keep the bookmark you created before registering. Figure 17-4. Blummy17.1.6.3. KayakKayak (http://kayak.com) is a travel search engine that retains queries you've made. A query history is available for nonregistered users and becomes part of your profile once registered. 17.1.6.4. PalmspherePalmsphere (http://palmsphere.com/store/home) showcases Palm applications for download and purchase. Each item has a Favorite buttonif checked, the item is one of your Favorites. The Favorites list is summarized in your Member Center area, even if you've never registered, and retained in a cookie for the next time you visit. 17.1.6.5. Amazon.comAmazon (http://amazon.com) has begun incorporating Ajax features only recently, but it blazed the trail for Lazy Registration a long time ago. Visit Amazon (http://amazon.com) as a new user, browse for just a few seconds, and here's what you'll see before even beginning to register or log in:
17.1.7. Code Example: AjaxPatterns Shop DemoThe Ajax Shop Demo (http://ajaxify.com/shop) illustrates the kind of user interface described by this pattern (Figure 17-5). Figure 17-5. Ajax Shop DemoWhen you run the demo, you'll notice a few things:
Figure 17-6. Ajax Shop Demo with email address and password enteredTo keep things simple, it doesn't actually use a persistent data store; all information is held in the session. That's definitely not advisable for a real system, because you don't want to store passwords and other sensitive data there. Also, it means that the user, in theory, could bypass the email verification by inspecting the cookie. Nevertheless, the application demonstrates Lazy Registration from the user's perspective, and the underlying code provides some illustration of what's required to develop such an application. Following are some of the features and how they were achieved. 17.1.7.1. Retrieval of categories and itemsThe application maintains the flow by avoiding any page reloads when categories and items are accessed. No information about categories or items is hardcoded; generic REST services are used to extract the data and are rendered locally in JavaScript. 17.1.7.2. Cart managementAgain, the only real relevance of cart management is that page reloads are avoided. The cart contents are tracked in the session, so they should be present when the user resumes using the web site. When the user adds something to the cart, the JavaScript cart is not directly altered. Instead, the new item is posted to the server as XML: function onAddItemClicked(item) { var vars = { command: 'add', item: item } ajaxCaller.postForXML("cart.phtml", vars, onCartResponse); } And likewise when the cart is cleared: function onCartClearClicked( ) { var vars = { command: 'clear' } ajaxCaller.postForXML("cart.phtml", vars, onCartResponse); } For both operations, the server retrieves the session cart and alters its state: $cart = $_SESSION['cart']; if (isset($_POST["command"]) && $_POST["command"]=="add") { $item = $_POST["item"]; $cart->add($item); } else if (isset($_POST["command"]) && $_POST["command"]=="clear") { $cart->clear( ); } Then, the server outputs the final state as an XML response: header("Content-type: text/xml"); echo "<cart>"; $contents = $cart->getContents( ); foreach (array_keys($contents) as $itemName) { echo "<item>"; echo "<name>$itemName</name>"; echo "<amount>".$contents[$itemName]."</amount>"; echo "</item>"; } echo "</cart>"; In the browser, onCartResponse is registered to render the cart based on the resulting XML. 17.1.7.3. Mailing cart contentsThe profile block contains, along with several other fields, the user's email. There's also a clickable Mail field on the cart: <div> <div >Email:</div> <input type="text" name="email" /> </div> ... <span >Mail Contents</span> .... $("cartMail").onclick = onCartMailClicked; When the user clicks on cartMail, the server checks that the email has been filled in and simply uploads a POST message for the mail to occur. In this case, there's no feedback to the web user, so the callback function is blank: vars = { command: "mailCart", email: email } ajaxCaller.postForPlainText("cart.phtml", vars, function( ) {}); The server receives not only the command, but the email address itself, since this might not be in the user's profile yet. Just prior to sending the mail, the server retains the address as part of the user's session:[*]
function mailCart( ) { ... $email = $_POST["email"]; // Add mail to the profile - it's part of the Lazy Registration. $_SESSION['email'] = $email; ... } Then, it's a simple matter of constructing a message from the server-side cart state and sending the email to the specified address using standard server-side libraries. 17.1.7.4. Tracking favorite categoriesThere's a fixed "favorite category" selector in the HTML. It begins empty and is populated when the categories are loaded: <div > My Best Category: <select ></select> </div> function onAllCategoriesResponse(xml, ignoredHeaders, ignoredContext) { ... categoryExplores[category] = 0; favoriteCategoryOption = document.createElement("option"); ... } There's also a mode variable to indicate whether the favorite category selection is automated. It begins in automated mode: var isFavoriteCategoryAutomated = true; If in automated mode, the script watches each time the user explores an item. Each category is tracked according to how many times the item was explored, and the selector is altered if a new maximum is reached: var categoryExplores = new Array( ); ... function onExploreClicked(category) { ... if (isFavoriteCategoryAutomated) { categoryExplores[category]++; favoriteCategory = $("favoriteCategory").value; favoriteCategoryExplores = categoryExplores[favoriteCategory]; if (categoryExplores[category] > favoriteCategoryExplores) { $("favoriteCategory").value = category; } } } If the user decides to overwrite this Guesstimate by manually setting the preference, it will stay manual permanently: $("favoriteCategory").onclick = function( ) { isFavoriteCategoryAutomated = false; } For the sake of simplicity, this field is not actually tracked in the server, though it could easily be incorporated into the user's profile. 17.1.7.5. Verifying password and emailNow for the most important part. The user is finally willing to verify her password and email. These could potentially be broken into two separate verification activities, but since they fit together as a formal registration step, they are combined in the demo. The trick is to manage the process with a little state transition logic. The registration is broken into a few states with transitions between them. Each state requires you to handle events in a slightly different way. Each transition involves altering the UI a little to reflect what the user can do. registerState holds the current state: /* "start": When page is loaded "mustSendMail": When instructions and verify password field shown "mustVerifySecretNumber": When email sent and user must enter secret number inside email "verified": When user is successfully logged in */ var registerState = "start"; The HTML for this demo contains all the necessary fields and buttons. Their visibility is toggled based on the current state. For example, following are the password and password verification fields. The password field is always shown until the user is at the "verified" stage, whereas the "verify password" field is only shown after the user initiates the registration process: <div > <div >Password:</div> <input type="password"ael name="password"/> </div> <div > <div >Demo Registration</div> <div > <strong>1.</strong> Please ensure email address is correct and password is <strong>not</strong> confidential, then verify your password below. </div> <div >Verify Password:</div> <input type="password" name="verifyPassword" /> </div> All three buttons are declared and, again, their visibility will change depending on the current state: <input type="button" value="Login"></button> <input type="button" value="Register"></button> <input type="button" value="Cancel"></button> What's most important here is the Register button, which drives the process through each state. The Cancel button returns the state back to start, which causes the display to return to its initial state too. The purpose of the Login button is purely for demonstration. The Register button is present until the user is verified, and its label changes at each stage of the registration process. Its event handler remains the same throughout; the handler decides what to do based on the current state: function onRegisterClicked( ) { if (registerState=="start") { registerState = "mustSendMail"; } else if (registerState=="mustSendMail") { var submissionOK = sendMail( ); if (submissionOK) { registerState = "mustVerifySecretNumber"; } else { return; } } else if (registerState=="mustVerifySecretNumber") { verifySecretNumber( ); } onRegistrationStateChanged( ); } And onRegistrationStateChanged( ) exists purely to reveal and hide fields, and to change the button label based on the current state: function onRegistrationStateChanged( ) { if (registerState=="start") { $("userForm").reset( ); $("login").style.display = "inline"; $("verifyPasswordInfo").style.display = "none"; $("secretNumberInfo").style.display = "none"; $("verifiedInfo").style.display="none"; $("cancel").style.display = "none"; $("register").value="Register"; } else if (registerState=="mustSendMail") { ... } else if (registerState=="mustVerifySecretNumber") { ... } else if (registerState=="verified") { ... } 17.1.8. Related Patterns17.1.8.1. Direct LoginDirect Login (see the next pattern) is a companion pattern, since some dynamic behavior can allow for login and registration to appear on the same form. 17.1.8.2. Live FormIt's useful to maintain the profile details in a Live Form (Chapter 14) so that the user can easily add to them and the server can synchronize state and provide opportunities for further enhancement to the profile. 17.1.8.3. TimeoutWhen data is held in cookies, it's important to expire the cookies if there's a risk that others may gain access to the browser. Timeout (see later) helps the server decide whether the client is still active. If it's not, it may be wise to ensure that any sensitive data is wiped from the cookies held in the browser. 17.1.8.4. GuesstimateLazy Registration can sometimes involve inferring information about the user's profile by monitoring his behavior. Thus, it embraces the same nebulous principles as Guesstimate (Chapter 13), where a guess is acknowledged to be imprecise but better than no guess at all. 17.1.9. MetaphorA good salesperson works the same way. While assumptions might be made based on a prospect's behavior, the salesperson is always listening; her assumptions are always open to challenge. (Malcolm Gladwell depicted this pattern of successful salespeople in Blink [Little, Brown, 2005]). 17.1.10. Want to Know More?
17.1.11. AcknowledgmentsThe idea to handle Lazy Registration in this Ajaxian manner was originally proposed by Chris Were ("Tahpot")(http://tahpot.blogspot.com/2005/06/lazy-registration-with-ajax.html). |