11.4. Implementing the AJAX Comment Login System Using XML
<login> <result>fail</result> <message>Login Failed</message> </login>
If a login was attempted, a message should always be provided. An alternate version of this response with no message nodes can be received on the initial page load. This alternate response happens when the login status is read from the session. The other case is, of course, the XML that is shown when a user is logged in. This response contains profile data; using XML for data like this is nice because it makes it easy to extend for future purposes. An example of the XML returned by a successful login is shown here:
<login> <result>success</result> <profile> <value name="name">Joshua Eichorn</value> <value name="email">firstname.lastname@example.org</value> <value name="url">http://blog.joshuaeichorn.com </value> </profile> </login>
In a successful case, a result message and profile data are returned. The value tags within the profile tag store the actual profile information. In this example, the name attribute matches the ID of the field we want to update, but in most cases, this mapping wouldn't work and you would have to add code to match the XML nodes and the field IDs. In the initial page load case, the same response as a successful login is used. The code that generates these messages is shown in Listing 11-5.
Listing 11-5. XMLCommentLogin.php
Like the other login pages, Listing 11-5 is included directly in the main content page to produce the loading of default values. Unlike the other pages, it needs a specific flag set to know this is happening. It needs this flag because it sends a content-type header on line 50, and we want to do that only in the stand-alone case. The page starts by initializing the PHP session; like all the login examples, the actual status of the login is stored on the server. Just as in normal Web development, storing the login status on the client is a big security problem. Next (lines 613), we set up a helper function, profileXML, which loops over profile data and generates XML for it. It's in a helper function because we need to be able to access it in two places: once for a successful login (line 28), and once for the already logged-in case (line 42).
Lines 1547 handle the actual login processing. We're using GET requests in this case because they are easier to accomplish with Sarissa, but this could cause some future problems. GET requests can be cached, unlike POST requests, which could make it hard to use this same code as a method to reload profile data. On line 15, we check whether we have a login attempt; if so, we start building our output xml string (line 16) and then do a login check (lines 1920). If the login succeeds, we set the session flag (line 30) and load the user's profile into the session (lines 2226). The successful login process is completed by outputting the needed XML. The successful login code first outputs a result tag (line 27) and then the profile data (line 28) by calling the profileXML helper function.
If the login fails, we output a result tag with a value of fail (line 33) and add a message tag whose value will be displayed on the login form (line 34). The failed login process is finished by setting the SESSION flag to false (line 35). Lines 3947 contain the two default loading cases. If the user is logged in, we generate the profile XML (lines 4143); if the user isn't logged in, we output the failure XML with no messages (line 46). The file finishes by outputting an XML Content-type header (line 50), which is needed because PHP generates HTML files by default, and then we display the xml (line 52). This example is used by a comment page, shown in Listing 11-6, which has been updated to use the Sarissa library to consume the XML.
Listing 11-6. XMLComment.php
After that, we define a function that will handle submitting the login form (lines 1849). It starts by getting a Sarissa DOM Document (line 19); it then sets up its onreadystatechange handler (lines 2038). When the page is loaded (readyState == 4), this handler will use an XPath query to grab the result node (lines 2223) and then check to see whether its value is successful (lines 2526). If the value is success, the profile information will be loaded using the loadProfile function. If the value isn't success, an XPath query will be used to load the message node (lines 3031), and then the content of this message will be added to the paragraph element with an ID of message (lines 3235).
Once the handler is set up, the URL to be used in the request is created (lines 4044). Because we're making a GET request, the data is appended to the query string. The values are read from the form using the elements.elementName syntax. Each value read from the form is escaped; this escaping makes sure that we won't get a broken URL. Once we have a URL, we use the DOM document's load method to make the request (line 46) and then return false (line 48) so that the form won't do a normal submission. Lines 5156 define the loadInline function; this function is called at page load, and it uses a DOMParser to load the XML from loginData and then calls loadProfile on it.
Listing 11-7. XMLComment.php Continued
The rest of the page builds the user interface. It has a few changes from Comment.php. The big one is that the login form is defined directly on this page instead of being included. On line 72, we set an onload handler. This handler called loadInline loads any current profile data. After that, we have just the standard comment form until line 102, where we define the login form. The only difference in this form is that it is now calling the processForm function in its onsubmit handler. This event handler will start the AJAX submit process when the user clicks the Submit button. The data flow of the user filling out a comment, logging in, and then submitting his or her comment is shown in Figure 11-6; this data flow is identical in both the HTML_AJAX and Sarissa XML cases.
Figure 11-6. Workflow of the AJAX login process using Sarissa