Integrating TFLogin with Your Application Take a look at the TLTestApp to see an example of the steps below.
There are a number of steps to introducing TFLogin authentication to an existing Seaside application. They are described below:
1. Add the following lines to your application class initialization method:
application preferenceAt: #sessionClass put: TLSession.
application configuration parents add: TLConfiguration instance.
application configuration parents add: WAEmailConfiguration instance.
2. If you plan to use reCaptcha to protect your registration form from spammers, then you will need the BowWave reCaptcha package that you can obtain by evaluating:
Then add lines like the following to your application class initialization method containing your public and private reCaptcha keys that you obtain free from http://www.google.com/recaptcha.
application configuration parents add: BWRecaptchaConfiguration instance.
application preferenceAt: #publicKey put: 'your-public-key'.
application preferenceAt: #privateKey put: 'your-private-key'.
Note that these preferences can also be set using your application's Seaside configuration page.
If you do not plan to use reCaptcha spambot protection, then it is not necessary to install the BowWave package.
3. You can set TFLogin preferences in the initialize method if you wish, or you can set the using the Seaside application config page. Here is an example of what can be done in the initialize method:
application preferenceAt: #sendRegistrationConfirmationEmail put: true.
application preferenceAt: #confirmationTimeoutMinutes put: 10.
application preferenceAt: #useRecaptchaInRegistrationForm put: false.
application preferenceAt: #smtpServer put: 'localhost'.
application preferenceAt: #confirmEmailChangeViaEmail put: false.
application preferenceAt: #confirmAccountChangesViaEmail put: false.
application preferenceAt: #allowEmptyPasswords put: false.
application preferenceAt: #allowUsernameChange put: false.
application preferenceAt: #allowRememberUsername put: true.
application preferenceAt: #allowAutomaticLogin put: false.
6. In your application's initialize method (not the class initialize method as described above) put the following to make the TFLogin components known to your application:
loginComponent := (TLLoginComponent appName: '<your-app-name>').
loginComponent onAnswer: [ :user | user ifNotNilDo: [ :u | self loggedIn: u ] ].
self children add: loginComponent.
editAccountComponent := (TLEditAccountComponent loginComponent: loginComponent).
editAccountComponent onAnswer: [ self editingAccount: false ].
self children add: editAccountComponent.
If you don't already have one, you will need a children method that looks like this:
^ children ifNil: [ children := Bag new ]
7. If you will be using email confirmation, then you will need to tell TFLogin where your email sending methods are (also in the application initialize method). An example of the email sending methods referenced here is presented later on in this guide.
loginComponent registrationConfirmationEmailSender: [ :url :email :timeout |
timeoutMinutes: timeout ].
loginComponent passwordResetEmailSender: [ :url :email :timeout |
timeout: timeout ].
loginComponent usernameReminderEmailSender: [ :usernames :email |
to: email ].
editAccountComponent emailChangeConfirmationEmailSender: [ :url :email :timeout :newUser |
newUser: newUser ].
editAccountComponent accountChangeConfirmationEmailSender: [ :url :email :timeout :newUser |
newUser: newUser ].
8. If you want to support automatic login using username/password cookies for your users, you will need to include the following in your application's initialRequest method:
This will log the user in immediately before your application has had a change to display the loginComponent if they have the correct cookies defined (as would be the case if they checked the "Log me in automatically when I return to this site" checkbox when they last logged in.)
9. Here is an example of a mail-sending method. The text would of course vary depending on your application and the specific message being sent.
sendRegistrationConfirmationEmailTo: email confirmUrl: url timeoutMinutes: timo
"Compose and send email. Answer true on success, false on failure."
| textBody htmlBody emailOk appName |
appName := 'Login Test App'.
"Compose a pain text message."
textBody := (WriteStream on: String new)
<< 'This email is in response to your request to register at '; << appName;
<< '.'; crlf;crlf;
<< 'Direct your browser to the following URL within ';<< timo asString;
<< ' minutes to confirm your registration.'; crlf;crlf;
<< ' '; << url; crlf;crlf;
<< 'If you did not attempt to register for a'; << appName;
<< ' account then this message was sent in error and should be ignored.'.
"Compose a nice HTML message."
htmlBody := WAHtmlCanvas builder fullDocument: true; render: [ :html |
style: 'margin-bottom: 10px;';
with: 'This email is in response to your request to register at ', appName, '.'.
html text: 'Click on the link below within ', timo asString,
' minutes to confirm your registration.'.
with: 'Confirm registration'].
html text: 'If the link above is unresponsive, copy and paste the URL',
'below into your browser''s address field to confirm your registration.'.
html text: 'If you did not attempt to register for a', appName,
' account then this message was sent in error and should be ignored.']].
"Send the message."
(emailOk := self
subject: appName, ' Registration - action required'
text: textBody html: htmlBody)
ifFalse: [ Transcript cr; show: url ].
sendEmailTo: toAddress subject: subj text: textBody html: htmlBody
"Send multi-part MIME email message."
| sem mm|
mm := TLMailMessage empty.
mm addAlternativePart: textBody contents contentType: 'text/plain'.
mm addAlternativePart: htmlBody contents contentType: 'text/html'.
sem := mm
seasideMailMessageFrom: 'Registrar@' , self emailHost
[sem send] on: Exception do: [ :ex | ^ false ].
10. In your application's renderContentOn: you should render your loginComponent or editAccountComponent (from step 4 above) as embedded components. (They also support being "called" modally, but this functionality has not been tested.)
If the TLSession>>#user is nil, it means no-one is logged in and you might want to use this to determine whether to display the loginComponent.
The editAccountComponent is generally displayed in response to an application-provided button. In step 4 we assumed that ths button would set the editingAccount variable to true and that this would cause the editAccountComponent to be rendered. You may wish to use another method to determine how to display the editAccountComponent.
The loginComponent will answer when a user has successfully logged in. This is handled in the block specified in step 4 above. In that block we call an application method called loggedIn. You can do whatever you wish in that method.
The editAccountComponent will answer when new values have been entered and the user has supplied the correct password or has clicked the cancel button. In step 4 above the block specified for this sets the editAccount variable to false, which we are assuming will cause the editAccountComponent not to be rendered.