Just published to Squeaksource: refactoring of the components of TFLogin.
The goal of the refactoring was to make the component rendering more amenable to styling and customization by the host application.
The components as delivered render their elements using HTML table formatting. As this may not be compatible with a given host application, the table formatting has been placed in a concrete subclass that can be replaced by one that uses some other method of formatting.
All embedded CSS has been removed from the rendering methods.
In almost all cases, rendering of individual form elements such as labels and input fields have been placed in individual base class methods that are used in the concrete classes to render the form elements within table data elements.
TF-Login
TF-Login is a login,registration, and user account management package for Seaside.
The problem
I have been constructing a web application that allows users to maintain a checklist of to-do items. In an effort to keep the barrier to entry as low as possible and entice new visitors to try the application, new visitors are allowed to create and manipulate a checklist without first logging in.
The new users are informed that for their content to be available to them on their next visit, they must register for a free account before they leave.
The trick here is how to save the content that they have already created (i.e. their checklist) and associate it with them so that after they have registered it can be made available to them in a seamless fashion. This is particularly interesting if registration involves email confirmation which will occur in a new Seaside session. And of course if the new user fails to confirm their registration, their saved content should be eventually discarded.
The TF-Login solution - #onRegistration:
TF-Login has features that make addressing this a straightforward process.
The TLLoginComponent has a method #onRegistration: to which you can supply a block to be evaluated before the registration email is sent. (Email confirmation may be enabled or disabled from the Seaside configuration page for the application.) If the result of the block evaluation is false, the registration process is aborted. That's not what is needed in this case, so we'll always return true.
The block is passed a single argument: a PendingUser object. This is the object that TF-Login will use to construct the RegisteredUser object that will be created and saved when the user navigates to address provided in the registration confirmation email.
We will insert the current checklist information into the applicationProperties dictionary of the PendingUser object. Then when the user logs in for the first time after confirming their registration, theat same checklist information will be available from the TLUser object's applicationProperties dictionary. The TLUser object for the currently logged in user is obtained like this from within a WAComponent in an application making use of TF-Login:
self session user.
If the user fails to confirm their registration, the checklist information stored in the PendingUser object's applicationProperties dictionary will be discarded along with the PendingUser object after the confirmation timeout period has expired. (The confirmation timeout period can be set in the Seaside application configuration page.)
Here's what the onRegistration block might look like:
loginComponent onRegistration: [ aPendingUser |
aPendingUser applicationProperties
at: 'checklistInfo'
put: self checklistInfo.
true]
Simple huh?
TF-Login's saveUser
A nice side benefit of saving user-specific data in the user object's applicationDictionary is that it can be easily persisted when it has changed using the TLLoginComponent>>#saveUser method. TF-Login saves the user object including its applicationProperties dictionary using the current storage adaptor. The default storage adaptor provided with TF-Login saves user objects in individual files and scales easily to 100,000 users.
What's next
My next problem was how to handle synchronization of the checklist information when the user logged in more than once concurrently. But that's a topic for another article...