@@ -2,6 +2,160 @@
2title: Tutorial (Part II)
2title: Tutorial (Part II)
5−## Under construction
5+## Creating a simple blog with Hakyll
7−Coming soon.
7+After we created a simple brochure site, we're going to try something more
8+advanced: we are going to create a simple blog system.
10+A [zip file containing the source](examples/simpleblog.zip) for this
11+tutorial is also available.
13+Blogs, as you probably know, are composed of posts. In Hakyll, we're going
14+to use simple pages for posts. All posts are located in the `posts`
15+directory. But we're not going to use the `directory` command here - you will
16+see why later. First, some trivial things like css.
23+## Finding the posts, and a bit about renderables
25+`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
26+provide us with all the blog posts. The blog posts have a
27+`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
28+can sort them easily, you could of course name them whatever you want. They
29+contain some metadata, too:
35+date: November 5, 2009
37+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
38+Vivamus pretium leo adipiscing lectus iaculis lobortis.
39+Vivamus scelerisque velit dignissim metus...
42+Now, we find the posts and sort them reversed:
45+-- Find all post paths.
46+postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
49+Our `postPaths` value is now of the type `[FilePath]`. `FilePath` is no
50+instance of `Renderable`, but `PagePath` is:
53+let renderablePosts = map createPagePath postPaths
56+We have two templates we want to render our posts with: first we would like to
57+render them using `templates/post.html`, and we want to render the result
58+using `templates/default.html`. This can be done with the `renderChain`
62+mapM_ (renderChain [ "templates/post.html"
63+ , "templates/default.html"
67+Remember that the `renderChain` works by rendering the datatype using the first
68+template, creating a new page with the render result in the `body` field, and so
69+on until it has been rendered with all templates.
71+Now, we have the posts rendered. What is left is to generate some kind of index
72+page with links to those posts. We want one general list showing all posts, and
73+we want to show a few recent posts on the index page.
77+Currently, there are 3 renderable datatypes in Hakyll:
79+- `Page`: The result of any rendering action. It is generally not recommended
80+ to use pages a lot, because they cannot check dependencies (and therefore,
81+ you would always regenerate your entire site if you use pages the wrong way).
82+- `PagePath`: Basically just a `FilePath` in a box. Internally, this will use
83+ a `Page` for rendering, but `PagePath` provides better dependency checking
84+ and works on a higher level.
85+- `CustomPage`: Basically the name says it - the preferred way of creating
86+ custom pages in Hakyll.
88+We will use a `CustomPage` here. Basically, all `Renderable` datatypes are in
89+the end just `key: value` mappings. A CustomPage is created using the
90+`createCustomPage` function, which has the following type signature:
93+createCustomPage :: FilePath
95+ -> [(String, Either String (IO ByteString)]
98+The first argument is the `url` of the page to generate. For our index page,
99+this will be, `index.html`. The second argument is _a list of dependencies_.
100+Basically, you should here give a list of files on which your custom page
103+The last argument is obviously our `key: value` mapping. But why the `Either`?
104+This, once again, is about dependency handling. The idea is that you can choose
105+which type to use for the value:
107+- `String`: Simply a `String`.
108+- `IO ByteString`: Here, you can give an arbitrary `IO` action that will result
109+ in a ByteString. However - this action _will not be executed_ when the file
110+ in `_site` is up-to-date.
112+First, let us define this `IO ByteString` for our index page. We want to render
113+every post using a simple template:
117+ <a href="/$url">$title</a>
118+ - <em>$date</em> - by <em>$author</em>
122+When every post is rendered with this template, we then want to concatenate the
123+result. Since rendering and concatenating is pretty common, Hakyll provides us
124+with a high-level function to do this.
127+let recentPosts = renderAndConcat "templates/postitem.html"
128+ (take 3 renderablePosts)
131+Now, creating our custom page is fairly straight-forward:
134+createCustomPage "index.html"
135+ ("templates/postitem.html" : take 3 postPaths)
136+ [ ("title", Left "All posts")
137+ , ("posts", Right recentPosts)
141+You can see our three arguments here. We're rendering `index.html`, then we tell
142+Hakyll on what files it depends - here the `templates/postitem.html` template
143+and the latest 3 posts. Finally, we give a `title` value to substitute in the
144+template, and the result of our concatenation. Of course, we also need to render
148+renderChain ["index.html", "templates/default.html"] $
149+ createCustomPage "index.html"
150+ ("templates/postitem.html" : take 3 postPaths)
151+ [ ("title", Left "All posts")
152+ , ("posts", Right recentPosts)
156+Note that the `index.html` in the `renderChain` list is also a template.
160+If you have any more questions, feel free to ask them on the
161+[google discussion group](http://groups.google.com/group/hakyll).