Iliad Framework, index page

In this post we will create a page to display the notes that we created with the project notes app. We will use the widget that we have created in the previous lesson to display the current user and we will create a form to search inside notes.

Creating the PnViewNotes widget

Let’s start by creating a new widget that will be the content of the new page:

ILWidget subclass: #PnViewNotes
    instanceVariableNames: 'searchstring'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

We created an instance variable, searchString, to remember the search string that will used to search inside the notes.

The rendering methods of the PnViewNotes widget

The interesting bits of this example are inside the rendering methods:

contents
!PnViewNotes methodsFor: 'building'!
    ^ [ :e | e h1 text:'Project notes'.
        e p text:'Use this application as a scratch pad. You can 
        attach to every page a series of tags to ease searching.'. 
        
        e build:self searchForm.
        e build:self searchResults ]

Let’s present the search form:

!PnViewNotes methodsFor: 'building'!
searchForm
    ^ [ :e | e form build:[ :form |
            form div class:'input-group'; build:[:row |
                row a class:'glyphicon glyphicon-plus input-group-addon'; action:[self addNewNote].
                row input class:'form-control'; beSubmitOnChange; 
                    attributeAt:'placeholder' put:'Search string'; 
                    action:[ :value | searchstring := value. self doSearch ].
                 ] ] ]

