I’ve been using Jekyll to run my professional website and my blog for about 2 months and I never look back. It can do pretty much everything a dynamic website does, but better and faster. The one thing that my blog was missing was a way to get feedback from the readers and to allow me to interact with them and learn from their experiences. It’s not very smart (and a bit arrogant, actually) to publish an article with a solution to a certain problem and not allow readers to comment on it, pointing possible weaknesses or even posting other solutions that (most likely) turn out to be better than mine.
A commenting system is something very common and easy to implement on classic blog platforms like Blogger or Wordpress, but we have to cater for the special characteristics of our beloved static sites and adapt things a bit. So how can we take a static HTML page and add a section with content that is constantly updated based on user input?
At first, I thought Disqus
It was that simple and it did work great, but I didn’t like it. It offered too much stuff that I didn’t want, like post ratings, favourites, social media integration and even a whole community! All I wanted was a simple form with 3 fields (name, email address or website and the message) so I didn’t want to load a heavier plugin full of controls that I wouldn’t use, and worse, with an interface that I couldn’t properly customise and make on-brand.
Then I found Poole
After doing some research, I came across Poole, a very simple and minimalistic form hosting service. After signing in with GitHub (off to a good start), it lets you create a form. And no, it doesn’t ask you for any form fields or options at this point, you just choose a form name and it comes back with two URLs: the one you use to post your data to using a regular HTML form, and another one you use to get all your data as a JSON object.
The first one is public and is bound to the form by a unique hash code, whilst the second one contains an API key that you should, in theory, keep private. I say in theory because the API is read-only, which means that even if someone else gets ahold of that key, they won’t be able to do any damage with your data, although it might not be ideal to expose your data in bulk to the public without any filtering. If you want to delete any records, you have to be signed in with your GitHub account on Poole’s website.
Poole is basically just an API to handle form submissions, so unlike Disqus you won’t get anything near a commenting system out of the box — which is exactly what I wanted, because it means that I can build exactly what I want and make it look exactly how I want. Even though it can be used for many different things, the creators of Poole clearly acknowledge that using it as a commenting system on a static site is a perfect use case, so they include an example of how to implement one on their examples page. In that example, they use Gulp to retrieve the JSON file with the comments, write them to the post layout page and trigger a site build automatically.
So I wasn’t quite there yet
In fact, getting the comments for a given post was a bit more complicated than just send an Ajax request and append the result to the page, because the result would contain every single comment on the whole site and not just the ones associated with that specific post, so I would have to start by going through all the comments and filter the ones I wanted.
Because Poole doesn’t offer any type of back-end validation, we can’t guarantee that we won’t have any records with empty fields, so we would have to do some more filtering to make sure we don’t add to the page any comments with missing information.
There was another problem I had to deal with: cross-domain Ajax requests. The JSON file containing my data is hosted at Poole’s servers, so my Ajax requests were being rejected due to cross-domain security restrictions. I didn’t have control over the origin, so using CORS was out of the equation. My only option was JSONP, but Poole didn’t support it (they did implement it after our conversation on Twitter though).
Turns out I needed a middleman
I realised that I could build a server-side middleman that would get the JSON file from Poole, process the data and echo it back in the format of my choice. That approach would get around all the abovementioned problems quite easily by allowing me to:
Use JSONP: I could write the middleman in a way that it would wrap the data in a function call and I could even specify its name with a GET variable. This would solve my problems with cross-domain requests.
Do some basic spam detection: I could plant a honeypot form field and look for it in when filtering the comments. This is not a bulletproof solution, but should stop some of the spam bots (at least the dumb ones).
Use Markdown: I could allow users to use Markdown in their comments by installing a Markdown parser and hooking it up with my middleman. This is particularly important on my blog, since I wanted to give my readers a nice way of sharing their own code in response to my articles (and this way I could make it compatible with my technique for displaying code snippets).
Gosh, will you show some code already?
Right away. I started by building my middleman, with PHP as my language of choice. I used composer to install Parsedown, a fast Markdown parser for PHP. This script will look for two GET variables:
page is the path for the post I want to get the comments for and
callback is the name of the function to pad the JSON data with.
After including it in my script, I set the URL for my Poole JSON file and grab its contents. I then loop through all the comments and, for each one, check if it a) matches the page I passed in the URL, b) passes the honeypot test and c) contains my required fields, name and message (lines 14 to 22).
I wanted to allow comments on all posts by default, but at the same time have the ability to disable them on specific cases. To do that, I modified the default front-matter settings for my posts.
Now I just need to add
enable_comments: false to any post I don’t want comments on. Time to add the comments section to my post layout:
redirect_to is a field specific to Poole (not stored in the dataset) and represents the relative path to the page users will be redirected to after submitting the form, and
page is the path to our current page, so we can associate the comment with the post.
I then called the middleman script on my post layout page:
async because I don’t want to wait for the middleman to give me the comments to load the post. I’m passing the two arguments we discussed before to the middleman:
page is the current URL.
And that’s pretty much it. You can see the result on the form below. Feel free to use it to leave any questions or suggestions you might have - sweet, I can finally say this! ∎