Web Application Development with Clojure – Part 4

The story so far – In the last 3 parts, we completed setting up the project for a simple blog engine, loading the test data and finally displaying the blog posts on the “home” page. In this post first we’ll first add some nice CSS, using Twitter Bootstrap, to make it look a bit better and finish the blog post detail page. Next we’ll add an authentication page for the blog – a simple login page that will check the credentials entered.

This post is part of the Web Application Development with Clojure tutorial. You might want to read the previous posts before this post for continuity’s sake.

Introduction

The story so far – In the last 3 parts, we completed setting up the project for a simple blog engine, loading the test data and finally displaying the blog posts on the “home” page. In this post first we’ll first add some nice CSS to make it look a bit better and finish the blog post detail page. Next we’ll add an authentication page for the blog.

Source code on github

The code for this series is now available on github and the source code is tagged with part names. If you want to checkout the code for a specific part of this tutorial you can do so using the following command:

Static Resources

To make our blog look a bit more nice, we need some CSS and styling. We’ll use twitter’s bootstrap CSS for styling. To serve the css files from the root path, we can use ring middleware file provides a function wrap-files which will serve the static resources. First create a folder called public in the resources folder. This will contain all the static stuff like images, CSS etc. Now download and copy the bootstrap folder to the newly created public folder. The folder structure in IntelliJ should look like the following:

Update the code in core.clj‘s routes and add the wrap-file as shown below:

Now update your HTML for the home page with the following code. The changes I made are adding the CSS declaration to the top, and restructuring HTML a bit.

 

 

 

Post Content

 

Whenever you change the template you need to compile the template.clj, to see the changes. This is a limitation of Enlive – which I think is being addressed. But for now, the work around is use

in the REPL, that will reload the templates.

Here’s how the home page looks now.

Hacking LESS (Optional)

If you are feeling adventurous, you can try editing the Bootstrap LESS and change the styling to your liking. I use less.js and the .less files directly, and experiment with changing the styles. Just download the latest less.js and keep it under bootstrap/js and replace the stylesheet link in the html’s head element to the following:

Here’s how the blog looks like with Clojure Colors

Post Detail Page

Now we just have the home page, but it would be better if we show the single blog post page when someone clicks on the post title. So let us add that functionality. First we need a new route that will handle the post detail URLs. Whenever someone goes to http://host/{post_id} we’ll show the detail page of that post. So we repeat pretty much the same exercise we did for rendering the home page, i.e., creating a template (both html and a new deftemplate in templates.clj ), adding a controller function, and finally calling that function in updated routes.

Here’s the updated version of the routes declaration which adds a new handler to the route as shown below.

For the HTML of the post detail page, we’ll just reuse the home page, copy the html from home.html to post.html. Update the templates.clj with the deftemplate macro, the code is also similar to the home-page template macro.

And finally the controller method

After these changes you should be able to see the post detail page by going to http://host:port/1 or http://host:port/2

Link to the Post pages

We’ll now modify the home page template and link the title of the post to the corresponding detail page. First modify the home.html and change the title span to an anchor. Here’s the relevant part of the modified html:

Title of the Post

Now edit the templates.clj to add the href attribute to the post detail page. Since we need to add the href and modify the content of the a tag, we use the do-> function to add the transformations. Here’s the updated home-page macro in templates.clj:

If everything went well, once you reload the templates namespace using (use ‘clog.templates :reload) you should see the home page with titles of the posts linking to the post detail pages.

Authentication

The next step is to create an administration area which is only accessible after login, and will be used to create the blog posts. First we need to create our login.html which will contain a form that will allow users to login. Notice that there’s an error div, which we will use to show the error message when the user enters invalid username and password. Take the following code and put it in a file login.html under resources

 

 

 

 

 

Cancel

 

Now let us add a macro to our templates.clj to render the login page. In this macro, we are checking if any message is passed to the template method, we’ll set the appropriate message in the error div. We are also using the set-attr and remove-attr functions from enlive.

Now let us update the routes, so when someone goes to /login we’ll render the login page. We need to also add a POST handler that will process the login form’s post request. We’ll also use the ring’s params middleware function wrap-params that will wrap the request parameters and pass it on along with request. So update the clog.core namespace to include the ring.middleware.param:

Now update the routes var with wrap-params and handler for the login page and login action as shown below:

The login route will handle both POST and GET, since we haven’t specified any request method. The function login is in the controller.clj which is shown below.

This is a bit “crude” way of handling login, but we are trying to keep this as simple as possible. What the above function is doing is checking if the params are empty, they’ll be empty when there’s a GET request on the /login URL, then just render the login-page with no parameters. When the params are not empty, check if username is equal to password, then redirect to the /admin url (which doesn’t exist yet, but we will add it soon). And finally, if the username isn’t equal to password, then render the same login-page but with a message “Invalid username or password”.

There’s bug here in the implementation that you can just click “Login” to get to the /admin, which is because empty strings are equal according to the logic. Fixing the bug is left as exercise to the reader!

Now that all the required pieces are in place, when you start the server (with port 8080), you should be able to see the login page at http://localhost:8080/login and when you enter two different strings for username and password, you should see an error message. You’ll get a redirect to /admin when you enter same string for username and password.

Conclusion

In this part we finished the “front-end” of the blog and started working on the admin area. In the Part 5of this series, we’ll wrap up the blog engine by completing the administration area that allows you to create the blog posts. In sixth and final part of this series we’ll see what deployment options are available and how to deploy the blog engine on Heroku.

Make sure you subscribe to the RSS feed or follow me on Twitter to get notified.

4 thoughts on “Web Application Development with Clojure – Part 4”

  1. Hi Vijay,

    This tutorial series has been excellent! I have a quick question about authentication. I’m trying to rewrite the login handler so that a given password is checked against the password associated with a username in the database. Here’s what it looks like: https://gist.github.com/fdaae281e87dd16ff3f0

    I can’t quite seem to make it work, though. Whenever I try to log in using the correct password, I’m told the password is incorrect. Can you see what I’m doing wrong? Any help would be much appreciated!

    Thanks.

  2. Had to add a function to respond with a 404 on favicon otherwise it’d get stacktraces

    (defn wrap-bounce-favicon [handler]
    (fn [req]
    (if (= [:get “/favicon.ico”] [(:request-method req) (:uri req)])
    {:status 404
    :headers {}
    :body “”}
    (handler req))))

  3. Had to add a function to respond with a 404 on favicon otherwise it’d get stacktraces

    (defn wrap-bounce-favicon [handler]
    (fn [req]
    (if (= [:get “/favicon.ico”] [(:request-method req) (:uri req)])
    {:status 404
    :headers {}
    :body “”}
    (handler req))))

Leave a Reply