As you see we have used the BootstrapJs CSS declarations with the class: method of elements. When our element doesn’t support an attribute, like the placeholder attribute and the ILInputElement objects, we can manually place attributes with the methodattributeAt:put:`.

Another interesting bit is the beSubmitOnChange method, that will cause the ILInputElement to call the server every time the user changes the text field content, without waiting for the form to be submitted.

Every time the search string is modified we place the new content in the searchstring instance variable and we invoke the doSearch method to search inside the notes.

As we haven’t already implemented the notes DAO we delay the discussion of the doSearch method, that we implement like this:

!PnViewNotes methodsFor: 'actions'!
doSearch

The same thing happens with the addNewNote method:

!PnViewNotes methodsFor: 'actions'!
addNewNote

Until we haven’n implemented the PnNote object and its DAO we delay the implementation of the searchResults method:

!PnViewNotes methodsFor: 'building'!
searchResults
    ^ [ :e | ]

In this lesson we left many method unimplemented, but in the following one we implement the PnNote object and its DAO.

Iliad Framework, index page

Iliad Framework, a status widget for displaying the current user

In the previous post we completed the users registration form. Now we can use the login form.

To show the current user in the next pages we will implement a current user heading as a widget:

ILWidget subclass: #PnCurrentUserHeading
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

In the contents of this widget we will use the application method of the ILWidget class to access the current application and the current user:

!PnCurrentUserHeading methodsFor: 'building'!
contents
    ^ [ :e | 
    e div
        cssClass: 'navbar navbar-static-top bs-docs-nav';
        build: [ :header | 
                    header div
                        class: 'navbar-brand';
                        text: 'Project Notes for ' , self application currentuser email.
                    (header ul
                        cssClass: 'nav navbar-nav';
                        li)
                        build: [ :logout | 
                            logout a
                                text: 'Logout';
                                action: [ self logout ] ] ] ]

We also included a logout action that reset the current user and redirect the application to the login page:

!PnCurrentUserHeading methodsFor: 'actions'!
logout
    self application currentuser:nil.
    self redirectToLocal: 'login'.

We include this widget in the application class like we have done before with the login and the registration page:

ILApplication subclass: #LcBlogProjectNotes
    instanceVariableNames: 'loginWidget registrationWidget currentuser currentUserWidget'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

!LcBlogProjectNotes methodsFor: 'accessing'!
currentUserWidget
    ^ currentUserWidget ifNil: [ currentUserWidget := PnCurrentUserHeading new ]

Now we create a new notes controller:

!LcBlogProjectNotes methodsFor: 'controllers'!
notes
    ^ [ :e | e build:(self currentUserWidget). ]
Iliad Framework, a status widget for displaying the current user

Iliad Framework, the registration form (part 2)

In the previous post we build a registration form and in this one we will attach it to our application.

We start adding an instance variable to our application to contain the registration widget and another instance variable to contain the current user:

ILApplication subclass: #LcBlogProjectNotes
    instanceVariableNames: 'loginWidget registrationWidget currentuser' 
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

As we have done for the loginWidget, we create an accessor method that will construct the widget if it hasn’t been instanciated:

!LcBlogProjectNotes methodsFor: 'accessing'!
registrationWidget
    ^ registrationWidget ifNil: [ registrationWidget := PnCreateUser new ]

We also create accessors for the current user instance variable:

!LcBlogProjectNotes methodsFor: 'accessing'!
currentuser: anObject
    currentuser := anObject

!LcBlogProjectNotes methodsFor: 'accessing'!
currentuser
    ^ currentuser

Now we create a controller for the registration page:

!LcBlogProjectNotes methodsFor: 'controllers'!
register
    ^ [ :e | e div class:'container'; build: self registrationWidget ]

Now we have our new controller and we can test it from the login page. The controller name, register, match with the href in the login page.

Iliad Framework, the registration form (part 2)

Iliad Framework, the registration form

In the previous post we talked about checking emails addresses, just to show how we can use regexps in Pharo.

In this post we are returning to the Iliad Framework and we will be a registration form.

We start, as we have done for the login form, creating a component for the registration form. This component will then be integrated into the application and another controller will be build.

ILWidget subclass: #PnCreateUser
    instanceVariableNames: 'email password password2 name surname errors'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'!

As you can see the instance variable of this component, like what was happening in the registration form, are derived from the variable state of the component. We created an instance variable for every data that the user will enter and a errors fields that will be empty whenxb there are no errors.

The initialize method clear all the instance variables:

!PnCreateUser methodsFor: 'initialization'!
initialize
    super initialize.
    name := ''.
    surname := ''.
    password := ''.
    password2 := ''.
    email := ''.
    errors := ''.! !

As you saw in the login form when you are programming in Smalltalk you usually create short methods, more short than the method you will be used to. This is an example:

!PnCreateUser methodsFor: 'building'!
contents
    ^ [ :e | 
    e h1 text: 'Register your account'.
    e p
        text:
            'You will use your email to login'.
    e form
        build: [ :form | 
            self buildEmailRow: form.
            
            self buildNameRow: form.
            self buildSurnameRow: form.
            
            self buildPasswordRow: form.
            self buildRepeatPasswordRow: form.
            
            self buildErrorMessageRow: form.
            
            form button
                class: 'btn btn-default';
                text: 'Register to project notes';
                action: [ self registerAction ] ] ]! !

!PnCreateUser methodsFor: 'building'!
buildErrorMessageRow: aForm
    errors
        ifNotEmpty: [ (aForm div class: 'alert alert-warning') ul
        build: [ :box | (errors findTokens: String cr) do: [ :msg | box li text: msg ] ] ]! !

!PnCreateUser methodsFor: 'building'!
buildPasswordRow: aForm
    aForm div class: (errors ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row | 
                row label text:'Password: '.
                row input class:'form-control';  type: 'password'; action: [ :text | password := text ]; value:'' ]! !

!PnCreateUser methodsFor: 'building'!
buildEmailRow: aForm
    aForm div class: (errors ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row | 
                row label text:'Email'.
                row input class:'form-control'; action: [ :text | email:= text ]; value:email]! !

!PnCreateUser methodsFor: 'building'!
buildSurnameRow: aForm
    aForm div class: (errors ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row | 
                row label text:'Surname'.
                row input class:'form-control'; action: [ :text | surname:= text ]; value:email]! !

!PnCreateUser methodsFor: 'building'!
buildRepeatPasswordRow: aForm
    aForm div class: (errors ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row | 
                row label text:'Repeat Password: '.
                row input class:'form-control'; type: 'password'; action: [ :text | password2 := text ]; value:'']! !

!PnCreateUser methodsFor: 'building'!
buildNameRow: aForm
    aForm div class: (errors ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row | 
                row label text:'Name'.
                row input class:'form-control'; action: [ :text | name:= text ]; value:email]! !

In the previous example there is nothing interesting: just creating a form like the login form but with more fields. This is the error checking method that we will be calling before the registration action:

!PnCreateUser methodsFor: 'actions'!
checkErrors
    errors := ''.
    email ifEmpty: [ errors := errors, 'The email must not be empty because you need to login in the system', String cr ].
    (PnUtils checkEmail: email) ifFalse: [ errors := errors, 'This doesn''t look like a real email to me', String cr ].
    name ifEmpty: [ errors := errors, 'The name is a required field', String cr ].
    surname ifEmpty: [ errors := errors, 'The surname is a required field', String cr ].
    (password, password2) ifEmpty: [ errors := errors, 'You must enter a password', String cr ].
    (password = password2) ifFalse: [ errors := errors, 'The password are not matching', String cr ].
    ! !

As you see I only fill the errors instance variable with the error message and the row building methods will do the rest.

Now the registration action, that is more interesting:

!PnCreateUser methodsFor: 'actions'!
registerAction
    | u |
    self markDirty; checkErrors.
    errors ifNotEmpty: [ ^ self. ].
    
    u := PnUser new realname:name; surname: surname; email: email; password: password.
    PnUserDAO current register:u.
    self show:(ILInformationWidget new informationString: 'We created an user for you. Click ok to go to the app.') 
        onAnswer: [ : e | self redirectToLocal: 'notes' ].! !

The interesting bits are in the DAO usage, that “persist” the PnUser object, and the show:onAnswer: method.

The show:onAnswer: method of the ILWidget class can handle the control flow for you. In the next page rendering process the invoking widget will be substituted with the passed widget, in this case ILInformationWidget. When the shown widget (ILInformationWidget) will call the answer method the control flow will return to the block passed to show:onAnswer:.

This block redirect the browser to the notes controller, which will handle the notes creation and visualization proces.

Iliad Framework, the registration form

Pharo, Checking emails with regexp

In the previous post we build a login form and we saw that we should provide an user registration form.

We are going to build a registration form so we need to check emails. I will create a new class PnUtils to contain all the string-checking utilities that we need for our App:

Object subclass: #PnUtils
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

Now, in a class method, I will implement my email-checking:

!PnUtils class methodsFor: 'as yet unclassified'!
checkEmail: anEmail
    ^ anEmail asUppercase matchesRegex:  '[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z][A-Z][A-Z]?[A-Z]?'.

As you see I check the email addresses using the RegExp feature of Smalltalk. The regexp is taken from this site.

I know that at this times my should send an activation email to let the user activate yourself but I won’t do it as I don’t want to mess up with SMTP configuration.

In this discussion I want to show you how simple is to use regexp from Pharo Smalltalk, without loading any external libraries.

Ok. We must test it to see if it’s working or not:

TestCase subclass: #PnUtilsTest
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog-Tests'

!PnUtilsTest methodsFor: 'tests'!
testEmailOk
    self assert: (PnUtils checkEmail: 'leonardoce@interfree.it' ).
    self assert: (PnUtils checkEmail: 'leonardoce@interfree.com' ).   

!PnUtilsTest methodsFor: 'tests'!
testEmailNotOk
    self assert: (PnUtils checkEmail: 'leonardoce@interfree.sirtr' ) not.

The tests should all be working.

Pharo, Checking emails with regexp

Iliad Framework, the users-dao intermezzo

In the previous post we build a login form. In that form we included a “Register” link and so we must provide a way to make our users entries persistent.

Using Smalltalk the image is already persistent, saved every time you need reloaded from the image file. To cut a long story short if you don’t need ACID transactions and a strong mechanism to backup and restore data you can use the image as your database.

This may look strange (and do look strange) to non-smalltalkers but this wonderful post explains very well why using your image as a persistence layer isn’t a crazy idea.

For our didactic purpose we can safely use the image.

We need to store users so we start by creating an PnUser class:

Object subclass: #PnUser
    instanceVariableNames: 'realname surname email md5pwd'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'!

As you see this is a really simple class. You can automatically generate the accessors method using the Pharo browser. You will obtain something like this:

!PnUser methodsFor: 'accessing'!
email
    ^ email

password:pwd
    md5pwd :=  (MD5 hashMessage:pwd) hex.

realname: anObject
    realname := anObject

email: anObject
    email := anObject

realname
    ^ realname

surname: anObject
    surname := anObject

surname
    ^ surname

email
    ^ email

email: anObject
    email := anObject

realname
    ^ realname

surname: anObject
    surname := anObject

surname
    ^ surname

realname: anObject
    realname := anObject

Instead of storing the password we store the MD5 hash code and so we have these methods:

!PnUser methodsFor: 'accessing'!
password:pwd
    md5pwd :=  (MD5 hashMessage:pwd) hex.

!PnUser methodsFor: 'accessing'!
hasPassword:aString
    ^ (MD5 hashMessage:aString) hex = md5pwd .

The MD5 class has all the code needed to compute the MD5 hash code of a string. Pharo has a lot of utilities to ease the life of developers.

Ok. Now we need a class to store users informations. We call it PnUserDAO and we will use a Dictionary to store users informations: the key will be the email and the value the PnUser object.

Let’s start with the class definition:

Object subclass: #PnUserDAO
    instanceVariableNames: 'usersDictionary'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'!

When we initialize a DAO we create a new Dictionary where we will store the users:

!PnUserDAO methodsFor: 'initialization'!
initialize
    super initialize.
    usersDictionary := Dictionary new.

The first thing we need is a method to store a new user:

!PnUserDAO methodsFor: 'accessing'!
register:anUser
    usersDictionary at: anUser email put: anUser

Then we need a method to retrieve an user from the email:

!PnUserDAO methodsFor: 'accessing'!
userForEmail: anEmail
    ^ usersDictionary at: anEmail ifAbsent: nil.

We created a DAO and now we need to create a test case, just to see if everything is working. Before every test we create a new DAO with a test user and after the test we delete the DAO just created:

TestCase subclass: #PnUserDAOTest
    instanceVariableNames: 'dao'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog-Tests'!

!PnUserDAOTest methodsFor: 'running'!
setUp
    | testUser |
    super setUp.
    dao := PnUserDAO new.
    testUser := PnUser new
        surname: 'Test user';
        realname: 'Test name';
        password: 'test';
        email: 'test@test.eu'.
    dao register: testUser.

!PnUserDAOTest methodsFor: 'running'!
tearDown
    super tearDown.
    dao := nil.

Now we can test the register: method:

!PnUserDAOTest methodsFor: 'tests'!
testUserCreation
    |u|
    u := PnUser new email: 'leonardo@leo.it'; realname:'Leonardo'; surname:'Test'.
    dao register:u.
    self assert:(dao userForEmail: 'leonardo@leo.it') isNotNil.

The login method needs a way to check for an user and a password:

!PnUserDAO methodsFor: 'authentication'!
userForEmail: anEmail password:pwd
    |user|
    user := self userForEmail: anEmail.
    user ifNil: [ ^ nil ].
    (user hasPassword: pwd) ifTrue: [ ^ user ] ifFalse: [ ^ nil].

Let’s test it:

!PnUserDAOTest methodsFor: 'tests'!
testAuthentication
    self assert:(dao userForEmail:'test@test.eu' password:'test' ) isNotNil.
    self assert:(dao userForEmail:'test@test.eu' password:'testnot' ) isNil.

As we will offer an user deregistration procedure we need a unregister: method:

!PnUserDAO methodsFor: 'accessing'!
unregister: anEmail
    usersDictionary removeKey: anEmail ifAbsent: [ ].
    ^ self

We test it:

!PnUserDAOTest methodsFor: 'tests'!
testRemovingUnknownUser
    dao unregister: 'nonexitent'

!PnUserDAOTest methodsFor: 'tests'!
testRemovingKnownUser
    |u|
    u := PnUser new email: 'leonardo@leo.it'; realname:'Leonardo'; surname:'Test'.
    dao register:u.
    self assert:(dao userForEmail: 'leonardo@leo.it') isNotNil.
    dao unregister:'leonardo@leo.it'.
    self assert:(dao userForEmail: 'leonardo@leo.it') isNil.

Now we create a method to remove all known users:

!PnUserDAO methodsFor: 'util'!
deleteAllUsers
    usersDictionary removeAll

This is the test:

!PnUserDAOTest methodsFor: 'tests'!
testDeleteAllUsers
    self assert:(dao userForEmail:'test@test.eu' password:'test' ) isNotNil .
    dao deleteAllUsers .
    self assert:(dao userForEmail:'test@test.eu' password:'test' ) isNil .

Well… if everything is ok you have all units test working correctly and you can be happy!

PnUserDAO test cases

Iliad Framework, the users-dao intermezzo

Iliad Framework, the login form

In the previous post we talked about customizing the generated page to include references to the Bootstrap project.

This is the first lession where we start working on a complete example, a projecs-notes app usable from desktop and mobile web browsers using the twitter bootstrap framework.

Now we will start working on a login page.

To build a new page with a form I will start making a component that will be built from the application controller named login. The controller will be named PnProjectLogin.

Let’s start building this new component creating a class whose superclass is ILWidget:

ILWidget subclass: #PnProjectLogin
    instanceVariableNames: 'username password loginError'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

remember that widgets are stateful: the instance variables will be populated by the application and by the user.

  • username will be the text entered by the user in the login form;
  • password as previous;
  • loginError will be the string of the error message of the login or nil if there is no error message.

I won’t show the accessor methods because they can be automatically generated by the Pharo browser but I will show the initializer method:

!PnProjectLogin methodsFor: 'initialization'!
initialize
    super initialize.
    loginError := nil.
    username := ''.
    password := ''.! !

Let’s start by creating a method to build the username field and the relative label:

!PnProjectLogin methodsFor: 'building'!
buildLoginRow: aForm
    aForm div class: (loginError ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row | 
                row label text:'Login:'.
                row input class:'form-control'; action: [ :text | username := text ]; value:username ]! !

See how interesting it the build: method: it takes a block and evaluate it passing, as argument, the receiver. Before executing the passed block the method opens the element and closes it when the block finished. In the previous example the aForm div execution results in a DIV element that get automatically closed when the block has terminated its execution.

Another interesting thing is the class: method invocation on the aForm div element: to comply with the bootstrap rules we need to insert the has-error class when this element of the form is wrong. To do this we check the loginError contents.

In the input element, the action block takes the element text content.

This method is somewhat similiar:

!PnProjectLogin methodsFor: 'building'!
buildPasswordRow: aForm
    aForm div class:(loginError ifNotNil: [ 'form-group has-error' ] ifNil:  [ 'form-group' ]);
            build: [ : row |
                row label text:'Password'.
                row input class:'form-control'; type:'password'; action: [ :text | password := text ] ]! !

As you see, while the login field get constructed with a value (or the previous one), the password is always empty.

Ok. Now we will construct the error message row of the form:

!PnProjectLogin methodsFor: 'building'!
buildErrorMessageRow: aForm
    loginError ifNotNil: [
    (aForm div class: 'alert alert-warning') text: loginError ]! !

Obviously we have to build the errors row only if there are errors! Well. Now we are missing the “register” link.

!PnProjectLogin methodsFor: 'building'!
buildRegisterRow: aForm
    (aForm div class: 'form-group') a
        href: 'register';
        text: 'Want to register?'! !

Let’s bind all together in the contents method:

!PnProjectLogin methodsFor: 'building' stamp: 'LeonardoCecchi 2/28/2014 23:03'!
contents
    ^ [ :e | 
    e h1 text: 'Project Notes - Login'.
    e p
        text:
            'Project Notes is a note taking app that you
            can use for your project. It will store memos for
            you and for your team.'.
    e p text: 'Believe me, you really need this app!!'.
    e form
        build: [ :form | 
            self buildLoginRow: form.
            self buildPasswordRow: form.
            self buildErrorMessageRow: form.
            self buildRegisterRow: form.
            form button
                class: 'btn btn-default';
                text: 'Login!!';
                action: [ self loginAction ] ] ]! !

In the loginAction method, which is called when the user press on the “Login” button we execute the loginAction method of this class, which is defined like this:

!PnProjectLogin methodsFor: 'actions'!
loginAction
    self markDirty .
    username ifEmpty: [ ^ loginError := 'Please enter the user name' ].
    password ifEmpty: [ ^ loginError := 'Please enter the password' ].
    loginError := nil.
    "self redirectToLocal: 'browseProject'."

In this method, which for now is only a stub, we check for empty data and then we will call the main page.

Now we need to integrate this widget in our application, LcBlogProjectNotes. We must add an instance variable named loginWidget.

ILApplication subclass: #LcBlogProjectNotes
    instanceVariableNames: 'loginWidget
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'

As we discussed in the relative article we need an accessor method that lazily creates the widget:

!LcBlogProjectNotes methodsFor: 'accessing'!
loginWidget
    ^ loginWidget ifNil: [ loginWidget := PnProjectLogin new ]! !

Now we can create the controller method:

!LcBlogProjectNotes methodsFor: 'controllers'!
login
    ^ [ :e | e div class:'container'; build: self loginWidget ]! !

If you go to http://localhost:7070/ProjectNotes/login you should see the following result:

Project notes login form

Yeah! We succesfuly build our first form, even with error checking. When the loginError is not empty the form looks like this:

Project notes login form error

Iliad Framework, the login form