Apart from the ability to login to a Web site, which is very useful of course, we should also be able to maintain state in WebSnap applications; in other words, session management. Using WebBroker, this can be done in three ways: using fat URLs, using hidden fields, or using cookies. Using WebSnap, however, this can be done somewhat easier. In the previous section, we used the TLoginFormAdapter and the TWebUserList components to enable the login functionality in our WebSnap application. You might not have realized it, but this also requires the maintenance of state ”logged in or not ”in your current session. The thing is that it was done implicitly and behind our back (like many of the WebSnap features). But you can use the same techniques to store any information in the user 's session, using the TSessionsService component. The TSessionsService component is able to store name =value pairs for us with little effort.
To spoil the surprise right from the start, TSessionsService is in fact using cookies to store session IDs, but not the session values themselves . (Those are stored in memory in the TSessionsService object, which is the reason why they don't work for CGI executables that are shutdown between requests , and why we must keep our WAD Web App Debugger executable up and running to remain logged in). Login and Session support works best in ISAPI applications.
Let's continue with the WebSnap application to show how we can use the TSessionsService component to maintain some session information. First, add a new Page Module to obtain some session- related state information such as the date-of-birth of the particular visitor (it might be a nice idea to be able to greet a visitor on his or her birthday).
Click File, New ”Other; go to the WebSnap tab and select the WebSnap Page Module Wizard to add a new page. Set its Name/Title to Birthday, and make sure it uses an TAdapterPageProducer component because we will be using an Adapter component itself in a moment (see Figure 22.18).
Click OK and save the Page Module in pmBirthday.cpp .
On the TWebPageModule , we now need to drop a regular TAdapter component, which will be used to request the date of birth for this particular visitor. While we're at it, we might as well ask for his or her name (it wouldn't be nice to congratulate someone with the message "congratulations visitor on your birthday!"). It would, of course, be so much more personal to use a person's own name.
What we need to do first, is to right-click the Adapter component to start the Adapter Fields Editor to define two new adapter fields named Name and Birthday . When you click Insert inside the Adapter Fields Editor, the Add Web Component dialog pops up (see Figure 22.19). Here, you can define what kind of AdapterField you want.
Both the Name and Birthday should be a regular (string) AdapterField , so just add two AdapterField components and name them Name and Birthday , see Figure 22.20.
To make the appearance of the two AdapterFields more user friendly, make sure to change the DisplayLabel property of Name to "What's your name?" and the DisplayLabel property of Birthday to "When were you born?" In a moment, this will give a great and inviting message to the visitor.
To make the connection between the Tadapter , its fields, and the generated output, we must now double-click the TAdapterPageProducer to start the Web Page Editor again. Right-click the AdapterPageProducer and add an AdapterForm . Then, right-click the AdapterForm and add an AdapterFieldGroup . This will give you the expected design time warning (the Adapter property is nil), which is solved by assigning the Adapter property of the AdapterFieldGroup to the Adapter component we dropped just a minute ago, see Figure 22.21.
It's nice to be able to fill in your name and birthday, but it would be really useful if we could actually submit these values to the WebSnap Web server application. So, let's add an AdapterCommandGroup component to the AdapterForm , which, of course, yields another design time warning that is solved by assigning the DisplayComponent property of the AdapterCommandGroup component to the AdapterFieldGroup .
Before we can actually add a submit button (and optionally a reset button) to the AdapterCommandGroup , we must first make sure there's a corresponding action in the Adapter component ( AdapterFields correspond with DisplayFields , and AdapterActions correspond with CommandButtons , remember?).
Close the Web Page Editor for now, and return to the TAdapter component. Right-click it and this time start the Actions Editor. We need just one action ”to submit our name and birthday ”so add one AdapterAction of name SubmitNameAndBirthday . We'll get back to the implemenation in a moment, let's return to add the buttons first.
Double-click the TAdapterPageProducer again, and in the Web Page Editor select the AdapterCommandGroup and right-click to select Add All Commands. This will automatically (explicitly) create a new button with name CmdSubmitNameAndBirthday and caption SubmitNameAndBirthday , so you might want to add some spaces here and there in the Caption property of this button, as can be seen in Figure 22.22.
Now, you can recompile the application, run it (using the WebApp Debugger), go to the Birthday page, fill in your Name and Birthday, click the Submit Name And Birthday button, and nothing will happen. Using Netscape the page will even be cleared, and even using Internet Explorer you can click Home and Return to Birthday, empty the page and start all over again. Nothing is saved! Which is not so strange because we still need to implement the SubmitNameAndBirthday action event handler, of course.
Go back to the TAdapter component, right-click it to select the Actions Editor, select the (only) action SubmitNameAndBirthday , move to the Events tab of the Object Inspector, and write the following code for the OnExecute event handler:
const char* const strName = "Name"; const char* const strBirthday = "Birthday"; void __fastcall TBirthday::SubmitNameAndBirthdayExecute(TObject *Sender, TStrings *Params) { _di_IActionFieldValue MyValue; MyValue = Name->ActionValue; if (MyValue->ValueCount == 1) Session->Values[strName] = MyValue->Values[0]; MyValue = Birthday->ActionValue; if (MyValue->ValueCount == 1) Session->Values[strBirthday] = MyValue->Values[0]; }
Now, you can again recompile the application and run it, but it won't work the way you hoped it would. The information is stored in your session, but other than that nothing will happen ”the page is still cleared and you can start all over again. Again not so strange because we only store and never retrieve the session data.
We need to modify the Name and Birthday AdapterFields to make sure that ”when we need them ”their values are retrieved from the current session. That would ensure these two fields are initialized by the session data, resulting in a nonempty screen when we return.
To implement this, we need to right-click the Adapter component, select the Fields Editor, and for each of the Adapter Fields write a single line of code for the OnGetValue event handler, as follows :
void __fastcall TBirthday::NameGetValue(TObject *Sender, Variant &Value) { Value = Session->Values[strName]; } void __fastcall TBirthday::BirthdayGetValue(TObject *Sender, Variant &Value) { Value = Session->Values[strBirthday]; }
This will ensure that the values entered are persistent during the lifetime of the session (i.e. when you close your browser, the data is gone again), but it's a start (see Figure 22.23).
Here is a final technique to show how we can access the session ID; we can modify the HTML generated by the Birthday page to include the session ID itself. Just add the following scripting snippet to the HTML tab of the Page Module, and you'll see for yourself:
<p> Session: <%= Session.SessionID.Value %> </p> <hr>
Top |